diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/module')
10 files changed, 883 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/module/copy_dir.go b/vendor/github.com/hashicorp/terraform/config/module/copy_dir.go new file mode 100644 index 0000000..095f61d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/copy_dir.go | |||
@@ -0,0 +1,114 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "io" | ||
5 | "os" | ||
6 | "path/filepath" | ||
7 | "strings" | ||
8 | ) | ||
9 | |||
10 | // copyDir copies the src directory contents into dst. Both directories | ||
11 | // should already exist. | ||
12 | func copyDir(dst, src string) error { | ||
13 | src, err := filepath.EvalSymlinks(src) | ||
14 | if err != nil { | ||
15 | return err | ||
16 | } | ||
17 | |||
18 | walkFn := func(path string, info os.FileInfo, err error) error { | ||
19 | if err != nil { | ||
20 | return err | ||
21 | } | ||
22 | |||
23 | if path == src { | ||
24 | return nil | ||
25 | } | ||
26 | |||
27 | if strings.HasPrefix(filepath.Base(path), ".") { | ||
28 | // Skip any dot files | ||
29 | if info.IsDir() { | ||
30 | return filepath.SkipDir | ||
31 | } else { | ||
32 | return nil | ||
33 | } | ||
34 | } | ||
35 | |||
36 | // The "path" has the src prefixed to it. We need to join our | ||
37 | // destination with the path without the src on it. | ||
38 | dstPath := filepath.Join(dst, path[len(src):]) | ||
39 | |||
40 | // we don't want to try and copy the same file over itself. | ||
41 | if eq, err := sameFile(path, dstPath); eq { | ||
42 | return nil | ||
43 | } else if err != nil { | ||
44 | return err | ||
45 | } | ||
46 | |||
47 | // If we have a directory, make that subdirectory, then continue | ||
48 | // the walk. | ||
49 | if info.IsDir() { | ||
50 | if path == filepath.Join(src, dst) { | ||
51 | // dst is in src; don't walk it. | ||
52 | return nil | ||
53 | } | ||
54 | |||
55 | if err := os.MkdirAll(dstPath, 0755); err != nil { | ||
56 | return err | ||
57 | } | ||
58 | |||
59 | return nil | ||
60 | } | ||
61 | |||
62 | // If we have a file, copy the contents. | ||
63 | srcF, err := os.Open(path) | ||
64 | if err != nil { | ||
65 | return err | ||
66 | } | ||
67 | defer srcF.Close() | ||
68 | |||
69 | dstF, err := os.Create(dstPath) | ||
70 | if err != nil { | ||
71 | return err | ||
72 | } | ||
73 | defer dstF.Close() | ||
74 | |||
75 | if _, err := io.Copy(dstF, srcF); err != nil { | ||
76 | return err | ||
77 | } | ||
78 | |||
79 | // Chmod it | ||
80 | return os.Chmod(dstPath, info.Mode()) | ||
81 | } | ||
82 | |||
83 | return filepath.Walk(src, walkFn) | ||
84 | } | ||
85 | |||
86 | // sameFile tried to determine if to paths are the same file. | ||
87 | // If the paths don't match, we lookup the inode on supported systems. | ||
88 | func sameFile(a, b string) (bool, error) { | ||
89 | if a == b { | ||
90 | return true, nil | ||
91 | } | ||
92 | |||
93 | aIno, err := inode(a) | ||
94 | if err != nil { | ||
95 | if os.IsNotExist(err) { | ||
96 | return false, nil | ||
97 | } | ||
98 | return false, err | ||
99 | } | ||
100 | |||
101 | bIno, err := inode(b) | ||
102 | if err != nil { | ||
103 | if os.IsNotExist(err) { | ||
104 | return false, nil | ||
105 | } | ||
106 | return false, err | ||
107 | } | ||
108 | |||
109 | if aIno > 0 && aIno == bIno { | ||
110 | return true, nil | ||
111 | } | ||
112 | |||
113 | return false, nil | ||
114 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/get.go b/vendor/github.com/hashicorp/terraform/config/module/get.go new file mode 100644 index 0000000..96b4a63 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/get.go | |||
@@ -0,0 +1,71 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "io/ioutil" | ||
5 | "os" | ||
6 | |||
7 | "github.com/hashicorp/go-getter" | ||
8 | ) | ||
9 | |||
10 | // GetMode is an enum that describes how modules are loaded. | ||
11 | // | ||
12 | // GetModeLoad says that modules will not be downloaded or updated, they will | ||
13 | // only be loaded from the storage. | ||
14 | // | ||
15 | // GetModeGet says that modules can be initially downloaded if they don't | ||
16 | // exist, but otherwise to just load from the current version in storage. | ||
17 | // | ||
18 | // GetModeUpdate says that modules should be checked for updates and | ||
19 | // downloaded prior to loading. If there are no updates, we load the version | ||
20 | // from disk, otherwise we download first and then load. | ||
21 | type GetMode byte | ||
22 | |||
23 | const ( | ||
24 | GetModeNone GetMode = iota | ||
25 | GetModeGet | ||
26 | GetModeUpdate | ||
27 | ) | ||
28 | |||
29 | // GetCopy is the same as Get except that it downloads a copy of the | ||
30 | // module represented by source. | ||
31 | // | ||
32 | // This copy will omit and dot-prefixed files (such as .git/, .hg/) and | ||
33 | // can't be updated on its own. | ||
34 | func GetCopy(dst, src string) error { | ||
35 | // Create the temporary directory to do the real Get to | ||
36 | tmpDir, err := ioutil.TempDir("", "tf") | ||
37 | if err != nil { | ||
38 | return err | ||
39 | } | ||
40 | // FIXME: This isn't completely safe. Creating and removing our temp path | ||
41 | // exposes where to race to inject files. | ||
42 | if err := os.RemoveAll(tmpDir); err != nil { | ||
43 | return err | ||
44 | } | ||
45 | defer os.RemoveAll(tmpDir) | ||
46 | |||
47 | // Get to that temporary dir | ||
48 | if err := getter.Get(tmpDir, src); err != nil { | ||
49 | return err | ||
50 | } | ||
51 | |||
52 | // Make sure the destination exists | ||
53 | if err := os.MkdirAll(dst, 0755); err != nil { | ||
54 | return err | ||
55 | } | ||
56 | |||
57 | // Copy to the final location | ||
58 | return copyDir(dst, tmpDir) | ||
59 | } | ||
60 | |||
61 | func getStorage(s getter.Storage, key string, src string, mode GetMode) (string, bool, error) { | ||
62 | // Get the module with the level specified if we were told to. | ||
63 | if mode > GetModeNone { | ||
64 | if err := s.Get(key, src, mode == GetModeUpdate); err != nil { | ||
65 | return "", false, err | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // Get the directory where the module is. | ||
70 | return s.Dir(key) | ||
71 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/inode.go b/vendor/github.com/hashicorp/terraform/config/module/inode.go new file mode 100644 index 0000000..8603ee2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/inode.go | |||
@@ -0,0 +1,21 @@ | |||
1 | // +build linux darwin openbsd netbsd solaris | ||
2 | |||
3 | package module | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "syscall" | ||
9 | ) | ||
10 | |||
11 | // lookup the inode of a file on posix systems | ||
12 | func inode(path string) (uint64, error) { | ||
13 | stat, err := os.Stat(path) | ||
14 | if err != nil { | ||
15 | return 0, err | ||
16 | } | ||
17 | if st, ok := stat.Sys().(*syscall.Stat_t); ok { | ||
18 | return st.Ino, nil | ||
19 | } | ||
20 | return 0, fmt.Errorf("could not determine file inode") | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/inode_freebsd.go b/vendor/github.com/hashicorp/terraform/config/module/inode_freebsd.go new file mode 100644 index 0000000..0d95730 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/inode_freebsd.go | |||
@@ -0,0 +1,21 @@ | |||
1 | // +build freebsd | ||
2 | |||
3 | package module | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "syscall" | ||
9 | ) | ||
10 | |||
11 | // lookup the inode of a file on posix systems | ||
12 | func inode(path string) (uint64, error) { | ||
13 | stat, err := os.Stat(path) | ||
14 | if err != nil { | ||
15 | return 0, err | ||
16 | } | ||
17 | if st, ok := stat.Sys().(*syscall.Stat_t); ok { | ||
18 | return uint64(st.Ino), nil | ||
19 | } | ||
20 | return 0, fmt.Errorf("could not determine file inode") | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/inode_windows.go b/vendor/github.com/hashicorp/terraform/config/module/inode_windows.go new file mode 100644 index 0000000..c0cf455 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/inode_windows.go | |||
@@ -0,0 +1,8 @@ | |||
1 | // +build windows | ||
2 | |||
3 | package module | ||
4 | |||
5 | // no syscall.Stat_t on windows, return 0 for inodes | ||
6 | func inode(path string) (uint64, error) { | ||
7 | return 0, nil | ||
8 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/module.go b/vendor/github.com/hashicorp/terraform/config/module/module.go new file mode 100644 index 0000000..f8649f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/module.go | |||
@@ -0,0 +1,7 @@ | |||
1 | package module | ||
2 | |||
3 | // Module represents the metadata for a single module. | ||
4 | type Module struct { | ||
5 | Name string | ||
6 | Source string | ||
7 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/testing.go b/vendor/github.com/hashicorp/terraform/config/module/testing.go new file mode 100644 index 0000000..fc9e733 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/testing.go | |||
@@ -0,0 +1,38 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "io/ioutil" | ||
5 | "os" | ||
6 | "testing" | ||
7 | |||
8 | "github.com/hashicorp/go-getter" | ||
9 | ) | ||
10 | |||
11 | // TestTree loads a module at the given path and returns the tree as well | ||
12 | // as a function that should be deferred to clean up resources. | ||
13 | func TestTree(t *testing.T, path string) (*Tree, func()) { | ||
14 | // Create a temporary directory for module storage | ||
15 | dir, err := ioutil.TempDir("", "tf") | ||
16 | if err != nil { | ||
17 | t.Fatalf("err: %s", err) | ||
18 | return nil, nil | ||
19 | } | ||
20 | |||
21 | // Load the module | ||
22 | mod, err := NewTreeModule("", path) | ||
23 | if err != nil { | ||
24 | t.Fatalf("err: %s", err) | ||
25 | return nil, nil | ||
26 | } | ||
27 | |||
28 | // Get the child modules | ||
29 | s := &getter.FolderStorage{StorageDir: dir} | ||
30 | if err := mod.Load(s, GetModeGet); err != nil { | ||
31 | t.Fatalf("err: %s", err) | ||
32 | return nil, nil | ||
33 | } | ||
34 | |||
35 | return mod, func() { | ||
36 | os.RemoveAll(dir) | ||
37 | } | ||
38 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree.go b/vendor/github.com/hashicorp/terraform/config/module/tree.go new file mode 100644 index 0000000..b6f90fd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/tree.go | |||
@@ -0,0 +1,428 @@ | |||
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 | // Loaded says whether or not this tree has been loaded or not yet. | ||
96 | func (t *Tree) Loaded() bool { | ||
97 | t.lock.RLock() | ||
98 | defer t.lock.RUnlock() | ||
99 | return t.children != nil | ||
100 | } | ||
101 | |||
102 | // Modules returns the list of modules that this tree imports. | ||
103 | // | ||
104 | // This is only the imports of _this_ level of the tree. To retrieve the | ||
105 | // full nested imports, you'll have to traverse the tree. | ||
106 | func (t *Tree) Modules() []*Module { | ||
107 | result := make([]*Module, len(t.config.Modules)) | ||
108 | for i, m := range t.config.Modules { | ||
109 | result[i] = &Module{ | ||
110 | Name: m.Name, | ||
111 | Source: m.Source, | ||
112 | } | ||
113 | } | ||
114 | |||
115 | return result | ||
116 | } | ||
117 | |||
118 | // Name returns the name of the tree. This will be "<root>" for the root | ||
119 | // tree and then the module name given for any children. | ||
120 | func (t *Tree) Name() string { | ||
121 | if t.name == "" { | ||
122 | return RootName | ||
123 | } | ||
124 | |||
125 | return t.name | ||
126 | } | ||
127 | |||
128 | // Load loads the configuration of the entire tree. | ||
129 | // | ||
130 | // The parameters are used to tell the tree where to find modules and | ||
131 | // whether it can download/update modules along the way. | ||
132 | // | ||
133 | // Calling this multiple times will reload the tree. | ||
134 | // | ||
135 | // Various semantic-like checks are made along the way of loading since | ||
136 | // module trees inherently require the configuration to be in a reasonably | ||
137 | // sane state: no circular dependencies, proper module sources, etc. A full | ||
138 | // suite of validations can be done by running Validate (after loading). | ||
139 | func (t *Tree) Load(s getter.Storage, mode GetMode) error { | ||
140 | t.lock.Lock() | ||
141 | defer t.lock.Unlock() | ||
142 | |||
143 | // Reset the children if we have any | ||
144 | t.children = nil | ||
145 | |||
146 | modules := t.Modules() | ||
147 | children := make(map[string]*Tree) | ||
148 | |||
149 | // Go through all the modules and get the directory for them. | ||
150 | for _, m := range modules { | ||
151 | if _, ok := children[m.Name]; ok { | ||
152 | return fmt.Errorf( | ||
153 | "module %s: duplicated. module names must be unique", m.Name) | ||
154 | } | ||
155 | |||
156 | // Determine the path to this child | ||
157 | path := make([]string, len(t.path), len(t.path)+1) | ||
158 | copy(path, t.path) | ||
159 | path = append(path, m.Name) | ||
160 | |||
161 | // Split out the subdir if we have one | ||
162 | source, subDir := getter.SourceDirSubdir(m.Source) | ||
163 | |||
164 | source, err := getter.Detect(source, t.config.Dir, getter.Detectors) | ||
165 | if err != nil { | ||
166 | return fmt.Errorf("module %s: %s", m.Name, err) | ||
167 | } | ||
168 | |||
169 | // Check if the detector introduced something new. | ||
170 | source, subDir2 := getter.SourceDirSubdir(source) | ||
171 | if subDir2 != "" { | ||
172 | subDir = filepath.Join(subDir2, subDir) | ||
173 | } | ||
174 | |||
175 | // Get the directory where this module is so we can load it | ||
176 | key := strings.Join(path, ".") | ||
177 | key = fmt.Sprintf("root.%s-%s", key, m.Source) | ||
178 | dir, ok, err := getStorage(s, key, source, mode) | ||
179 | if err != nil { | ||
180 | return err | ||
181 | } | ||
182 | if !ok { | ||
183 | return fmt.Errorf( | ||
184 | "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) | ||
185 | } | ||
186 | |||
187 | // If we have a subdirectory, then merge that in | ||
188 | if subDir != "" { | ||
189 | dir = filepath.Join(dir, subDir) | ||
190 | } | ||
191 | |||
192 | // Load the configurations.Dir(source) | ||
193 | children[m.Name], err = NewTreeModule(m.Name, dir) | ||
194 | if err != nil { | ||
195 | return fmt.Errorf( | ||
196 | "module %s: %s", m.Name, err) | ||
197 | } | ||
198 | |||
199 | // Set the path of this child | ||
200 | children[m.Name].path = path | ||
201 | } | ||
202 | |||
203 | // Go through all the children and load them. | ||
204 | for _, c := range children { | ||
205 | if err := c.Load(s, mode); err != nil { | ||
206 | return err | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // Set our tree up | ||
211 | t.children = children | ||
212 | |||
213 | return nil | ||
214 | } | ||
215 | |||
216 | // Path is the full path to this tree. | ||
217 | func (t *Tree) Path() []string { | ||
218 | return t.path | ||
219 | } | ||
220 | |||
221 | // String gives a nice output to describe the tree. | ||
222 | func (t *Tree) String() string { | ||
223 | var result bytes.Buffer | ||
224 | path := strings.Join(t.path, ", ") | ||
225 | if path != "" { | ||
226 | path = fmt.Sprintf(" (path: %s)", path) | ||
227 | } | ||
228 | result.WriteString(t.Name() + path + "\n") | ||
229 | |||
230 | cs := t.Children() | ||
231 | if cs == nil { | ||
232 | result.WriteString(" not loaded") | ||
233 | } else { | ||
234 | // Go through each child and get its string value, then indent it | ||
235 | // by two. | ||
236 | for _, c := range cs { | ||
237 | r := strings.NewReader(c.String()) | ||
238 | scanner := bufio.NewScanner(r) | ||
239 | for scanner.Scan() { | ||
240 | result.WriteString(" ") | ||
241 | result.WriteString(scanner.Text()) | ||
242 | result.WriteString("\n") | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | return result.String() | ||
248 | } | ||
249 | |||
250 | // Validate does semantic checks on the entire tree of configurations. | ||
251 | // | ||
252 | // This will call the respective config.Config.Validate() functions as well | ||
253 | // as verifying things such as parameters/outputs between the various modules. | ||
254 | // | ||
255 | // Load must be called prior to calling Validate or an error will be returned. | ||
256 | func (t *Tree) Validate() error { | ||
257 | if !t.Loaded() { | ||
258 | return fmt.Errorf("tree must be loaded before calling Validate") | ||
259 | } | ||
260 | |||
261 | // If something goes wrong, here is our error template | ||
262 | newErr := &treeError{Name: []string{t.Name()}} | ||
263 | |||
264 | // Terraform core does not handle root module children named "root". | ||
265 | // We plan to fix this in the future but this bug was brought up in | ||
266 | // the middle of a release and we don't want to introduce wide-sweeping | ||
267 | // changes at that time. | ||
268 | if len(t.path) == 1 && t.name == "root" { | ||
269 | return fmt.Errorf("root module cannot contain module named 'root'") | ||
270 | } | ||
271 | |||
272 | // Validate our configuration first. | ||
273 | if err := t.config.Validate(); err != nil { | ||
274 | newErr.Add(err) | ||
275 | } | ||
276 | |||
277 | // If we're the root, we do extra validation. This validation usually | ||
278 | // requires the entire tree (since children don't have parent pointers). | ||
279 | if len(t.path) == 0 { | ||
280 | if err := t.validateProviderAlias(); err != nil { | ||
281 | newErr.Add(err) | ||
282 | } | ||
283 | } | ||
284 | |||
285 | // Get the child trees | ||
286 | children := t.Children() | ||
287 | |||
288 | // Validate all our children | ||
289 | for _, c := range children { | ||
290 | err := c.Validate() | ||
291 | if err == nil { | ||
292 | continue | ||
293 | } | ||
294 | |||
295 | verr, ok := err.(*treeError) | ||
296 | if !ok { | ||
297 | // Unknown error, just return... | ||
298 | return err | ||
299 | } | ||
300 | |||
301 | // Append ourselves to the error and then return | ||
302 | verr.Name = append(verr.Name, t.Name()) | ||
303 | newErr.AddChild(verr) | ||
304 | } | ||
305 | |||
306 | // Go over all the modules and verify that any parameters are valid | ||
307 | // variables into the module in question. | ||
308 | for _, m := range t.config.Modules { | ||
309 | tree, ok := children[m.Name] | ||
310 | if !ok { | ||
311 | // This should never happen because Load watches us | ||
312 | panic("module not found in children: " + m.Name) | ||
313 | } | ||
314 | |||
315 | // Build the variables that the module defines | ||
316 | requiredMap := make(map[string]struct{}) | ||
317 | varMap := make(map[string]struct{}) | ||
318 | for _, v := range tree.config.Variables { | ||
319 | varMap[v.Name] = struct{}{} | ||
320 | |||
321 | if v.Required() { | ||
322 | requiredMap[v.Name] = struct{}{} | ||
323 | } | ||
324 | } | ||
325 | |||
326 | // Compare to the keys in our raw config for the module | ||
327 | for k, _ := range m.RawConfig.Raw { | ||
328 | if _, ok := varMap[k]; !ok { | ||
329 | newErr.Add(fmt.Errorf( | ||
330 | "module %s: %s is not a valid parameter", | ||
331 | m.Name, k)) | ||
332 | } | ||
333 | |||
334 | // Remove the required | ||
335 | delete(requiredMap, k) | ||
336 | } | ||
337 | |||
338 | // If we have any required left over, they aren't set. | ||
339 | for k, _ := range requiredMap { | ||
340 | newErr.Add(fmt.Errorf( | ||
341 | "module %s: required variable %q not set", | ||
342 | m.Name, k)) | ||
343 | } | ||
344 | } | ||
345 | |||
346 | // Go over all the variables used and make sure that any module | ||
347 | // variables represent outputs properly. | ||
348 | for source, vs := range t.config.InterpolatedVariables() { | ||
349 | for _, v := range vs { | ||
350 | mv, ok := v.(*config.ModuleVariable) | ||
351 | if !ok { | ||
352 | continue | ||
353 | } | ||
354 | |||
355 | tree, ok := children[mv.Name] | ||
356 | if !ok { | ||
357 | newErr.Add(fmt.Errorf( | ||
358 | "%s: undefined module referenced %s", | ||
359 | source, mv.Name)) | ||
360 | continue | ||
361 | } | ||
362 | |||
363 | found := false | ||
364 | for _, o := range tree.config.Outputs { | ||
365 | if o.Name == mv.Field { | ||
366 | found = true | ||
367 | break | ||
368 | } | ||
369 | } | ||
370 | if !found { | ||
371 | newErr.Add(fmt.Errorf( | ||
372 | "%s: %s is not a valid output for module %s", | ||
373 | source, mv.Field, mv.Name)) | ||
374 | } | ||
375 | } | ||
376 | } | ||
377 | |||
378 | return newErr.ErrOrNil() | ||
379 | } | ||
380 | |||
381 | // treeError is an error use by Tree.Validate to accumulates all | ||
382 | // validation errors. | ||
383 | type treeError struct { | ||
384 | Name []string | ||
385 | Errs []error | ||
386 | Children []*treeError | ||
387 | } | ||
388 | |||
389 | func (e *treeError) Add(err error) { | ||
390 | e.Errs = append(e.Errs, err) | ||
391 | } | ||
392 | |||
393 | func (e *treeError) AddChild(err *treeError) { | ||
394 | e.Children = append(e.Children, err) | ||
395 | } | ||
396 | |||
397 | func (e *treeError) ErrOrNil() error { | ||
398 | if len(e.Errs) > 0 || len(e.Children) > 0 { | ||
399 | return e | ||
400 | } | ||
401 | return nil | ||
402 | } | ||
403 | |||
404 | func (e *treeError) Error() string { | ||
405 | name := strings.Join(e.Name, ".") | ||
406 | var out bytes.Buffer | ||
407 | fmt.Fprintf(&out, "module %s: ", name) | ||
408 | |||
409 | if len(e.Errs) == 1 { | ||
410 | // single like error | ||
411 | out.WriteString(e.Errs[0].Error()) | ||
412 | } else { | ||
413 | // multi-line error | ||
414 | for _, err := range e.Errs { | ||
415 | fmt.Fprintf(&out, "\n %s", err) | ||
416 | } | ||
417 | } | ||
418 | |||
419 | if len(e.Children) > 0 { | ||
420 | // start the next error on a new line | ||
421 | out.WriteString("\n ") | ||
422 | } | ||
423 | for _, child := range e.Children { | ||
424 | out.WriteString(child.Error()) | ||
425 | } | ||
426 | |||
427 | return out.String() | ||
428 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree_gob.go b/vendor/github.com/hashicorp/terraform/config/module/tree_gob.go new file mode 100644 index 0000000..fcd37f4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/tree_gob.go | |||
@@ -0,0 +1,57 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "encoding/gob" | ||
6 | |||
7 | "github.com/hashicorp/terraform/config" | ||
8 | ) | ||
9 | |||
10 | func (t *Tree) GobDecode(bs []byte) error { | ||
11 | t.lock.Lock() | ||
12 | defer t.lock.Unlock() | ||
13 | |||
14 | // Decode the gob data | ||
15 | var data treeGob | ||
16 | dec := gob.NewDecoder(bytes.NewReader(bs)) | ||
17 | if err := dec.Decode(&data); err != nil { | ||
18 | return err | ||
19 | } | ||
20 | |||
21 | // Set the fields | ||
22 | t.name = data.Name | ||
23 | t.config = data.Config | ||
24 | t.children = data.Children | ||
25 | t.path = data.Path | ||
26 | |||
27 | return nil | ||
28 | } | ||
29 | |||
30 | func (t *Tree) GobEncode() ([]byte, error) { | ||
31 | data := &treeGob{ | ||
32 | Config: t.config, | ||
33 | Children: t.children, | ||
34 | Name: t.name, | ||
35 | Path: t.path, | ||
36 | } | ||
37 | |||
38 | var buf bytes.Buffer | ||
39 | enc := gob.NewEncoder(&buf) | ||
40 | if err := enc.Encode(data); err != nil { | ||
41 | return nil, err | ||
42 | } | ||
43 | |||
44 | return buf.Bytes(), nil | ||
45 | } | ||
46 | |||
47 | // treeGob is used as a structure to Gob encode a tree. | ||
48 | // | ||
49 | // This structure is private so it can't be referenced but the fields are | ||
50 | // public, allowing Gob to properly encode this. When we decode this, we are | ||
51 | // able to turn it into a Tree. | ||
52 | type treeGob struct { | ||
53 | Config *config.Config | ||
54 | Children map[string]*Tree | ||
55 | Name string | ||
56 | Path []string | ||
57 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go new file mode 100644 index 0000000..090d4f7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go | |||
@@ -0,0 +1,118 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/go-multierror" | ||
8 | "github.com/hashicorp/terraform/dag" | ||
9 | ) | ||
10 | |||
11 | // validateProviderAlias validates that all provider alias references are | ||
12 | // defined at some point in the parent tree. This improves UX by catching | ||
13 | // alias typos at the slight cost of requiring a declaration of usage. This | ||
14 | // is usually a good tradeoff since not many aliases are used. | ||
15 | func (t *Tree) validateProviderAlias() error { | ||
16 | // If we're not the root, don't perform this validation. We must be the | ||
17 | // root since we require full tree visibilty. | ||
18 | if len(t.path) != 0 { | ||
19 | return nil | ||
20 | } | ||
21 | |||
22 | // We'll use a graph to keep track of defined aliases at each level. | ||
23 | // As long as a parent defines an alias, it is okay. | ||
24 | var g dag.AcyclicGraph | ||
25 | t.buildProviderAliasGraph(&g, nil) | ||
26 | |||
27 | // Go through the graph and check that the usage is all good. | ||
28 | var err error | ||
29 | for _, v := range g.Vertices() { | ||
30 | pv, ok := v.(*providerAliasVertex) | ||
31 | if !ok { | ||
32 | // This shouldn't happen, just ignore it. | ||
33 | continue | ||
34 | } | ||
35 | |||
36 | // If we're not using any aliases, fast track and just continue | ||
37 | if len(pv.Used) == 0 { | ||
38 | continue | ||
39 | } | ||
40 | |||
41 | // Grab the ancestors since we're going to have to check if our | ||
42 | // parents define any of our aliases. | ||
43 | var parents []*providerAliasVertex | ||
44 | ancestors, _ := g.Ancestors(v) | ||
45 | for _, raw := range ancestors.List() { | ||
46 | if pv, ok := raw.(*providerAliasVertex); ok { | ||
47 | parents = append(parents, pv) | ||
48 | } | ||
49 | } | ||
50 | for k, _ := range pv.Used { | ||
51 | // Check if we define this | ||
52 | if _, ok := pv.Defined[k]; ok { | ||
53 | continue | ||
54 | } | ||
55 | |||
56 | // Check for a parent | ||
57 | found := false | ||
58 | for _, parent := range parents { | ||
59 | _, found = parent.Defined[k] | ||
60 | if found { | ||
61 | break | ||
62 | } | ||
63 | } | ||
64 | if found { | ||
65 | continue | ||
66 | } | ||
67 | |||
68 | // We didn't find the alias, error! | ||
69 | err = multierror.Append(err, fmt.Errorf( | ||
70 | "module %s: provider alias must be defined by the module or a parent: %s", | ||
71 | strings.Join(pv.Path, "."), k)) | ||
72 | } | ||
73 | } | ||
74 | |||
75 | return err | ||
76 | } | ||
77 | |||
78 | func (t *Tree) buildProviderAliasGraph(g *dag.AcyclicGraph, parent dag.Vertex) { | ||
79 | // Add all our defined aliases | ||
80 | defined := make(map[string]struct{}) | ||
81 | for _, p := range t.config.ProviderConfigs { | ||
82 | defined[p.FullName()] = struct{}{} | ||
83 | } | ||
84 | |||
85 | // Add all our used aliases | ||
86 | used := make(map[string]struct{}) | ||
87 | for _, r := range t.config.Resources { | ||
88 | if r.Provider != "" { | ||
89 | used[r.Provider] = struct{}{} | ||
90 | } | ||
91 | } | ||
92 | |||
93 | // Add it to the graph | ||
94 | vertex := &providerAliasVertex{ | ||
95 | Path: t.Path(), | ||
96 | Defined: defined, | ||
97 | Used: used, | ||
98 | } | ||
99 | g.Add(vertex) | ||
100 | |||
101 | // Connect to our parent if we have one | ||
102 | if parent != nil { | ||
103 | g.Connect(dag.BasicEdge(vertex, parent)) | ||
104 | } | ||
105 | |||
106 | // Build all our children | ||
107 | for _, c := range t.Children() { | ||
108 | c.buildProviderAliasGraph(g, vertex) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | // providerAliasVertex is the vertex for the graph that keeps track of | ||
113 | // defined provider aliases. | ||
114 | type providerAliasVertex struct { | ||
115 | Path []string | ||
116 | Defined map[string]struct{} | ||
117 | Used map[string]struct{} | ||
118 | } | ||