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