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  }