aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/configs/module_merge.go
blob: 12614c1d60d13f343f8a843c7829e51345df5a89 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
package configs

import (
	"fmt"

	"github.com/hashicorp/terraform/addrs"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/convert"
)

// The methods in this file are used by Module.mergeFile to apply overrides
// to our different configuration elements. These methods all follow the
// pattern of mutating the receiver to incorporate settings from the parameter,
// returning error diagnostics if any aspect of the parameter cannot be merged
// into the receiver for some reason.
//
// User expectation is that anything _explicitly_ set in the given object
// should take precedence over the corresponding settings in the receiver,
// but that anything omitted in the given object should be left unchanged.
// In some cases it may be reasonable to do a "deep merge" of certain nested
// features, if it is possible to unambiguously correlate the nested elements
// and their behaviors are orthogonal to each other.

func (p *Provider) merge(op *Provider) hcl.Diagnostics {
	var diags hcl.Diagnostics

	if op.Version.Required != nil {
		p.Version = op.Version
	}

	p.Config = MergeBodies(p.Config, op.Config)

	return diags
}

func mergeProviderVersionConstraints(recv map[string][]VersionConstraint, ovrd []*ProviderRequirement) {
	// Any provider name that's mentioned in the override gets nilled out in
	// our map so that we'll rebuild it below. Any provider not mentioned is
	// left unchanged.
	for _, reqd := range ovrd {
		delete(recv, reqd.Name)
	}
	for _, reqd := range ovrd {
		recv[reqd.Name] = append(recv[reqd.Name], reqd.Requirement)
	}
}

func (v *Variable) merge(ov *Variable) hcl.Diagnostics {
	var diags hcl.Diagnostics

	if ov.DescriptionSet {
		v.Description = ov.Description
		v.DescriptionSet = ov.DescriptionSet
	}
	if ov.Default != cty.NilVal {
		v.Default = ov.Default
	}
	if ov.Type != cty.NilType {
		v.Type = ov.Type
	}
	if ov.ParsingMode != 0 {
		v.ParsingMode = ov.ParsingMode
	}

	// If the override file overrode type without default or vice-versa then
	// it may have created an invalid situation, which we'll catch now by
	// attempting to re-convert the value.
	//
	// Note that here we may be re-converting an already-converted base value
	// from the base config. This will be a no-op if the type was not changed,
	// but in particular might be user-observable in the edge case where the
	// literal value in config could've been converted to the overridden type
	// constraint but the converted value cannot. In practice, this situation
	// should be rare since most of our conversions are interchangable.
	if v.Default != cty.NilVal {
		val, err := convert.Convert(v.Default, v.Type)
		if err != nil {
			// What exactly we'll say in the error message here depends on whether
			// it was Default or Type that was overridden here.
			switch {
			case ov.Type != cty.NilType && ov.Default == cty.NilVal:
				// If only the type was overridden
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid default value for variable",
					Detail:   fmt.Sprintf("Overriding this variable's type constraint has made its default value invalid: %s.", err),
					Subject:  &ov.DeclRange,
				})
			case ov.Type == cty.NilType && ov.Default != cty.NilVal:
				// Only the default was overridden
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid default value for variable",
					Detail:   fmt.Sprintf("The overridden default value for this variable is not compatible with the variable's type constraint: %s.", err),
					Subject:  &ov.DeclRange,
				})
			default:
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Invalid default value for variable",
					Detail:   fmt.Sprintf("This variable's default value is not compatible with its type constraint: %s.", err),
					Subject:  &ov.DeclRange,
				})
			}
		} else {
			v.Default = val
		}
	}

	return diags
}

func (l *Local) merge(ol *Local) hcl.Diagnostics {
	var diags hcl.Diagnostics

	// Since a local is just a single expression in configuration, the
	// override definition entirely replaces the base definition, including
	// the source range so that we'll send the user to the right place if
	// there is an error.
	l.Expr = ol.Expr
	l.DeclRange = ol.DeclRange

	return diags
}

func (o *Output) merge(oo *Output) hcl.Diagnostics {
	var diags hcl.Diagnostics

	if oo.Description != "" {
		o.Description = oo.Description
	}
	if oo.Expr != nil {
		o.Expr = oo.Expr
	}
	if oo.SensitiveSet {
		o.Sensitive = oo.Sensitive
		o.SensitiveSet = oo.SensitiveSet
	}

	// We don't allow depends_on to be overridden because that is likely to
	// cause confusing misbehavior.
	if len(oo.DependsOn) != 0 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Unsupported override",
			Detail:   "The depends_on argument may not be overridden.",
			Subject:  oo.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
		})
	}

	return diags
}

func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
	var diags hcl.Diagnostics

	if omc.SourceSet {
		mc.SourceAddr = omc.SourceAddr
		mc.SourceAddrRange = omc.SourceAddrRange
		mc.SourceSet = omc.SourceSet
	}

	if omc.Count != nil {
		mc.Count = omc.Count
	}

	if omc.ForEach != nil {
		mc.ForEach = omc.ForEach
	}

	if len(omc.Version.Required) != 0 {
		mc.Version = omc.Version
	}

	mc.Config = MergeBodies(mc.Config, omc.Config)

	// We don't allow depends_on to be overridden because that is likely to
	// cause confusing misbehavior.
	if len(mc.DependsOn) != 0 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Unsupported override",
			Detail:   "The depends_on argument may not be overridden.",
			Subject:  mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
		})
	}

	return diags
}

func (r *Resource) merge(or *Resource) hcl.Diagnostics {
	var diags hcl.Diagnostics

	if r.Mode != or.Mode {
		// This is always a programming error, since managed and data resources
		// are kept in separate maps in the configuration structures.
		panic(fmt.Errorf("can't merge %s into %s", or.Mode, r.Mode))
	}

	if or.Count != nil {
		r.Count = or.Count
	}
	if or.ForEach != nil {
		r.ForEach = or.ForEach
	}
	if or.ProviderConfigRef != nil {
		r.ProviderConfigRef = or.ProviderConfigRef
	}
	if r.Mode == addrs.ManagedResourceMode {
		// or.Managed is always non-nil for managed resource mode

		if or.Managed.Connection != nil {
			r.Managed.Connection = or.Managed.Connection
		}
		if or.Managed.CreateBeforeDestroySet {
			r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy
			r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet
		}
		if len(or.Managed.IgnoreChanges) != 0 {
			r.Managed.IgnoreChanges = or.Managed.IgnoreChanges
		}
		if or.Managed.PreventDestroySet {
			r.Managed.PreventDestroy = or.Managed.PreventDestroy
			r.Managed.PreventDestroySet = or.Managed.PreventDestroySet
		}
		if len(or.Managed.Provisioners) != 0 {
			r.Managed.Provisioners = or.Managed.Provisioners
		}
	}

	r.Config = MergeBodies(r.Config, or.Config)

	// We don't allow depends_on to be overridden because that is likely to
	// cause confusing misbehavior.
	if len(or.DependsOn) != 0 {
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  "Unsupported override",
			Detail:   "The depends_on argument may not be overridden.",
			Subject:  or.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
		})
	}

	return diags
}