github.com/gruntwork-io/terraform@v0.11.12-beta1/configs/module_merge.go (about)

     1  package configs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/hcl2/hcl"
     7  	"github.com/zclconf/go-cty/cty"
     8  	"github.com/zclconf/go-cty/cty/convert"
     9  )
    10  
    11  // The methods in this file are used by Module.mergeFile to apply overrides
    12  // to our different configuration elements. These methods all follow the
    13  // pattern of mutating the receiver to incorporate settings from the parameter,
    14  // returning error diagnostics if any aspect of the parameter cannot be merged
    15  // into the receiver for some reason.
    16  //
    17  // User expectation is that anything _explicitly_ set in the given object
    18  // should take precedence over the corresponding settings in the receiver,
    19  // but that anything omitted in the given object should be left unchanged.
    20  // In some cases it may be reasonable to do a "deep merge" of certain nested
    21  // features, if it is possible to unambiguously correlate the nested elements
    22  // and their behaviors are orthogonal to each other.
    23  
    24  func (p *Provider) merge(op *Provider) hcl.Diagnostics {
    25  	var diags hcl.Diagnostics
    26  
    27  	if op.Version.Required != nil {
    28  		p.Version = op.Version
    29  	}
    30  
    31  	p.Config = mergeBodies(p.Config, op.Config)
    32  
    33  	return diags
    34  }
    35  
    36  func mergeProviderVersionConstraints(recv map[string][]VersionConstraint, ovrd []*ProviderRequirement) {
    37  	// Any provider name that's mentioned in the override gets nilled out in
    38  	// our map so that we'll rebuild it below. Any provider not mentioned is
    39  	// left unchanged.
    40  	for _, reqd := range ovrd {
    41  		delete(recv, reqd.Name)
    42  	}
    43  	for _, reqd := range ovrd {
    44  		recv[reqd.Name] = append(recv[reqd.Name], reqd.Requirement)
    45  	}
    46  }
    47  
    48  func (v *Variable) merge(ov *Variable) hcl.Diagnostics {
    49  	var diags hcl.Diagnostics
    50  
    51  	if ov.DescriptionSet {
    52  		v.Description = ov.Description
    53  		v.DescriptionSet = ov.DescriptionSet
    54  	}
    55  	if ov.Default != cty.NilVal {
    56  		v.Default = ov.Default
    57  	}
    58  	if ov.Type != cty.NilType {
    59  		v.Type = ov.Type
    60  	}
    61  	if ov.ParsingMode != 0 {
    62  		v.ParsingMode = ov.ParsingMode
    63  	}
    64  
    65  	// If the override file overrode type without default or vice-versa then
    66  	// it may have created an invalid situation, which we'll catch now by
    67  	// attempting to re-convert the value.
    68  	//
    69  	// Note that here we may be re-converting an already-converted base value
    70  	// from the base config. This will be a no-op if the type was not changed,
    71  	// but in particular might be user-observable in the edge case where the
    72  	// literal value in config could've been converted to the overridden type
    73  	// constraint but the converted value cannot. In practice, this situation
    74  	// should be rare since most of our conversions are interchangable.
    75  	if v.Default != cty.NilVal {
    76  		val, err := convert.Convert(v.Default, v.Type)
    77  		if err != nil {
    78  			// What exactly we'll say in the error message here depends on whether
    79  			// it was Default or Type that was overridden here.
    80  			switch {
    81  			case ov.Type != cty.NilType && ov.Default == cty.NilVal:
    82  				// If only the type was overridden
    83  				diags = append(diags, &hcl.Diagnostic{
    84  					Severity: hcl.DiagError,
    85  					Summary:  "Invalid default value for variable",
    86  					Detail:   fmt.Sprintf("Overriding this variable's type constraint has made its default value invalid: %s.", err),
    87  					Subject:  &ov.DeclRange,
    88  				})
    89  			case ov.Type == cty.NilType && ov.Default != cty.NilVal:
    90  				// Only the default was overridden
    91  				diags = append(diags, &hcl.Diagnostic{
    92  					Severity: hcl.DiagError,
    93  					Summary:  "Invalid default value for variable",
    94  					Detail:   fmt.Sprintf("The overridden default value for this variable is not compatible with the variable's type constraint: %s.", err),
    95  					Subject:  &ov.DeclRange,
    96  				})
    97  			default:
    98  				diags = append(diags, &hcl.Diagnostic{
    99  					Severity: hcl.DiagError,
   100  					Summary:  "Invalid default value for variable",
   101  					Detail:   fmt.Sprintf("This variable's default value is not compatible with its type constraint: %s.", err),
   102  					Subject:  &ov.DeclRange,
   103  				})
   104  			}
   105  		} else {
   106  			v.Default = val
   107  		}
   108  	}
   109  
   110  	return diags
   111  }
   112  
   113  func (l *Local) merge(ol *Local) hcl.Diagnostics {
   114  	var diags hcl.Diagnostics
   115  
   116  	// Since a local is just a single expression in configuration, the
   117  	// override definition entirely replaces the base definition, including
   118  	// the source range so that we'll send the user to the right place if
   119  	// there is an error.
   120  	l.Expr = ol.Expr
   121  	l.DeclRange = ol.DeclRange
   122  
   123  	return diags
   124  }
   125  
   126  func (o *Output) merge(oo *Output) hcl.Diagnostics {
   127  	var diags hcl.Diagnostics
   128  
   129  	if oo.Description != "" {
   130  		o.Description = oo.Description
   131  	}
   132  	if oo.Expr != nil {
   133  		o.Expr = oo.Expr
   134  	}
   135  	if oo.SensitiveSet {
   136  		o.Sensitive = oo.Sensitive
   137  		o.SensitiveSet = oo.SensitiveSet
   138  	}
   139  
   140  	// We don't allow depends_on to be overridden because that is likely to
   141  	// cause confusing misbehavior.
   142  	if len(oo.DependsOn) != 0 {
   143  		diags = append(diags, &hcl.Diagnostic{
   144  			Severity: hcl.DiagError,
   145  			Summary:  "Unsupported override",
   146  			Detail:   "The depends_on argument may not be overridden.",
   147  			Subject:  oo.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
   148  		})
   149  	}
   150  
   151  	return diags
   152  }
   153  
   154  func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics {
   155  	var diags hcl.Diagnostics
   156  
   157  	if omc.SourceSet {
   158  		mc.SourceAddr = omc.SourceAddr
   159  		mc.SourceAddrRange = omc.SourceAddrRange
   160  		mc.SourceSet = omc.SourceSet
   161  	}
   162  
   163  	if omc.Count != nil {
   164  		mc.Count = omc.Count
   165  	}
   166  
   167  	if omc.ForEach != nil {
   168  		mc.ForEach = omc.ForEach
   169  	}
   170  
   171  	if len(omc.Version.Required) != 0 {
   172  		mc.Version = omc.Version
   173  	}
   174  
   175  	mc.Config = mergeBodies(mc.Config, omc.Config)
   176  
   177  	// We don't allow depends_on to be overridden because that is likely to
   178  	// cause confusing misbehavior.
   179  	if len(mc.DependsOn) != 0 {
   180  		diags = append(diags, &hcl.Diagnostic{
   181  			Severity: hcl.DiagError,
   182  			Summary:  "Unsupported override",
   183  			Detail:   "The depends_on argument may not be overridden.",
   184  			Subject:  mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
   185  		})
   186  	}
   187  
   188  	return diags
   189  }
   190  
   191  func (r *ManagedResource) merge(or *ManagedResource) hcl.Diagnostics {
   192  	var diags hcl.Diagnostics
   193  
   194  	if or.Connection != nil {
   195  		r.Connection = or.Connection
   196  	}
   197  	if or.Count != nil {
   198  		r.Count = or.Count
   199  	}
   200  	if or.CreateBeforeDestroySet {
   201  		r.CreateBeforeDestroy = or.CreateBeforeDestroy
   202  		r.CreateBeforeDestroySet = or.CreateBeforeDestroySet
   203  	}
   204  	if or.ForEach != nil {
   205  		r.ForEach = or.ForEach
   206  	}
   207  	if len(or.IgnoreChanges) != 0 {
   208  		r.IgnoreChanges = or.IgnoreChanges
   209  	}
   210  	if or.PreventDestroySet {
   211  		r.PreventDestroy = or.PreventDestroy
   212  		r.PreventDestroySet = or.PreventDestroySet
   213  	}
   214  	if or.ProviderConfigRef != nil {
   215  		r.ProviderConfigRef = or.ProviderConfigRef
   216  	}
   217  	if len(or.Provisioners) != 0 {
   218  		r.Provisioners = or.Provisioners
   219  	}
   220  
   221  	r.Config = mergeBodies(r.Config, or.Config)
   222  
   223  	// We don't allow depends_on to be overridden because that is likely to
   224  	// cause confusing misbehavior.
   225  	if len(r.DependsOn) != 0 {
   226  		diags = append(diags, &hcl.Diagnostic{
   227  			Severity: hcl.DiagError,
   228  			Summary:  "Unsupported override",
   229  			Detail:   "The depends_on argument may not be overridden.",
   230  			Subject:  r.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
   231  		})
   232  	}
   233  
   234  	return diags
   235  }
   236  
   237  func (r *DataResource) merge(or *DataResource) hcl.Diagnostics {
   238  	var diags hcl.Diagnostics
   239  
   240  	if or.Count != nil {
   241  		r.Count = or.Count
   242  	}
   243  	if or.ForEach != nil {
   244  		r.ForEach = or.ForEach
   245  	}
   246  	if or.ProviderConfigRef != nil {
   247  		r.ProviderConfigRef = or.ProviderConfigRef
   248  	}
   249  
   250  	r.Config = mergeBodies(r.Config, or.Config)
   251  
   252  	// We don't allow depends_on to be overridden because that is likely to
   253  	// cause confusing misbehavior.
   254  	if len(r.DependsOn) != 0 {
   255  		diags = append(diags, &hcl.Diagnostic{
   256  			Severity: hcl.DiagError,
   257  			Summary:  "Unsupported override",
   258  			Detail:   "The depends_on argument may not be overridden.",
   259  			Subject:  r.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have
   260  		})
   261  	}
   262  
   263  	return diags
   264  }