github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/configs/module_merge.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/internal/addrs" 7 8 "github.com/hashicorp/hcl/v2" 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 (v *Variable) merge(ov *Variable) hcl.Diagnostics { 39 var diags hcl.Diagnostics 40 41 if ov.DescriptionSet { 42 v.Description = ov.Description 43 v.DescriptionSet = ov.DescriptionSet 44 } 45 if ov.SensitiveSet { 46 v.Sensitive = ov.Sensitive 47 v.SensitiveSet = ov.SensitiveSet 48 } 49 if ov.Default != cty.NilVal { 50 v.Default = ov.Default 51 } 52 if ov.Type != cty.NilType { 53 v.Type = ov.Type 54 } 55 if ov.ParsingMode != 0 { 56 v.ParsingMode = ov.ParsingMode 57 } 58 59 // If the override file overrode type without default or vice-versa then 60 // it may have created an invalid situation, which we'll catch now by 61 // attempting to re-convert the value. 62 // 63 // Note that here we may be re-converting an already-converted base value 64 // from the base config. This will be a no-op if the type was not changed, 65 // but in particular might be user-observable in the edge case where the 66 // literal value in config could've been converted to the overridden type 67 // constraint but the converted value cannot. In practice, this situation 68 // should be rare since most of our conversions are interchangable. 69 if v.Default != cty.NilVal { 70 val, err := convert.Convert(v.Default, v.Type) 71 if err != nil { 72 // What exactly we'll say in the error message here depends on whether 73 // it was Default or Type that was overridden here. 74 switch { 75 case ov.Type != cty.NilType && ov.Default == cty.NilVal: 76 // If only the type was overridden 77 diags = append(diags, &hcl.Diagnostic{ 78 Severity: hcl.DiagError, 79 Summary: "Invalid default value for variable", 80 Detail: fmt.Sprintf("Overriding this variable's type constraint has made its default value invalid: %s.", err), 81 Subject: &ov.DeclRange, 82 }) 83 case ov.Type == cty.NilType && ov.Default != cty.NilVal: 84 // Only the default was overridden 85 diags = append(diags, &hcl.Diagnostic{ 86 Severity: hcl.DiagError, 87 Summary: "Invalid default value for variable", 88 Detail: fmt.Sprintf("The overridden default value for this variable is not compatible with the variable's type constraint: %s.", err), 89 Subject: &ov.DeclRange, 90 }) 91 default: 92 diags = append(diags, &hcl.Diagnostic{ 93 Severity: hcl.DiagError, 94 Summary: "Invalid default value for variable", 95 Detail: fmt.Sprintf("This variable's default value is not compatible with its type constraint: %s.", err), 96 Subject: &ov.DeclRange, 97 }) 98 } 99 } else { 100 v.Default = val 101 } 102 } 103 104 return diags 105 } 106 107 func (l *Local) merge(ol *Local) hcl.Diagnostics { 108 var diags hcl.Diagnostics 109 110 // Since a local is just a single expression in configuration, the 111 // override definition entirely replaces the base definition, including 112 // the source range so that we'll send the user to the right place if 113 // there is an error. 114 l.Expr = ol.Expr 115 l.DeclRange = ol.DeclRange 116 117 return diags 118 } 119 120 func (o *Output) merge(oo *Output) hcl.Diagnostics { 121 var diags hcl.Diagnostics 122 123 if oo.Description != "" { 124 o.Description = oo.Description 125 } 126 if oo.Expr != nil { 127 o.Expr = oo.Expr 128 } 129 if oo.SensitiveSet { 130 o.Sensitive = oo.Sensitive 131 o.SensitiveSet = oo.SensitiveSet 132 } 133 134 // We don't allow depends_on to be overridden because that is likely to 135 // cause confusing misbehavior. 136 if len(oo.DependsOn) != 0 { 137 diags = append(diags, &hcl.Diagnostic{ 138 Severity: hcl.DiagError, 139 Summary: "Unsupported override", 140 Detail: "The depends_on argument may not be overridden.", 141 Subject: oo.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have 142 }) 143 } 144 145 return diags 146 } 147 148 func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics { 149 var diags hcl.Diagnostics 150 151 if omc.SourceSet { 152 mc.SourceAddr = omc.SourceAddr 153 mc.SourceAddrRaw = omc.SourceAddrRaw 154 mc.SourceAddrRange = omc.SourceAddrRange 155 mc.SourceSet = omc.SourceSet 156 } 157 158 if omc.Count != nil { 159 mc.Count = omc.Count 160 } 161 162 if omc.ForEach != nil { 163 mc.ForEach = omc.ForEach 164 } 165 166 if len(omc.Version.Required) != 0 { 167 mc.Version = omc.Version 168 } 169 170 mc.Config = MergeBodies(mc.Config, omc.Config) 171 172 if len(omc.Providers) != 0 { 173 mc.Providers = omc.Providers 174 } 175 176 // We don't allow depends_on to be overridden because that is likely to 177 // cause confusing misbehavior. 178 if len(mc.DependsOn) != 0 { 179 diags = append(diags, &hcl.Diagnostic{ 180 Severity: hcl.DiagError, 181 Summary: "Unsupported override", 182 Detail: "The depends_on argument may not be overridden.", 183 Subject: mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have 184 }) 185 } 186 187 return diags 188 } 189 190 func (r *Resource) merge(or *Resource, rps map[string]*RequiredProvider) hcl.Diagnostics { 191 var diags hcl.Diagnostics 192 193 if r.Mode != or.Mode { 194 // This is always a programming error, since managed and data resources 195 // are kept in separate maps in the configuration structures. 196 panic(fmt.Errorf("can't merge %s into %s", or.Mode, r.Mode)) 197 } 198 199 if or.Count != nil { 200 r.Count = or.Count 201 } 202 if or.ForEach != nil { 203 r.ForEach = or.ForEach 204 } 205 206 if or.ProviderConfigRef != nil { 207 r.ProviderConfigRef = or.ProviderConfigRef 208 if existing, exists := rps[or.ProviderConfigRef.Name]; exists { 209 r.Provider = existing.Type 210 } else { 211 r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigRef.Name) 212 } 213 } 214 215 // Provider FQN is set by Terraform during Merge 216 217 if r.Mode == addrs.ManagedResourceMode { 218 // or.Managed is always non-nil for managed resource mode 219 220 if or.Managed.Connection != nil { 221 r.Managed.Connection = or.Managed.Connection 222 } 223 if or.Managed.CreateBeforeDestroySet { 224 r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy 225 r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet 226 } 227 if len(or.Managed.IgnoreChanges) != 0 { 228 r.Managed.IgnoreChanges = or.Managed.IgnoreChanges 229 } 230 if or.Managed.PreventDestroySet { 231 r.Managed.PreventDestroy = or.Managed.PreventDestroy 232 r.Managed.PreventDestroySet = or.Managed.PreventDestroySet 233 } 234 if len(or.Managed.Provisioners) != 0 { 235 r.Managed.Provisioners = or.Managed.Provisioners 236 } 237 } 238 239 r.Config = MergeBodies(r.Config, or.Config) 240 241 // We don't allow depends_on to be overridden because that is likely to 242 // cause confusing misbehavior. 243 if len(or.DependsOn) != 0 { 244 diags = append(diags, &hcl.Diagnostic{ 245 Severity: hcl.DiagError, 246 Summary: "Unsupported override", 247 Detail: "The depends_on argument may not be overridden.", 248 Subject: or.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have 249 }) 250 } 251 252 return diags 253 }