]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/configs/module.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / configs / module.go
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 }