]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package configs |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ||
6 | "github.com/hashicorp/hcl2/gohcl" | |
7 | "github.com/hashicorp/hcl2/hcl" | |
8 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | |
9 | ) | |
10 | ||
11 | // ModuleCall represents a "module" block in a module or file. | |
12 | type ModuleCall struct { | |
13 | Name string | |
14 | ||
15 | SourceAddr string | |
16 | SourceAddrRange hcl.Range | |
17 | SourceSet bool | |
18 | ||
19 | Config hcl.Body | |
20 | ||
21 | Version VersionConstraint | |
22 | ||
23 | Count hcl.Expression | |
24 | ForEach hcl.Expression | |
25 | ||
26 | Providers []PassedProviderConfig | |
27 | ||
28 | DependsOn []hcl.Traversal | |
29 | ||
30 | DeclRange hcl.Range | |
31 | } | |
32 | ||
33 | func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagnostics) { | |
34 | mc := &ModuleCall{ | |
35 | Name: block.Labels[0], | |
36 | DeclRange: block.DefRange, | |
37 | } | |
38 | ||
39 | schema := moduleBlockSchema | |
40 | if override { | |
41 | schema = schemaForOverrides(schema) | |
42 | } | |
43 | ||
44 | content, remain, diags := block.Body.PartialContent(schema) | |
45 | mc.Config = remain | |
46 | ||
47 | if !hclsyntax.ValidIdentifier(mc.Name) { | |
48 | diags = append(diags, &hcl.Diagnostic{ | |
49 | Severity: hcl.DiagError, | |
50 | Summary: "Invalid module instance name", | |
51 | Detail: badIdentifierDetail, | |
52 | Subject: &block.LabelRanges[0], | |
53 | }) | |
54 | } | |
55 | ||
56 | if attr, exists := content.Attributes["source"]; exists { | |
57 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &mc.SourceAddr) | |
58 | diags = append(diags, valDiags...) | |
59 | mc.SourceAddrRange = attr.Expr.Range() | |
60 | mc.SourceSet = true | |
61 | } | |
62 | ||
63 | if attr, exists := content.Attributes["version"]; exists { | |
64 | var versionDiags hcl.Diagnostics | |
65 | mc.Version, versionDiags = decodeVersionConstraint(attr) | |
66 | diags = append(diags, versionDiags...) | |
67 | } | |
68 | ||
69 | if attr, exists := content.Attributes["count"]; exists { | |
70 | mc.Count = attr.Expr | |
71 | ||
72 | // We currently parse this, but don't yet do anything with it. | |
73 | diags = append(diags, &hcl.Diagnostic{ | |
74 | Severity: hcl.DiagError, | |
75 | Summary: "Reserved argument name in module block", | |
76 | Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), | |
77 | Subject: &attr.NameRange, | |
78 | }) | |
79 | } | |
80 | ||
81 | if attr, exists := content.Attributes["for_each"]; exists { | |
82 | mc.ForEach = attr.Expr | |
83 | ||
84 | // We currently parse this, but don't yet do anything with it. | |
85 | diags = append(diags, &hcl.Diagnostic{ | |
86 | Severity: hcl.DiagError, | |
87 | Summary: "Reserved argument name in module block", | |
88 | Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), | |
89 | Subject: &attr.NameRange, | |
90 | }) | |
91 | } | |
92 | ||
93 | if attr, exists := content.Attributes["depends_on"]; exists { | |
94 | deps, depsDiags := decodeDependsOn(attr) | |
95 | diags = append(diags, depsDiags...) | |
96 | mc.DependsOn = append(mc.DependsOn, deps...) | |
97 | ||
98 | // We currently parse this, but don't yet do anything with it. | |
99 | diags = append(diags, &hcl.Diagnostic{ | |
100 | Severity: hcl.DiagError, | |
101 | Summary: "Reserved argument name in module block", | |
102 | Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), | |
103 | Subject: &attr.NameRange, | |
104 | }) | |
105 | } | |
106 | ||
107 | if attr, exists := content.Attributes["providers"]; exists { | |
108 | seen := make(map[string]hcl.Range) | |
109 | pairs, pDiags := hcl.ExprMap(attr.Expr) | |
110 | diags = append(diags, pDiags...) | |
111 | for _, pair := range pairs { | |
112 | key, keyDiags := decodeProviderConfigRef(pair.Key, "providers") | |
113 | diags = append(diags, keyDiags...) | |
114 | value, valueDiags := decodeProviderConfigRef(pair.Value, "providers") | |
115 | diags = append(diags, valueDiags...) | |
116 | if keyDiags.HasErrors() || valueDiags.HasErrors() { | |
117 | continue | |
118 | } | |
119 | ||
120 | matchKey := key.String() | |
121 | if prev, exists := seen[matchKey]; exists { | |
122 | diags = append(diags, &hcl.Diagnostic{ | |
123 | Severity: hcl.DiagError, | |
124 | Summary: "Duplicate provider address", | |
125 | Detail: fmt.Sprintf("A provider configuration was already passed to %s at %s. Each child provider configuration can be assigned only once.", matchKey, prev), | |
126 | Subject: pair.Value.Range().Ptr(), | |
127 | }) | |
128 | continue | |
129 | } | |
130 | ||
131 | rng := hcl.RangeBetween(pair.Key.Range(), pair.Value.Range()) | |
132 | seen[matchKey] = rng | |
133 | mc.Providers = append(mc.Providers, PassedProviderConfig{ | |
134 | InChild: key, | |
135 | InParent: value, | |
136 | }) | |
137 | } | |
138 | } | |
139 | ||
140 | // Reserved block types (all of them) | |
141 | for _, block := range content.Blocks { | |
142 | diags = append(diags, &hcl.Diagnostic{ | |
143 | Severity: hcl.DiagError, | |
144 | Summary: "Reserved block type name in module block", | |
145 | Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), | |
146 | Subject: &block.TypeRange, | |
147 | }) | |
148 | } | |
149 | ||
150 | return mc, diags | |
151 | } | |
152 | ||
153 | // PassedProviderConfig represents a provider config explicitly passed down to | |
154 | // a child module, possibly giving it a new local address in the process. | |
155 | type PassedProviderConfig struct { | |
156 | InChild *ProviderConfigRef | |
157 | InParent *ProviderConfigRef | |
158 | } | |
159 | ||
160 | var moduleBlockSchema = &hcl.BodySchema{ | |
161 | Attributes: []hcl.AttributeSchema{ | |
162 | { | |
163 | Name: "source", | |
164 | Required: true, | |
165 | }, | |
166 | { | |
167 | Name: "version", | |
168 | }, | |
169 | { | |
170 | Name: "count", | |
171 | }, | |
172 | { | |
173 | Name: "for_each", | |
174 | }, | |
175 | { | |
176 | Name: "depends_on", | |
177 | }, | |
178 | { | |
179 | Name: "providers", | |
180 | }, | |
181 | }, | |
182 | Blocks: []hcl.BlockHeaderSchema{ | |
183 | // These are all reserved for future use. | |
184 | {Type: "lifecycle"}, | |
185 | {Type: "locals"}, | |
186 | {Type: "provider", LabelNames: []string{"type"}}, | |
187 | }, | |
188 | } |