diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/configs/module.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/configs/module.go | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/configs/module.go b/vendor/github.com/hashicorp/terraform/configs/module.go new file mode 100644 index 0000000..250f9d3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/module.go | |||
@@ -0,0 +1,404 @@ | |||
1 | package configs | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/hcl" | ||
7 | |||
8 | "github.com/hashicorp/terraform/addrs" | ||
9 | ) | ||
10 | |||
11 | // Module is a container for a set of configuration constructs that are | ||
12 | // evaluated within a common namespace. | ||
13 | type Module struct { | ||
14 | // SourceDir is the filesystem directory that the module was loaded from. | ||
15 | // | ||
16 | // This is populated automatically only for configurations loaded with | ||
17 | // LoadConfigDir. If the parser is using a virtual filesystem then the | ||
18 | // path here will be in terms of that virtual filesystem. | ||
19 | |||
20 | // Any other caller that constructs a module directly with NewModule may | ||
21 | // assign a suitable value to this attribute before using it for other | ||
22 | // purposes. It should be treated as immutable by all consumers of Module | ||
23 | // values. | ||
24 | SourceDir string | ||
25 | |||
26 | CoreVersionConstraints []VersionConstraint | ||
27 | |||
28 | Backend *Backend | ||
29 | ProviderConfigs map[string]*Provider | ||
30 | ProviderRequirements map[string][]VersionConstraint | ||
31 | |||
32 | Variables map[string]*Variable | ||
33 | Locals map[string]*Local | ||
34 | Outputs map[string]*Output | ||
35 | |||
36 | ModuleCalls map[string]*ModuleCall | ||
37 | |||
38 | ManagedResources map[string]*Resource | ||
39 | DataResources map[string]*Resource | ||
40 | } | ||
41 | |||
42 | // File describes the contents of a single configuration file. | ||
43 | // | ||
44 | // Individual files are not usually used alone, but rather combined together | ||
45 | // with other files (conventionally, those in the same directory) to produce | ||
46 | // a *Module, using NewModule. | ||
47 | // | ||
48 | // At the level of an individual file we represent directly the structural | ||
49 | // elements present in the file, without any attempt to detect conflicting | ||
50 | // declarations. A File object can therefore be used for some basic static | ||
51 | // analysis of individual elements, but must be built into a Module to detect | ||
52 | // duplicate declarations. | ||
53 | type File struct { | ||
54 | CoreVersionConstraints []VersionConstraint | ||
55 | |||
56 | Backends []*Backend | ||
57 | ProviderConfigs []*Provider | ||
58 | ProviderRequirements []*ProviderRequirement | ||
59 | |||
60 | Variables []*Variable | ||
61 | Locals []*Local | ||
62 | Outputs []*Output | ||
63 | |||
64 | ModuleCalls []*ModuleCall | ||
65 | |||
66 | ManagedResources []*Resource | ||
67 | DataResources []*Resource | ||
68 | } | ||
69 | |||
70 | // NewModule takes a list of primary files and a list of override files and | ||
71 | // produces a *Module by combining the files together. | ||
72 | // | ||
73 | // If there are any conflicting declarations in the given files -- for example, | ||
74 | // if the same variable name is defined twice -- then the resulting module | ||
75 | // will be incomplete and error diagnostics will be returned. Careful static | ||
76 | // analysis of the returned Module is still possible in this case, but the | ||
77 | // module will probably not be semantically valid. | ||
78 | func NewModule(primaryFiles, overrideFiles []*File) (*Module, hcl.Diagnostics) { | ||
79 | var diags hcl.Diagnostics | ||
80 | mod := &Module{ | ||
81 | ProviderConfigs: map[string]*Provider{}, | ||
82 | ProviderRequirements: map[string][]VersionConstraint{}, | ||
83 | Variables: map[string]*Variable{}, | ||
84 | Locals: map[string]*Local{}, | ||
85 | Outputs: map[string]*Output{}, | ||
86 | ModuleCalls: map[string]*ModuleCall{}, | ||
87 | ManagedResources: map[string]*Resource{}, | ||
88 | DataResources: map[string]*Resource{}, | ||
89 | } | ||
90 | |||
91 | for _, file := range primaryFiles { | ||
92 | fileDiags := mod.appendFile(file) | ||
93 | diags = append(diags, fileDiags...) | ||
94 | } | ||
95 | |||
96 | for _, file := range overrideFiles { | ||
97 | fileDiags := mod.mergeFile(file) | ||
98 | diags = append(diags, fileDiags...) | ||
99 | } | ||
100 | |||
101 | return mod, diags | ||
102 | } | ||
103 | |||
104 | // ResourceByAddr returns the configuration for the resource with the given | ||
105 | // address, or nil if there is no such resource. | ||
106 | func (m *Module) ResourceByAddr(addr addrs.Resource) *Resource { | ||
107 | key := addr.String() | ||
108 | switch addr.Mode { | ||
109 | case addrs.ManagedResourceMode: | ||
110 | return m.ManagedResources[key] | ||
111 | case addrs.DataResourceMode: | ||
112 | return m.DataResources[key] | ||
113 | default: | ||
114 | return nil | ||
115 | } | ||
116 | } | ||
117 | |||
118 | func (m *Module) appendFile(file *File) hcl.Diagnostics { | ||
119 | var diags hcl.Diagnostics | ||
120 | |||
121 | for _, constraint := range file.CoreVersionConstraints { | ||
122 | // If there are any conflicting requirements then we'll catch them | ||
123 | // when we actually check these constraints. | ||
124 | m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) | ||
125 | } | ||
126 | |||
127 | for _, b := range file.Backends { | ||
128 | if m.Backend != nil { | ||
129 | diags = append(diags, &hcl.Diagnostic{ | ||
130 | Severity: hcl.DiagError, | ||
131 | Summary: "Duplicate backend configuration", | ||
132 | Detail: fmt.Sprintf("A module may have only one backend configuration. The backend was previously configured at %s.", m.Backend.DeclRange), | ||
133 | Subject: &b.DeclRange, | ||
134 | }) | ||
135 | continue | ||
136 | } | ||
137 | m.Backend = b | ||
138 | } | ||
139 | |||
140 | for _, pc := range file.ProviderConfigs { | ||
141 | key := pc.moduleUniqueKey() | ||
142 | if existing, exists := m.ProviderConfigs[key]; exists { | ||
143 | if existing.Alias == "" { | ||
144 | diags = append(diags, &hcl.Diagnostic{ | ||
145 | Severity: hcl.DiagError, | ||
146 | Summary: "Duplicate provider configuration", | ||
147 | Detail: fmt.Sprintf("A default (non-aliased) provider configuration for %q was already given at %s. If multiple configurations are required, set the \"alias\" argument for alternative configurations.", existing.Name, existing.DeclRange), | ||
148 | Subject: &pc.DeclRange, | ||
149 | }) | ||
150 | } else { | ||
151 | diags = append(diags, &hcl.Diagnostic{ | ||
152 | Severity: hcl.DiagError, | ||
153 | Summary: "Duplicate provider configuration", | ||
154 | Detail: fmt.Sprintf("A provider configuration for %q with alias %q was already given at %s. Each configuration for the same provider must have a distinct alias.", existing.Name, existing.Alias, existing.DeclRange), | ||
155 | Subject: &pc.DeclRange, | ||
156 | }) | ||
157 | } | ||
158 | continue | ||
159 | } | ||
160 | m.ProviderConfigs[key] = pc | ||
161 | } | ||
162 | |||
163 | for _, reqd := range file.ProviderRequirements { | ||
164 | m.ProviderRequirements[reqd.Name] = append(m.ProviderRequirements[reqd.Name], reqd.Requirement) | ||
165 | } | ||
166 | |||
167 | for _, v := range file.Variables { | ||
168 | if existing, exists := m.Variables[v.Name]; exists { | ||
169 | diags = append(diags, &hcl.Diagnostic{ | ||
170 | Severity: hcl.DiagError, | ||
171 | Summary: "Duplicate variable declaration", | ||
172 | Detail: fmt.Sprintf("A variable named %q was already declared at %s. Variable names must be unique within a module.", existing.Name, existing.DeclRange), | ||
173 | Subject: &v.DeclRange, | ||
174 | }) | ||
175 | } | ||
176 | m.Variables[v.Name] = v | ||
177 | } | ||
178 | |||
179 | for _, l := range file.Locals { | ||
180 | if existing, exists := m.Locals[l.Name]; exists { | ||
181 | diags = append(diags, &hcl.Diagnostic{ | ||
182 | Severity: hcl.DiagError, | ||
183 | Summary: "Duplicate local value definition", | ||
184 | Detail: fmt.Sprintf("A local value named %q was already defined at %s. Local value names must be unique within a module.", existing.Name, existing.DeclRange), | ||
185 | Subject: &l.DeclRange, | ||
186 | }) | ||
187 | } | ||
188 | m.Locals[l.Name] = l | ||
189 | } | ||
190 | |||
191 | for _, o := range file.Outputs { | ||
192 | if existing, exists := m.Outputs[o.Name]; exists { | ||
193 | diags = append(diags, &hcl.Diagnostic{ | ||
194 | Severity: hcl.DiagError, | ||
195 | Summary: "Duplicate output definition", | ||
196 | Detail: fmt.Sprintf("An output named %q was already defined at %s. Output names must be unique within a module.", existing.Name, existing.DeclRange), | ||
197 | Subject: &o.DeclRange, | ||
198 | }) | ||
199 | } | ||
200 | m.Outputs[o.Name] = o | ||
201 | } | ||
202 | |||
203 | for _, mc := range file.ModuleCalls { | ||
204 | if existing, exists := m.ModuleCalls[mc.Name]; exists { | ||
205 | diags = append(diags, &hcl.Diagnostic{ | ||
206 | Severity: hcl.DiagError, | ||
207 | Summary: "Duplicate module call", | ||
208 | Detail: fmt.Sprintf("An module call named %q was already defined at %s. Module calls must have unique names within a module.", existing.Name, existing.DeclRange), | ||
209 | Subject: &mc.DeclRange, | ||
210 | }) | ||
211 | } | ||
212 | m.ModuleCalls[mc.Name] = mc | ||
213 | } | ||
214 | |||
215 | for _, r := range file.ManagedResources { | ||
216 | key := r.moduleUniqueKey() | ||
217 | if existing, exists := m.ManagedResources[key]; exists { | ||
218 | diags = append(diags, &hcl.Diagnostic{ | ||
219 | Severity: hcl.DiagError, | ||
220 | Summary: fmt.Sprintf("Duplicate resource %q configuration", existing.Type), | ||
221 | Detail: fmt.Sprintf("A %s resource named %q was already declared at %s. Resource names must be unique per type in each module.", existing.Type, existing.Name, existing.DeclRange), | ||
222 | Subject: &r.DeclRange, | ||
223 | }) | ||
224 | continue | ||
225 | } | ||
226 | m.ManagedResources[key] = r | ||
227 | } | ||
228 | |||
229 | for _, r := range file.DataResources { | ||
230 | key := r.moduleUniqueKey() | ||
231 | if existing, exists := m.DataResources[key]; exists { | ||
232 | diags = append(diags, &hcl.Diagnostic{ | ||
233 | Severity: hcl.DiagError, | ||
234 | Summary: fmt.Sprintf("Duplicate data %q configuration", existing.Type), | ||
235 | Detail: fmt.Sprintf("A %s data resource named %q was already declared at %s. Resource names must be unique per type in each module.", existing.Type, existing.Name, existing.DeclRange), | ||
236 | Subject: &r.DeclRange, | ||
237 | }) | ||
238 | continue | ||
239 | } | ||
240 | m.DataResources[key] = r | ||
241 | } | ||
242 | |||
243 | return diags | ||
244 | } | ||
245 | |||
246 | func (m *Module) mergeFile(file *File) hcl.Diagnostics { | ||
247 | var diags hcl.Diagnostics | ||
248 | |||
249 | if len(file.CoreVersionConstraints) != 0 { | ||
250 | // This is a bit of a strange case for overriding since we normally | ||
251 | // would union together across multiple files anyway, but we'll | ||
252 | // allow it and have each override file clobber any existing list. | ||
253 | m.CoreVersionConstraints = nil | ||
254 | for _, constraint := range file.CoreVersionConstraints { | ||
255 | m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if len(file.Backends) != 0 { | ||
260 | switch len(file.Backends) { | ||
261 | case 1: | ||
262 | m.Backend = file.Backends[0] | ||
263 | default: | ||
264 | // An override file with multiple backends is still invalid, even | ||
265 | // though it can override backends from _other_ files. | ||
266 | diags = append(diags, &hcl.Diagnostic{ | ||
267 | Severity: hcl.DiagError, | ||
268 | Summary: "Duplicate backend configuration", | ||
269 | Detail: fmt.Sprintf("Each override file may have only one backend configuration. A backend was previously configured at %s.", file.Backends[0].DeclRange), | ||
270 | Subject: &file.Backends[1].DeclRange, | ||
271 | }) | ||
272 | } | ||
273 | } | ||
274 | |||
275 | for _, pc := range file.ProviderConfigs { | ||
276 | key := pc.moduleUniqueKey() | ||
277 | existing, exists := m.ProviderConfigs[key] | ||
278 | if pc.Alias == "" { | ||
279 | // We allow overriding a non-existing _default_ provider configuration | ||
280 | // because the user model is that an absent provider configuration | ||
281 | // implies an empty provider configuration, which is what the user | ||
282 | // is therefore overriding here. | ||
283 | if exists { | ||
284 | mergeDiags := existing.merge(pc) | ||
285 | diags = append(diags, mergeDiags...) | ||
286 | } else { | ||
287 | m.ProviderConfigs[key] = pc | ||
288 | } | ||
289 | } else { | ||
290 | // For aliased providers, there must be a base configuration to | ||
291 | // override. This allows us to detect and report alias typos | ||
292 | // that might otherwise cause the override to not apply. | ||
293 | if !exists { | ||
294 | diags = append(diags, &hcl.Diagnostic{ | ||
295 | Severity: hcl.DiagError, | ||
296 | Summary: "Missing base provider configuration for override", | ||
297 | Detail: fmt.Sprintf("There is no %s provider configuration with the alias %q. An override file can only override an aliased provider configuration that was already defined in a primary configuration file.", pc.Name, pc.Alias), | ||
298 | Subject: &pc.DeclRange, | ||
299 | }) | ||
300 | continue | ||
301 | } | ||
302 | mergeDiags := existing.merge(pc) | ||
303 | diags = append(diags, mergeDiags...) | ||
304 | } | ||
305 | } | ||
306 | |||
307 | if len(file.ProviderRequirements) != 0 { | ||
308 | mergeProviderVersionConstraints(m.ProviderRequirements, file.ProviderRequirements) | ||
309 | } | ||
310 | |||
311 | for _, v := range file.Variables { | ||
312 | existing, exists := m.Variables[v.Name] | ||
313 | if !exists { | ||
314 | diags = append(diags, &hcl.Diagnostic{ | ||
315 | Severity: hcl.DiagError, | ||
316 | Summary: "Missing base variable declaration to override", | ||
317 | Detail: fmt.Sprintf("There is no variable named %q. An override file can only override a variable that was already declared in a primary configuration file.", v.Name), | ||
318 | Subject: &v.DeclRange, | ||
319 | }) | ||
320 | continue | ||
321 | } | ||
322 | mergeDiags := existing.merge(v) | ||
323 | diags = append(diags, mergeDiags...) | ||
324 | } | ||
325 | |||
326 | for _, l := range file.Locals { | ||
327 | existing, exists := m.Locals[l.Name] | ||
328 | if !exists { | ||
329 | diags = append(diags, &hcl.Diagnostic{ | ||
330 | Severity: hcl.DiagError, | ||
331 | Summary: "Missing base local value definition to override", | ||
332 | Detail: fmt.Sprintf("There is no local value named %q. An override file can only override a local value that was already defined in a primary configuration file.", l.Name), | ||
333 | Subject: &l.DeclRange, | ||
334 | }) | ||
335 | continue | ||
336 | } | ||
337 | mergeDiags := existing.merge(l) | ||
338 | diags = append(diags, mergeDiags...) | ||
339 | } | ||
340 | |||
341 | for _, o := range file.Outputs { | ||
342 | existing, exists := m.Outputs[o.Name] | ||
343 | if !exists { | ||
344 | diags = append(diags, &hcl.Diagnostic{ | ||
345 | Severity: hcl.DiagError, | ||
346 | Summary: "Missing base output definition to override", | ||
347 | Detail: fmt.Sprintf("There is no output named %q. An override file can only override an output that was already defined in a primary configuration file.", o.Name), | ||
348 | Subject: &o.DeclRange, | ||
349 | }) | ||
350 | continue | ||
351 | } | ||
352 | mergeDiags := existing.merge(o) | ||
353 | diags = append(diags, mergeDiags...) | ||
354 | } | ||
355 | |||
356 | for _, mc := range file.ModuleCalls { | ||
357 | existing, exists := m.ModuleCalls[mc.Name] | ||
358 | if !exists { | ||
359 | diags = append(diags, &hcl.Diagnostic{ | ||
360 | Severity: hcl.DiagError, | ||
361 | Summary: "Missing module call to override", | ||
362 | Detail: fmt.Sprintf("There is no module call named %q. An override file can only override a module call that was defined in a primary configuration file.", mc.Name), | ||
363 | Subject: &mc.DeclRange, | ||
364 | }) | ||
365 | continue | ||
366 | } | ||
367 | mergeDiags := existing.merge(mc) | ||
368 | diags = append(diags, mergeDiags...) | ||
369 | } | ||
370 | |||
371 | for _, r := range file.ManagedResources { | ||
372 | key := r.moduleUniqueKey() | ||
373 | existing, exists := m.ManagedResources[key] | ||
374 | if !exists { | ||
375 | diags = append(diags, &hcl.Diagnostic{ | ||
376 | Severity: hcl.DiagError, | ||
377 | Summary: "Missing resource to override", | ||
378 | Detail: fmt.Sprintf("There is no %s resource named %q. An override file can only override a resource block defined in a primary configuration file.", r.Type, r.Name), | ||
379 | Subject: &r.DeclRange, | ||
380 | }) | ||
381 | continue | ||
382 | } | ||
383 | mergeDiags := existing.merge(r) | ||
384 | diags = append(diags, mergeDiags...) | ||
385 | } | ||
386 | |||
387 | for _, r := range file.DataResources { | ||
388 | key := r.moduleUniqueKey() | ||
389 | existing, exists := m.DataResources[key] | ||
390 | if !exists { | ||
391 | diags = append(diags, &hcl.Diagnostic{ | ||
392 | Severity: hcl.DiagError, | ||
393 | Summary: "Missing data resource to override", | ||
394 | Detail: fmt.Sprintf("There is no %s data resource named %q. An override file can only override a data block defined in a primary configuration file.", r.Type, r.Name), | ||
395 | Subject: &r.DeclRange, | ||
396 | }) | ||
397 | continue | ||
398 | } | ||
399 | mergeDiags := existing.merge(r) | ||
400 | diags = append(diags, mergeDiags...) | ||
401 | } | ||
402 | |||
403 | return diags | ||
404 | } | ||