]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package configs |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ||
6 | "github.com/hashicorp/terraform/addrs" | |
7 | ||
8 | "github.com/hashicorp/hcl2/hcl" | |
9 | "github.com/zclconf/go-cty/cty" | |
10 | "github.com/zclconf/go-cty/cty/convert" | |
11 | ) | |
12 | ||
13 | // The methods in this file are used by Module.mergeFile to apply overrides | |
14 | // to our different configuration elements. These methods all follow the | |
15 | // pattern of mutating the receiver to incorporate settings from the parameter, | |
16 | // returning error diagnostics if any aspect of the parameter cannot be merged | |
17 | // into the receiver for some reason. | |
18 | // | |
19 | // User expectation is that anything _explicitly_ set in the given object | |
20 | // should take precedence over the corresponding settings in the receiver, | |
21 | // but that anything omitted in the given object should be left unchanged. | |
22 | // In some cases it may be reasonable to do a "deep merge" of certain nested | |
23 | // features, if it is possible to unambiguously correlate the nested elements | |
24 | // and their behaviors are orthogonal to each other. | |
25 | ||
26 | func (p *Provider) merge(op *Provider) hcl.Diagnostics { | |
27 | var diags hcl.Diagnostics | |
28 | ||
29 | if op.Version.Required != nil { | |
30 | p.Version = op.Version | |
31 | } | |
32 | ||
33 | p.Config = MergeBodies(p.Config, op.Config) | |
34 | ||
35 | return diags | |
36 | } | |
37 | ||
38 | func mergeProviderVersionConstraints(recv map[string][]VersionConstraint, ovrd []*ProviderRequirement) { | |
39 | // Any provider name that's mentioned in the override gets nilled out in | |
40 | // our map so that we'll rebuild it below. Any provider not mentioned is | |
41 | // left unchanged. | |
42 | for _, reqd := range ovrd { | |
43 | delete(recv, reqd.Name) | |
44 | } | |
45 | for _, reqd := range ovrd { | |
46 | recv[reqd.Name] = append(recv[reqd.Name], reqd.Requirement) | |
47 | } | |
48 | } | |
49 | ||
50 | func (v *Variable) merge(ov *Variable) hcl.Diagnostics { | |
51 | var diags hcl.Diagnostics | |
52 | ||
53 | if ov.DescriptionSet { | |
54 | v.Description = ov.Description | |
55 | v.DescriptionSet = ov.DescriptionSet | |
56 | } | |
57 | if ov.Default != cty.NilVal { | |
58 | v.Default = ov.Default | |
59 | } | |
60 | if ov.Type != cty.NilType { | |
61 | v.Type = ov.Type | |
62 | } | |
63 | if ov.ParsingMode != 0 { | |
64 | v.ParsingMode = ov.ParsingMode | |
65 | } | |
66 | ||
67 | // If the override file overrode type without default or vice-versa then | |
68 | // it may have created an invalid situation, which we'll catch now by | |
69 | // attempting to re-convert the value. | |
70 | // | |
71 | // Note that here we may be re-converting an already-converted base value | |
72 | // from the base config. This will be a no-op if the type was not changed, | |
73 | // but in particular might be user-observable in the edge case where the | |
74 | // literal value in config could've been converted to the overridden type | |
75 | // constraint but the converted value cannot. In practice, this situation | |
76 | // should be rare since most of our conversions are interchangable. | |
77 | if v.Default != cty.NilVal { | |
78 | val, err := convert.Convert(v.Default, v.Type) | |
79 | if err != nil { | |
80 | // What exactly we'll say in the error message here depends on whether | |
81 | // it was Default or Type that was overridden here. | |
82 | switch { | |
83 | case ov.Type != cty.NilType && ov.Default == cty.NilVal: | |
84 | // If only the type was overridden | |
85 | diags = append(diags, &hcl.Diagnostic{ | |
86 | Severity: hcl.DiagError, | |
87 | Summary: "Invalid default value for variable", | |
88 | Detail: fmt.Sprintf("Overriding this variable's type constraint has made its default value invalid: %s.", err), | |
89 | Subject: &ov.DeclRange, | |
90 | }) | |
91 | case ov.Type == cty.NilType && ov.Default != cty.NilVal: | |
92 | // Only the default was overridden | |
93 | diags = append(diags, &hcl.Diagnostic{ | |
94 | Severity: hcl.DiagError, | |
95 | Summary: "Invalid default value for variable", | |
96 | Detail: fmt.Sprintf("The overridden default value for this variable is not compatible with the variable's type constraint: %s.", err), | |
97 | Subject: &ov.DeclRange, | |
98 | }) | |
99 | default: | |
100 | diags = append(diags, &hcl.Diagnostic{ | |
101 | Severity: hcl.DiagError, | |
102 | Summary: "Invalid default value for variable", | |
103 | Detail: fmt.Sprintf("This variable's default value is not compatible with its type constraint: %s.", err), | |
104 | Subject: &ov.DeclRange, | |
105 | }) | |
106 | } | |
107 | } else { | |
108 | v.Default = val | |
109 | } | |
110 | } | |
111 | ||
112 | return diags | |
113 | } | |
114 | ||
115 | func (l *Local) merge(ol *Local) hcl.Diagnostics { | |
116 | var diags hcl.Diagnostics | |
117 | ||
118 | // Since a local is just a single expression in configuration, the | |
119 | // override definition entirely replaces the base definition, including | |
120 | // the source range so that we'll send the user to the right place if | |
121 | // there is an error. | |
122 | l.Expr = ol.Expr | |
123 | l.DeclRange = ol.DeclRange | |
124 | ||
125 | return diags | |
126 | } | |
127 | ||
128 | func (o *Output) merge(oo *Output) hcl.Diagnostics { | |
129 | var diags hcl.Diagnostics | |
130 | ||
131 | if oo.Description != "" { | |
132 | o.Description = oo.Description | |
133 | } | |
134 | if oo.Expr != nil { | |
135 | o.Expr = oo.Expr | |
136 | } | |
137 | if oo.SensitiveSet { | |
138 | o.Sensitive = oo.Sensitive | |
139 | o.SensitiveSet = oo.SensitiveSet | |
140 | } | |
141 | ||
142 | // We don't allow depends_on to be overridden because that is likely to | |
143 | // cause confusing misbehavior. | |
144 | if len(oo.DependsOn) != 0 { | |
145 | diags = append(diags, &hcl.Diagnostic{ | |
146 | Severity: hcl.DiagError, | |
147 | Summary: "Unsupported override", | |
148 | Detail: "The depends_on argument may not be overridden.", | |
149 | Subject: oo.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have | |
150 | }) | |
151 | } | |
152 | ||
153 | return diags | |
154 | } | |
155 | ||
156 | func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics { | |
157 | var diags hcl.Diagnostics | |
158 | ||
159 | if omc.SourceSet { | |
160 | mc.SourceAddr = omc.SourceAddr | |
161 | mc.SourceAddrRange = omc.SourceAddrRange | |
162 | mc.SourceSet = omc.SourceSet | |
163 | } | |
164 | ||
165 | if omc.Count != nil { | |
166 | mc.Count = omc.Count | |
167 | } | |
168 | ||
169 | if omc.ForEach != nil { | |
170 | mc.ForEach = omc.ForEach | |
171 | } | |
172 | ||
173 | if len(omc.Version.Required) != 0 { | |
174 | mc.Version = omc.Version | |
175 | } | |
176 | ||
177 | mc.Config = MergeBodies(mc.Config, omc.Config) | |
178 | ||
179 | // We don't allow depends_on to be overridden because that is likely to | |
180 | // cause confusing misbehavior. | |
181 | if len(mc.DependsOn) != 0 { | |
182 | diags = append(diags, &hcl.Diagnostic{ | |
183 | Severity: hcl.DiagError, | |
184 | Summary: "Unsupported override", | |
185 | Detail: "The depends_on argument may not be overridden.", | |
186 | Subject: mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have | |
187 | }) | |
188 | } | |
189 | ||
190 | return diags | |
191 | } | |
192 | ||
193 | func (r *Resource) merge(or *Resource) hcl.Diagnostics { | |
194 | var diags hcl.Diagnostics | |
195 | ||
196 | if r.Mode != or.Mode { | |
197 | // This is always a programming error, since managed and data resources | |
198 | // are kept in separate maps in the configuration structures. | |
199 | panic(fmt.Errorf("can't merge %s into %s", or.Mode, r.Mode)) | |
200 | } | |
201 | ||
202 | if or.Count != nil { | |
203 | r.Count = or.Count | |
204 | } | |
205 | if or.ForEach != nil { | |
206 | r.ForEach = or.ForEach | |
207 | } | |
208 | if or.ProviderConfigRef != nil { | |
209 | r.ProviderConfigRef = or.ProviderConfigRef | |
210 | } | |
211 | if r.Mode == addrs.ManagedResourceMode { | |
212 | // or.Managed is always non-nil for managed resource mode | |
213 | ||
214 | if or.Managed.Connection != nil { | |
215 | r.Managed.Connection = or.Managed.Connection | |
216 | } | |
217 | if or.Managed.CreateBeforeDestroySet { | |
218 | r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy | |
219 | r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet | |
220 | } | |
221 | if len(or.Managed.IgnoreChanges) != 0 { | |
222 | r.Managed.IgnoreChanges = or.Managed.IgnoreChanges | |
223 | } | |
224 | if or.Managed.PreventDestroySet { | |
225 | r.Managed.PreventDestroy = or.Managed.PreventDestroy | |
226 | r.Managed.PreventDestroySet = or.Managed.PreventDestroySet | |
227 | } | |
228 | if len(or.Managed.Provisioners) != 0 { | |
229 | r.Managed.Provisioners = or.Managed.Provisioners | |
230 | } | |
231 | } | |
232 | ||
233 | r.Config = MergeBodies(r.Config, or.Config) | |
234 | ||
235 | // We don't allow depends_on to be overridden because that is likely to | |
236 | // cause confusing misbehavior. | |
237 | if len(or.DependsOn) != 0 { | |
238 | diags = append(diags, &hcl.Diagnostic{ | |
239 | Severity: hcl.DiagError, | |
240 | Summary: "Unsupported override", | |
241 | Detail: "The depends_on argument may not be overridden.", | |
242 | Subject: or.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have | |
243 | }) | |
244 | } | |
245 | ||
246 | return diags | |
247 | } |