github.com/grange74/terraform@v0.7.0-rc3.0.20160722171430-8c8803864753/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 len(path) == 0 {
    70  		return t
    71  	}
    72  
    73  	c := t.Children()[path[0]]
    74  	if c == nil {
    75  		return nil
    76  	}
    77  
    78  	return c.Child(path[1:])
    79  }
    80  
    81  // Children returns the children of this tree (the modules that are
    82  // imported by this root).
    83  //
    84  // This will only return a non-nil value after Load is called.
    85  func (t *Tree) Children() map[string]*Tree {
    86  	t.lock.RLock()
    87  	defer t.lock.RUnlock()
    88  	return t.children
    89  }
    90  
    91  // Loaded says whether or not this tree has been loaded or not yet.
    92  func (t *Tree) Loaded() bool {
    93  	t.lock.RLock()
    94  	defer t.lock.RUnlock()
    95  	return t.children != nil
    96  }
    97  
    98  // Modules returns the list of modules that this tree imports.
    99  //
   100  // This is only the imports of _this_ level of the tree. To retrieve the
   101  // full nested imports, you'll have to traverse the tree.
   102  func (t *Tree) Modules() []*Module {
   103  	result := make([]*Module, len(t.config.Modules))
   104  	for i, m := range t.config.Modules {
   105  		result[i] = &Module{
   106  			Name:   m.Name,
   107  			Source: m.Source,
   108  		}
   109  	}
   110  
   111  	return result
   112  }
   113  
   114  // Name returns the name of the tree. This will be "<root>" for the root
   115  // tree and then the module name given for any children.
   116  func (t *Tree) Name() string {
   117  	if t.name == "" {
   118  		return RootName
   119  	}
   120  
   121  	return t.name
   122  }
   123  
   124  // Load loads the configuration of the entire tree.
   125  //
   126  // The parameters are used to tell the tree where to find modules and
   127  // whether it can download/update modules along the way.
   128  //
   129  // Calling this multiple times will reload the tree.
   130  //
   131  // Various semantic-like checks are made along the way of loading since
   132  // module trees inherently require the configuration to be in a reasonably
   133  // sane state: no circular dependencies, proper module sources, etc. A full
   134  // suite of validations can be done by running Validate (after loading).
   135  func (t *Tree) Load(s getter.Storage, mode GetMode) error {
   136  	t.lock.Lock()
   137  	defer t.lock.Unlock()
   138  
   139  	// Reset the children if we have any
   140  	t.children = nil
   141  
   142  	modules := t.Modules()
   143  	children := make(map[string]*Tree)
   144  
   145  	// Go through all the modules and get the directory for them.
   146  	for _, m := range modules {
   147  		if _, ok := children[m.Name]; ok {
   148  			return fmt.Errorf(
   149  				"module %s: duplicated. module names must be unique", m.Name)
   150  		}
   151  
   152  		// Determine the path to this child
   153  		path := make([]string, len(t.path), len(t.path)+1)
   154  		copy(path, t.path)
   155  		path = append(path, m.Name)
   156  
   157  		// Split out the subdir if we have one
   158  		source, subDir := getter.SourceDirSubdir(m.Source)
   159  
   160  		source, err := getter.Detect(source, t.config.Dir, getter.Detectors)
   161  		if err != nil {
   162  			return fmt.Errorf("module %s: %s", m.Name, err)
   163  		}
   164  
   165  		// Check if the detector introduced something new.
   166  		source, subDir2 := getter.SourceDirSubdir(source)
   167  		if subDir2 != "" {
   168  			subDir = filepath.Join(subDir2, subDir)
   169  		}
   170  
   171  		// Get the directory where this module is so we can load it
   172  		key := strings.Join(path, ".")
   173  		key = "root." + key
   174  		dir, ok, err := getStorage(s, key, source, mode)
   175  		if err != nil {
   176  			return err
   177  		}
   178  		if !ok {
   179  			return fmt.Errorf(
   180  				"module %s: not found, may need to be downloaded using 'terraform get'", m.Name)
   181  		}
   182  
   183  		// If we have a subdirectory, then merge that in
   184  		if subDir != "" {
   185  			dir = filepath.Join(dir, subDir)
   186  		}
   187  
   188  		// Load the configurations.Dir(source)
   189  		children[m.Name], err = NewTreeModule(m.Name, dir)
   190  		if err != nil {
   191  			return fmt.Errorf(
   192  				"module %s: %s", m.Name, err)
   193  		}
   194  
   195  		// Set the path of this child
   196  		children[m.Name].path = path
   197  	}
   198  
   199  	// Go through all the children and load them.
   200  	for _, c := range children {
   201  		if err := c.Load(s, mode); err != nil {
   202  			return err
   203  		}
   204  	}
   205  
   206  	// Set our tree up
   207  	t.children = children
   208  
   209  	return nil
   210  }
   211  
   212  // Path is the full path to this tree.
   213  func (t *Tree) Path() []string {
   214  	return t.path
   215  }
   216  
   217  // String gives a nice output to describe the tree.
   218  func (t *Tree) String() string {
   219  	var result bytes.Buffer
   220  	path := strings.Join(t.path, ", ")
   221  	if path != "" {
   222  		path = fmt.Sprintf(" (path: %s)", path)
   223  	}
   224  	result.WriteString(t.Name() + path + "\n")
   225  
   226  	cs := t.Children()
   227  	if cs == nil {
   228  		result.WriteString("  not loaded")
   229  	} else {
   230  		// Go through each child and get its string value, then indent it
   231  		// by two.
   232  		for _, c := range cs {
   233  			r := strings.NewReader(c.String())
   234  			scanner := bufio.NewScanner(r)
   235  			for scanner.Scan() {
   236  				result.WriteString("  ")
   237  				result.WriteString(scanner.Text())
   238  				result.WriteString("\n")
   239  			}
   240  		}
   241  	}
   242  
   243  	return result.String()
   244  }
   245  
   246  // Validate does semantic checks on the entire tree of configurations.
   247  //
   248  // This will call the respective config.Config.Validate() functions as well
   249  // as verifying things such as parameters/outputs between the various modules.
   250  //
   251  // Load must be called prior to calling Validate or an error will be returned.
   252  func (t *Tree) Validate() error {
   253  	if !t.Loaded() {
   254  		return fmt.Errorf("tree must be loaded before calling Validate")
   255  	}
   256  
   257  	// If something goes wrong, here is our error template
   258  	newErr := &TreeError{Name: []string{t.Name()}}
   259  
   260  	// Validate our configuration first.
   261  	if err := t.config.Validate(); err != nil {
   262  		newErr.Err = err
   263  		return newErr
   264  	}
   265  
   266  	// Get the child trees
   267  	children := t.Children()
   268  
   269  	// Validate all our children
   270  	for _, c := range children {
   271  		err := c.Validate()
   272  		if err == nil {
   273  			continue
   274  		}
   275  
   276  		verr, ok := err.(*TreeError)
   277  		if !ok {
   278  			// Unknown error, just return...
   279  			return err
   280  		}
   281  
   282  		// Append ourselves to the error and then return
   283  		verr.Name = append(verr.Name, t.Name())
   284  		return verr
   285  	}
   286  
   287  	// Go over all the modules and verify that any parameters are valid
   288  	// variables into the module in question.
   289  	for _, m := range t.config.Modules {
   290  		tree, ok := children[m.Name]
   291  		if !ok {
   292  			// This should never happen because Load watches us
   293  			panic("module not found in children: " + m.Name)
   294  		}
   295  
   296  		// Build the variables that the module defines
   297  		requiredMap := make(map[string]struct{})
   298  		varMap := make(map[string]struct{})
   299  		for _, v := range tree.config.Variables {
   300  			varMap[v.Name] = struct{}{}
   301  
   302  			if v.Required() {
   303  				requiredMap[v.Name] = struct{}{}
   304  			}
   305  		}
   306  
   307  		// Compare to the keys in our raw config for the module
   308  		for k, _ := range m.RawConfig.Raw {
   309  			if _, ok := varMap[k]; !ok {
   310  				newErr.Err = fmt.Errorf(
   311  					"module %s: %s is not a valid parameter",
   312  					m.Name, k)
   313  				return newErr
   314  			}
   315  
   316  			// Remove the required
   317  			delete(requiredMap, k)
   318  		}
   319  
   320  		// If we have any required left over, they aren't set.
   321  		for k, _ := range requiredMap {
   322  			newErr.Err = fmt.Errorf(
   323  				"module %s: required variable %s not set",
   324  				m.Name, k)
   325  			return newErr
   326  		}
   327  	}
   328  
   329  	// Go over all the variables used and make sure that any module
   330  	// variables represent outputs properly.
   331  	for source, vs := range t.config.InterpolatedVariables() {
   332  		for _, v := range vs {
   333  			mv, ok := v.(*config.ModuleVariable)
   334  			if !ok {
   335  				continue
   336  			}
   337  
   338  			tree, ok := children[mv.Name]
   339  			if !ok {
   340  				// This should never happen because Load watches us
   341  				panic("module not found in children: " + mv.Name)
   342  			}
   343  
   344  			found := false
   345  			for _, o := range tree.config.Outputs {
   346  				if o.Name == mv.Field {
   347  					found = true
   348  					break
   349  				}
   350  			}
   351  			if !found {
   352  				newErr.Err = fmt.Errorf(
   353  					"%s: %s is not a valid output for module %s",
   354  					source, mv.Field, mv.Name)
   355  				return newErr
   356  			}
   357  		}
   358  	}
   359  
   360  	return nil
   361  }
   362  
   363  // TreeError is an error returned by Tree.Validate if an error occurs
   364  // with validation.
   365  type TreeError struct {
   366  	Name []string
   367  	Err  error
   368  }
   369  
   370  func (e *TreeError) Error() string {
   371  	// Build up the name
   372  	var buf bytes.Buffer
   373  	for _, n := range e.Name {
   374  		buf.WriteString(n)
   375  		buf.WriteString(".")
   376  	}
   377  	buf.Truncate(buf.Len() - 1)
   378  
   379  	// Format the value
   380  	return fmt.Sprintf("module %s: %s", buf.String(), e.Err)
   381  }