2014-09-14 02:45:56 +02:00
|
|
|
package module
|
|
|
|
|
|
|
|
import (
|
2014-09-15 05:14:37 +02:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2017-09-22 22:49:58 +02:00
|
|
|
"encoding/json"
|
2014-09-15 01:17:29 +02:00
|
|
|
"fmt"
|
2017-09-22 22:49:58 +02:00
|
|
|
"io/ioutil"
|
2017-09-21 22:59:48 +02:00
|
|
|
"log"
|
2017-09-22 22:49:58 +02:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2014-09-15 05:14:37 +02:00
|
|
|
"strings"
|
2014-09-15 01:17:29 +02:00
|
|
|
"sync"
|
|
|
|
|
2015-10-15 22:36:58 +02:00
|
|
|
"github.com/hashicorp/go-getter"
|
2014-09-14 02:45:56 +02:00
|
|
|
"github.com/hashicorp/terraform/config"
|
|
|
|
)
|
|
|
|
|
2014-09-23 01:39:01 +02:00
|
|
|
// RootName is the name of the root tree.
|
|
|
|
const RootName = "root"
|
|
|
|
|
2014-09-14 02:45:56 +02:00
|
|
|
// Tree represents the module import tree of configurations.
|
|
|
|
//
|
|
|
|
// This Tree structure can be used to get (download) new modules, load
|
|
|
|
// all the modules without getting, flatten the tree into something
|
|
|
|
// Terraform can use, etc.
|
|
|
|
type Tree struct {
|
2014-09-15 05:14:37 +02:00
|
|
|
name string
|
2014-09-15 01:17:29 +02:00
|
|
|
config *config.Config
|
2014-09-15 18:53:29 +02:00
|
|
|
children map[string]*Tree
|
2015-04-08 01:37:46 +02:00
|
|
|
path []string
|
2014-09-15 05:14:37 +02:00
|
|
|
lock sync.RWMutex
|
2014-09-14 02:45:56 +02:00
|
|
|
}
|
|
|
|
|
2014-09-14 23:46:45 +02:00
|
|
|
// NewTree returns a new Tree for the given config structure.
|
2014-09-16 00:49:07 +02:00
|
|
|
func NewTree(name string, c *config.Config) *Tree {
|
|
|
|
return &Tree{config: c, name: name}
|
|
|
|
}
|
|
|
|
|
2016-05-04 19:46:34 +02:00
|
|
|
// NewEmptyTree returns a new tree that is empty (contains no configuration).
|
|
|
|
func NewEmptyTree() *Tree {
|
2016-05-10 18:15:19 +02:00
|
|
|
t := &Tree{config: &config.Config{}}
|
|
|
|
|
|
|
|
// We do this dummy load so that the tree is marked as "loaded". It
|
|
|
|
// should never fail because this is just about a no-op. If it does fail
|
|
|
|
// we panic so we can know its a bug.
|
|
|
|
if err := t.Load(nil, GetModeGet); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return t
|
2016-05-04 19:46:34 +02:00
|
|
|
}
|
|
|
|
|
2014-09-16 00:49:07 +02:00
|
|
|
// NewTreeModule is like NewTree except it parses the configuration in
|
|
|
|
// the directory and gives it a specific name. Use a blank name "" to specify
|
|
|
|
// the root module.
|
|
|
|
func NewTreeModule(name, dir string) (*Tree, error) {
|
|
|
|
c, err := config.LoadDir(dir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewTree(name, c), nil
|
2014-09-15 01:17:29 +02:00
|
|
|
}
|
|
|
|
|
2014-09-22 19:56:50 +02:00
|
|
|
// Config returns the configuration for this module.
|
|
|
|
func (t *Tree) Config() *config.Config {
|
|
|
|
return t.config
|
|
|
|
}
|
|
|
|
|
2014-10-08 05:00:36 +02:00
|
|
|
// Child returns the child with the given path (by name).
|
|
|
|
func (t *Tree) Child(path []string) *Tree {
|
2016-09-14 22:35:54 +02:00
|
|
|
if t == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-08 05:00:36 +02:00
|
|
|
if len(path) == 0 {
|
2014-10-08 05:02:18 +02:00
|
|
|
return t
|
2014-10-08 05:00:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
c := t.Children()[path[0]]
|
|
|
|
if c == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.Child(path[1:])
|
|
|
|
}
|
|
|
|
|
2014-09-15 01:17:29 +02:00
|
|
|
// Children returns the children of this tree (the modules that are
|
|
|
|
// imported by this root).
|
|
|
|
//
|
|
|
|
// This will only return a non-nil value after Load is called.
|
2014-09-15 18:53:29 +02:00
|
|
|
func (t *Tree) Children() map[string]*Tree {
|
2014-09-15 05:14:37 +02:00
|
|
|
t.lock.RLock()
|
|
|
|
defer t.lock.RUnlock()
|
|
|
|
return t.children
|
2014-09-14 23:46:45 +02:00
|
|
|
}
|
|
|
|
|
2017-04-15 04:39:35 +02:00
|
|
|
// DeepEach calls the provided callback for the receiver and then all of
|
|
|
|
// its descendents in the tree, allowing an operation to be performed on
|
|
|
|
// all modules in the tree.
|
|
|
|
//
|
|
|
|
// Parents will be visited before their children but otherwise the order is
|
|
|
|
// not defined.
|
|
|
|
func (t *Tree) DeepEach(cb func(*Tree)) {
|
|
|
|
t.lock.RLock()
|
|
|
|
defer t.lock.RUnlock()
|
|
|
|
t.deepEach(cb)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Tree) deepEach(cb func(*Tree)) {
|
|
|
|
cb(t)
|
|
|
|
for _, c := range t.children {
|
|
|
|
c.deepEach(cb)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-15 18:37:40 +02:00
|
|
|
// Loaded says whether or not this tree has been loaded or not yet.
|
|
|
|
func (t *Tree) Loaded() bool {
|
|
|
|
t.lock.RLock()
|
|
|
|
defer t.lock.RUnlock()
|
|
|
|
return t.children != nil
|
|
|
|
}
|
|
|
|
|
2014-09-14 02:45:56 +02:00
|
|
|
// Modules returns the list of modules that this tree imports.
|
2014-09-14 23:46:45 +02:00
|
|
|
//
|
|
|
|
// This is only the imports of _this_ level of the tree. To retrieve the
|
|
|
|
// full nested imports, you'll have to traverse the tree.
|
2014-09-14 02:45:56 +02:00
|
|
|
func (t *Tree) Modules() []*Module {
|
2014-09-15 01:17:29 +02:00
|
|
|
result := make([]*Module, len(t.config.Modules))
|
|
|
|
for i, m := range t.config.Modules {
|
2014-09-14 23:46:45 +02:00
|
|
|
result[i] = &Module{
|
2014-09-15 05:00:17 +02:00
|
|
|
Name: m.Name,
|
2014-09-14 23:46:45 +02:00
|
|
|
Source: m.Source,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
2014-09-14 02:45:56 +02:00
|
|
|
}
|
|
|
|
|
2014-09-15 05:14:37 +02:00
|
|
|
// Name returns the name of the tree. This will be "<root>" for the root
|
|
|
|
// tree and then the module name given for any children.
|
|
|
|
func (t *Tree) Name() string {
|
|
|
|
if t.name == "" {
|
2014-09-23 01:39:01 +02:00
|
|
|
return RootName
|
2014-09-15 05:14:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return t.name
|
|
|
|
}
|
|
|
|
|
2014-09-14 02:45:56 +02:00
|
|
|
// Load loads the configuration of the entire tree.
|
|
|
|
//
|
|
|
|
// The parameters are used to tell the tree where to find modules and
|
|
|
|
// whether it can download/update modules along the way.
|
|
|
|
//
|
2014-09-15 01:17:29 +02:00
|
|
|
// Calling this multiple times will reload the tree.
|
|
|
|
//
|
2014-09-14 02:45:56 +02:00
|
|
|
// Various semantic-like checks are made along the way of loading since
|
|
|
|
// module trees inherently require the configuration to be in a reasonably
|
|
|
|
// sane state: no circular dependencies, proper module sources, etc. A full
|
|
|
|
// suite of validations can be done by running Validate (after loading).
|
2015-10-15 22:36:58 +02:00
|
|
|
func (t *Tree) Load(s getter.Storage, mode GetMode) error {
|
2014-09-15 01:17:29 +02:00
|
|
|
t.lock.Lock()
|
|
|
|
defer t.lock.Unlock()
|
|
|
|
|
|
|
|
// Reset the children if we have any
|
|
|
|
t.children = nil
|
|
|
|
|
|
|
|
modules := t.Modules()
|
2014-09-15 18:53:29 +02:00
|
|
|
children := make(map[string]*Tree)
|
2014-09-15 01:17:29 +02:00
|
|
|
|
|
|
|
// Go through all the modules and get the directory for them.
|
2014-09-15 18:53:29 +02:00
|
|
|
for _, m := range modules {
|
|
|
|
if _, ok := children[m.Name]; ok {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"module %s: duplicated. module names must be unique", m.Name)
|
|
|
|
}
|
|
|
|
|
2015-04-08 01:44:24 +02:00
|
|
|
// Determine the path to this child
|
|
|
|
path := make([]string, len(t.path), len(t.path)+1)
|
|
|
|
copy(path, t.path)
|
|
|
|
path = append(path, m.Name)
|
|
|
|
|
2017-09-22 22:49:58 +02:00
|
|
|
// The key is the string that will be hashed to uniquely id the Source.
|
|
|
|
// The leading digit can be incremented to force re-fetch all existing
|
|
|
|
// modules.
|
|
|
|
key := fmt.Sprintf("0.root.%s-%s", strings.Join(path, "."), m.Source)
|
|
|
|
|
2017-09-21 22:59:48 +02:00
|
|
|
log.Printf("[TRACE] module source %q", m.Source)
|
|
|
|
// Split out the subdir if we have one.
|
2017-09-22 22:49:58 +02:00
|
|
|
// Terraform keeps the entire requested tree for now, so that modules can
|
2017-09-21 22:59:48 +02:00
|
|
|
// reference sibling modules from the same archive or repo.
|
|
|
|
source, subDir := getter.SourceDirSubdir(m.Source)
|
|
|
|
|
2017-09-22 22:49:58 +02:00
|
|
|
// First check if we we need to download anything.
|
|
|
|
// This is also checked by the getter.Storage implementation, but we
|
|
|
|
// want to be able to short-circuit the detection as well, since some
|
|
|
|
// detectors may need to make external calls.
|
|
|
|
dir, found, err := s.Dir(key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// looks like we already have it
|
|
|
|
// In order to load the Tree we need to find out if there was another
|
|
|
|
// subDir stored from discovery.
|
|
|
|
if found && mode != GetModeUpdate {
|
|
|
|
subDir, err := t.getSubdir(dir)
|
|
|
|
if err != nil {
|
|
|
|
// If there's a problem with the subdir record, we'll let the
|
|
|
|
// recordSubdir method fix it up. Any other errors filesystem
|
|
|
|
// errors will turn up again below.
|
|
|
|
log.Println("[WARN] error reading subdir record:", err)
|
|
|
|
} else {
|
|
|
|
dir := filepath.Join(dir, subDir)
|
|
|
|
// Load the configurations.Dir(source)
|
|
|
|
children[m.Name], err = NewTreeModule(m.Name, dir)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("module %s: %s", m.Name, err)
|
|
|
|
}
|
|
|
|
// Set the path of this child
|
|
|
|
children[m.Name].path = path
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-21 22:59:48 +02:00
|
|
|
log.Printf("[TRACE] module source: %q", source)
|
|
|
|
|
2017-09-22 22:49:58 +02:00
|
|
|
source, err = getter.Detect(source, t.config.Dir, detectors)
|
2014-09-15 01:17:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("module %s: %s", m.Name, err)
|
|
|
|
}
|
2017-09-21 22:59:48 +02:00
|
|
|
|
|
|
|
log.Printf("[TRACE] detected module source %q", source)
|
|
|
|
|
|
|
|
// Check if the detector introduced something new.
|
|
|
|
// For example, the registry always adds a subdir of `//*`,
|
|
|
|
// indicating that we need to strip off the first component from the
|
|
|
|
// tar archive, though we may not yet know what it is called.
|
|
|
|
//
|
|
|
|
// TODO: This can cause us to lose the previously detected subdir. It
|
|
|
|
// was never an issue before, since none of the supported detectors
|
|
|
|
// previously had this behavior, but we may want to add this ability to
|
|
|
|
// registry modules.
|
|
|
|
source, subDir2 := getter.SourceDirSubdir(source)
|
|
|
|
if subDir2 != "" {
|
|
|
|
subDir = subDir2
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[TRACE] getting module source %q", source)
|
|
|
|
|
2015-04-08 01:44:24 +02:00
|
|
|
dir, ok, err := getStorage(s, key, source, mode)
|
2014-09-15 01:17:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf(
|
2016-02-17 17:01:51 +01:00
|
|
|
"module %s: not found, may need to be downloaded using 'terraform get'", m.Name)
|
2014-09-15 01:17:29 +02:00
|
|
|
}
|
|
|
|
|
2017-09-22 22:49:58 +02:00
|
|
|
// expand and record the subDir for later
|
|
|
|
if subDir != "" {
|
|
|
|
fullDir, err := getter.SubdirGlob(dir, subDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// +1 to account for the pathsep
|
|
|
|
if len(dir)+1 > len(fullDir) {
|
|
|
|
return fmt.Errorf("invalid module storage path %q", fullDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
subDir = fullDir[len(dir)+1:]
|
|
|
|
|
|
|
|
if err := t.recordSubdir(dir, subDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dir = fullDir
|
2017-09-21 22:59:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the configurations.Dir(source)
|
2014-09-16 00:49:07 +02:00
|
|
|
children[m.Name], err = NewTreeModule(m.Name, dir)
|
2014-09-15 01:17:29 +02:00
|
|
|
if err != nil {
|
2017-09-22 22:49:58 +02:00
|
|
|
return fmt.Errorf("module %s: %s", m.Name, err)
|
2014-09-15 01:17:29 +02:00
|
|
|
}
|
2015-04-08 01:37:46 +02:00
|
|
|
// Set the path of this child
|
2015-04-08 01:44:24 +02:00
|
|
|
children[m.Name].path = path
|
2014-09-15 01:17:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Go through all the children and load them.
|
|
|
|
for _, c := range children {
|
|
|
|
if err := c.Load(s, mode); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set our tree up
|
|
|
|
t.children = children
|
|
|
|
|
2014-09-14 02:45:56 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-22 22:49:58 +02:00
|
|
|
func subdirRecordsPath(dir string) string {
|
|
|
|
const filename = "module-subdir.json"
|
|
|
|
// Get the parent directory.
|
|
|
|
// The current FolderStorage implementation needed to be able to create
|
|
|
|
// this directory, so we can be reasonably certain we can use it.
|
|
|
|
parent := filepath.Dir(filepath.Clean(dir))
|
|
|
|
return filepath.Join(parent, filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
// unmarshal the records file in the parent directory. Always returns a valid map.
|
|
|
|
func loadSubdirRecords(dir string) (map[string]string, error) {
|
|
|
|
records := map[string]string{}
|
|
|
|
|
|
|
|
recordsPath := subdirRecordsPath(dir)
|
|
|
|
data, err := ioutil.ReadFile(recordsPath)
|
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
return records, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(data) == 0 {
|
|
|
|
return records, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &records); err != nil {
|
|
|
|
return records, err
|
|
|
|
}
|
|
|
|
return records, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Tree) getSubdir(dir string) (string, error) {
|
|
|
|
records, err := loadSubdirRecords(dir)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return records[dir], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark the location of a detected subdir in a top-level file so we
|
|
|
|
// can skip detection when not updating the module.
|
|
|
|
func (t *Tree) recordSubdir(dir, subdir string) error {
|
|
|
|
records, err := loadSubdirRecords(dir)
|
|
|
|
if err != nil {
|
|
|
|
// if there was a problem with the file, we will attempt to write a new
|
|
|
|
// one. Any non-data related error should surface there.
|
|
|
|
log.Printf("[WARN] error reading subdir records: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
records[dir] = subdir
|
|
|
|
|
|
|
|
js, err := json.Marshal(records)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
recordsPath := subdirRecordsPath(dir)
|
|
|
|
return ioutil.WriteFile(recordsPath, js, 0644)
|
|
|
|
}
|
|
|
|
|
2015-04-08 01:37:46 +02:00
|
|
|
// Path is the full path to this tree.
|
|
|
|
func (t *Tree) Path() []string {
|
|
|
|
return t.path
|
|
|
|
}
|
|
|
|
|
2014-09-15 05:14:37 +02:00
|
|
|
// String gives a nice output to describe the tree.
|
|
|
|
func (t *Tree) String() string {
|
|
|
|
var result bytes.Buffer
|
2015-04-08 01:37:46 +02:00
|
|
|
path := strings.Join(t.path, ", ")
|
|
|
|
if path != "" {
|
|
|
|
path = fmt.Sprintf(" (path: %s)", path)
|
|
|
|
}
|
|
|
|
result.WriteString(t.Name() + path + "\n")
|
2014-09-15 05:14:37 +02:00
|
|
|
|
|
|
|
cs := t.Children()
|
|
|
|
if cs == nil {
|
|
|
|
result.WriteString(" not loaded")
|
|
|
|
} else {
|
|
|
|
// Go through each child and get its string value, then indent it
|
|
|
|
// by two.
|
|
|
|
for _, c := range cs {
|
|
|
|
r := strings.NewReader(c.String())
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
|
|
for scanner.Scan() {
|
|
|
|
result.WriteString(" ")
|
|
|
|
result.WriteString(scanner.Text())
|
|
|
|
result.WriteString("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.String()
|
|
|
|
}
|
|
|
|
|
2014-09-14 02:45:56 +02:00
|
|
|
// Validate does semantic checks on the entire tree of configurations.
|
|
|
|
//
|
|
|
|
// This will call the respective config.Config.Validate() functions as well
|
|
|
|
// as verifying things such as parameters/outputs between the various modules.
|
2014-09-15 18:37:40 +02:00
|
|
|
//
|
|
|
|
// Load must be called prior to calling Validate or an error will be returned.
|
2014-09-14 02:45:56 +02:00
|
|
|
func (t *Tree) Validate() error {
|
2014-09-15 18:37:40 +02:00
|
|
|
if !t.Loaded() {
|
|
|
|
return fmt.Errorf("tree must be loaded before calling Validate")
|
|
|
|
}
|
|
|
|
|
2014-09-15 19:32:41 +02:00
|
|
|
// If something goes wrong, here is our error template
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr := &treeError{Name: []string{t.Name()}}
|
2014-09-15 19:32:41 +02:00
|
|
|
|
2017-01-09 00:39:57 +01:00
|
|
|
// Terraform core does not handle root module children named "root".
|
|
|
|
// We plan to fix this in the future but this bug was brought up in
|
|
|
|
// the middle of a release and we don't want to introduce wide-sweeping
|
|
|
|
// changes at that time.
|
|
|
|
if len(t.path) == 1 && t.name == "root" {
|
|
|
|
return fmt.Errorf("root module cannot contain module named 'root'")
|
|
|
|
}
|
|
|
|
|
2014-09-15 18:37:40 +02:00
|
|
|
// Validate our configuration first.
|
|
|
|
if err := t.config.Validate(); err != nil {
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.Add(err)
|
2014-09-15 18:37:40 +02:00
|
|
|
}
|
|
|
|
|
2016-12-17 01:47:32 +01:00
|
|
|
// If we're the root, we do extra validation. This validation usually
|
|
|
|
// requires the entire tree (since children don't have parent pointers).
|
|
|
|
if len(t.path) == 0 {
|
|
|
|
if err := t.validateProviderAlias(); err != nil {
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.Add(err)
|
2016-12-17 01:47:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-15 19:32:41 +02:00
|
|
|
// Get the child trees
|
|
|
|
children := t.Children()
|
|
|
|
|
2014-09-15 18:37:40 +02:00
|
|
|
// Validate all our children
|
2014-09-15 19:32:41 +02:00
|
|
|
for _, c := range children {
|
2014-09-15 18:37:40 +02:00
|
|
|
err := c.Validate()
|
|
|
|
if err == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2017-03-02 20:10:41 +01:00
|
|
|
verr, ok := err.(*treeError)
|
2014-09-15 18:37:40 +02:00
|
|
|
if !ok {
|
|
|
|
// Unknown error, just return...
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append ourselves to the error and then return
|
|
|
|
verr.Name = append(verr.Name, t.Name())
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.AddChild(verr)
|
2014-09-15 18:37:40 +02:00
|
|
|
}
|
|
|
|
|
2014-09-15 19:32:41 +02:00
|
|
|
// Go over all the modules and verify that any parameters are valid
|
|
|
|
// variables into the module in question.
|
|
|
|
for _, m := range t.config.Modules {
|
|
|
|
tree, ok := children[m.Name]
|
|
|
|
if !ok {
|
|
|
|
// This should never happen because Load watches us
|
|
|
|
panic("module not found in children: " + m.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build the variables that the module defines
|
2014-09-25 04:40:06 +02:00
|
|
|
requiredMap := make(map[string]struct{})
|
2014-09-15 19:32:41 +02:00
|
|
|
varMap := make(map[string]struct{})
|
|
|
|
for _, v := range tree.config.Variables {
|
|
|
|
varMap[v.Name] = struct{}{}
|
2014-09-25 04:40:06 +02:00
|
|
|
|
|
|
|
if v.Required() {
|
|
|
|
requiredMap[v.Name] = struct{}{}
|
|
|
|
}
|
2014-09-15 19:32:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compare to the keys in our raw config for the module
|
|
|
|
for k, _ := range m.RawConfig.Raw {
|
|
|
|
if _, ok := varMap[k]; !ok {
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.Add(fmt.Errorf(
|
2014-09-15 19:32:41 +02:00
|
|
|
"module %s: %s is not a valid parameter",
|
2017-03-02 20:10:41 +01:00
|
|
|
m.Name, k))
|
2014-09-15 19:32:41 +02:00
|
|
|
}
|
2014-09-25 04:40:06 +02:00
|
|
|
|
|
|
|
// Remove the required
|
|
|
|
delete(requiredMap, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have any required left over, they aren't set.
|
|
|
|
for k, _ := range requiredMap {
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.Add(fmt.Errorf(
|
|
|
|
"module %s: required variable %q not set",
|
|
|
|
m.Name, k))
|
2014-09-15 19:32:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-15 22:57:07 +02:00
|
|
|
// Go over all the variables used and make sure that any module
|
|
|
|
// variables represent outputs properly.
|
|
|
|
for source, vs := range t.config.InterpolatedVariables() {
|
|
|
|
for _, v := range vs {
|
|
|
|
mv, ok := v.(*config.ModuleVariable)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
tree, ok := children[mv.Name]
|
|
|
|
if !ok {
|
2017-03-17 04:14:20 +01:00
|
|
|
newErr.Add(fmt.Errorf(
|
|
|
|
"%s: undefined module referenced %s",
|
|
|
|
source, mv.Name))
|
|
|
|
continue
|
2014-09-15 22:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
found := false
|
|
|
|
for _, o := range tree.config.Outputs {
|
|
|
|
if o.Name == mv.Field {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2017-03-02 20:10:41 +01:00
|
|
|
newErr.Add(fmt.Errorf(
|
2014-09-15 22:57:07 +02:00
|
|
|
"%s: %s is not a valid output for module %s",
|
2017-03-02 20:10:41 +01:00
|
|
|
source, mv.Field, mv.Name))
|
2014-09-15 22:57:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 20:10:41 +01:00
|
|
|
return newErr.ErrOrNil()
|
|
|
|
}
|
|
|
|
|
|
|
|
// treeError is an error use by Tree.Validate to accumulates all
|
|
|
|
// validation errors.
|
|
|
|
type treeError struct {
|
|
|
|
Name []string
|
|
|
|
Errs []error
|
|
|
|
Children []*treeError
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *treeError) Add(err error) {
|
|
|
|
e.Errs = append(e.Errs, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *treeError) AddChild(err *treeError) {
|
|
|
|
e.Children = append(e.Children, err)
|
2014-09-14 02:45:56 +02:00
|
|
|
}
|
2014-09-15 18:37:40 +02:00
|
|
|
|
2017-03-02 20:10:41 +01:00
|
|
|
func (e *treeError) ErrOrNil() error {
|
|
|
|
if len(e.Errs) > 0 || len(e.Children) > 0 {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
return nil
|
2014-09-15 18:37:40 +02:00
|
|
|
}
|
|
|
|
|
2017-03-02 20:10:41 +01:00
|
|
|
func (e *treeError) Error() string {
|
|
|
|
name := strings.Join(e.Name, ".")
|
|
|
|
var out bytes.Buffer
|
|
|
|
fmt.Fprintf(&out, "module %s: ", name)
|
|
|
|
|
|
|
|
if len(e.Errs) == 1 {
|
|
|
|
// single like error
|
|
|
|
out.WriteString(e.Errs[0].Error())
|
|
|
|
} else {
|
|
|
|
// multi-line error
|
|
|
|
for _, err := range e.Errs {
|
|
|
|
fmt.Fprintf(&out, "\n %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(e.Children) > 0 {
|
|
|
|
// start the next error on a new line
|
|
|
|
out.WriteString("\n ")
|
|
|
|
}
|
|
|
|
for _, child := range e.Children {
|
|
|
|
out.WriteString(child.Error())
|
2014-09-15 18:37:40 +02:00
|
|
|
}
|
|
|
|
|
2017-03-02 20:10:41 +01:00
|
|
|
return out.String()
|
2014-09-15 18:37:40 +02:00
|
|
|
}
|