github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/configs/module_merge.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 6 "github.com/iaas-resource-provision/iaas-rpc/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.SourceAddrRange = omc.SourceAddrRange 154 mc.SourceSet = omc.SourceSet 155 } 156 157 if omc.Count != nil { 158 mc.Count = omc.Count 159 } 160 161 if omc.ForEach != nil { 162 mc.ForEach = omc.ForEach 163 } 164 165 if len(omc.Version.Required) != 0 { 166 mc.Version = omc.Version 167 } 168 169 mc.Config = MergeBodies(mc.Config, omc.Config) 170 171 if len(omc.Providers) != 0 { 172 mc.Providers = omc.Providers 173 } 174 175 // We don't allow depends_on to be overridden because that is likely to 176 // cause confusing misbehavior. 177 if len(mc.DependsOn) != 0 { 178 diags = append(diags, &hcl.Diagnostic{ 179 Severity: hcl.DiagError, 180 Summary: "Unsupported override", 181 Detail: "The depends_on argument may not be overridden.", 182 Subject: mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have 183 }) 184 } 185 186 return diags 187 } 188 189 func (r *Resource) merge(or *Resource, rps map[string]*RequiredProvider) hcl.Diagnostics { 190 var diags hcl.Diagnostics 191 192 if r.Mode != or.Mode { 193 // This is always a programming error, since managed and data resources 194 // are kept in separate maps in the configuration structures. 195 panic(fmt.Errorf("can't merge %s into %s", or.Mode, r.Mode)) 196 } 197 198 if or.Count != nil { 199 r.Count = or.Count 200 } 201 if or.ForEach != nil { 202 r.ForEach = or.ForEach 203 } 204 205 if or.ProviderConfigRef != nil { 206 r.ProviderConfigRef = or.ProviderConfigRef 207 if existing, exists := rps[or.ProviderConfigRef.Name]; exists { 208 r.Provider = existing.Type 209 } else { 210 r.Provider = addrs.ImpliedProviderForUnqualifiedType(r.ProviderConfigRef.Name) 211 } 212 } 213 214 // Provider FQN is set by Terraform during Merge 215 216 if r.Mode == addrs.ManagedResourceMode { 217 // or.Managed is always non-nil for managed resource mode 218 219 if or.Managed.Connection != nil { 220 r.Managed.Connection = or.Managed.Connection 221 } 222 if or.Managed.CreateBeforeDestroySet { 223 r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy 224 r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet 225 } 226 if len(or.Managed.IgnoreChanges) != 0 { 227 r.Managed.IgnoreChanges = or.Managed.IgnoreChanges 228 } 229 if or.Managed.PreventDestroySet { 230 r.Managed.PreventDestroy = or.Managed.PreventDestroy 231 r.Managed.PreventDestroySet = or.Managed.PreventDestroySet 232 } 233 if len(or.Managed.Provisioners) != 0 { 234 r.Managed.Provisioners = or.Managed.Provisioners 235 } 236 } 237 238 r.Config = MergeBodies(r.Config, or.Config) 239 240 // We don't allow depends_on to be overridden because that is likely to 241 // cause confusing misbehavior. 242 if len(or.DependsOn) != 0 { 243 diags = append(diags, &hcl.Diagnostic{ 244 Severity: hcl.DiagError, 245 Summary: "Unsupported override", 246 Detail: "The depends_on argument may not be overridden.", 247 Subject: or.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have 248 }) 249 } 250 251 return diags 252 }