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