github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/configs/module_merge.go (about)

     1  package configs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/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 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  }