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  }