6 "github.com/hashicorp/terraform/addrs"
8 "github.com/hashicorp/hcl2/hcl"
9 "github.com/zclconf/go-cty/cty"
10 "github.com/zclconf/go-cty/cty/convert"
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.
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.
26 func (p *Provider) merge(op *Provider) hcl.Diagnostics {
27 var diags hcl.Diagnostics
29 if op.Version.Required != nil {
30 p.Version = op.Version
33 p.Config = MergeBodies(p.Config, op.Config)
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
42 for _, reqd := range ovrd {
43 delete(recv, reqd.Name)
45 for _, reqd := range ovrd {
46 recv[reqd.Name] = append(recv[reqd.Name], reqd.Requirement)
50 func (v *Variable) merge(ov *Variable) hcl.Diagnostics {
51 var diags hcl.Diagnostics
53 if ov.DescriptionSet {
54 v.Description = ov.Description
55 v.DescriptionSet = ov.DescriptionSet
57 if ov.Default != cty.NilVal {
58 v.Default = ov.Default
60 if ov.Type != cty.NilType {
63 if ov.ParsingMode != 0 {
64 v.ParsingMode = ov.ParsingMode
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.
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)
80 // What exactly we'll say in the error message here depends on whether
81 // it was Default or Type that was overridden here.
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,
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,
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,
115 func (l *Local) merge(ol *Local) hcl.Diagnostics {
116 var diags hcl.Diagnostics
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.
123 l.DeclRange = ol.DeclRange
128 func (o *Output) merge(oo *Output) hcl.Diagnostics {
129 var diags hcl.Diagnostics
131 if oo.Description != "" {
132 o.Description = oo.Description
138 o.Sensitive = oo.Sensitive
139 o.SensitiveSet = oo.SensitiveSet
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
156 func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
157 var diags hcl.Diagnostics
160 mc.SourceAddr = omc.SourceAddr
161 mc.SourceAddrRange = omc.SourceAddrRange
162 mc.SourceSet = omc.SourceSet
165 if omc.Count != nil {
169 if omc.ForEach != nil {
170 mc.ForEach = omc.ForEach
173 if len(omc.Version.Required) != 0 {
174 mc.Version = omc.Version
177 mc.Config = MergeBodies(mc.Config, omc.Config)
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
193 func (r *Resource) merge(or *Resource) hcl.Diagnostics {
194 var diags hcl.Diagnostics
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))
205 if or.ForEach != nil {
206 r.ForEach = or.ForEach
208 if or.ProviderConfigRef != nil {
209 r.ProviderConfigRef = or.ProviderConfigRef
211 if r.Mode == addrs.ManagedResourceMode {
212 // or.Managed is always non-nil for managed resource mode
214 if or.Managed.Connection != nil {
215 r.Managed.Connection = or.Managed.Connection
217 if or.Managed.CreateBeforeDestroySet {
218 r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy
219 r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet
221 if len(or.Managed.IgnoreChanges) != 0 {
222 r.Managed.IgnoreChanges = or.Managed.IgnoreChanges
224 if or.Managed.PreventDestroySet {
225 r.Managed.PreventDestroy = or.Managed.PreventDestroy
226 r.Managed.PreventDestroySet = or.Managed.PreventDestroySet
228 if len(or.Managed.Provisioners) != 0 {
229 r.Managed.Provisioners = or.Managed.Provisioners
233 r.Config = MergeBodies(r.Config, or.Config)
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