github.com/nirarg/terraform@v0.11.12-beta1/config/merge.go (about)

     1  package config
     2  
     3  // Merge merges two configurations into a single configuration.
     4  //
     5  // Merge allows for the two configurations to have duplicate resources,
     6  // because the resources will be merged. This differs from a single
     7  // Config which must only have unique resources.
     8  func Merge(c1, c2 *Config) (*Config, error) {
     9  	c := new(Config)
    10  
    11  	// Merge unknown keys
    12  	unknowns := make(map[string]struct{})
    13  	for _, k := range c1.unknownKeys {
    14  		_, present := unknowns[k]
    15  		if !present {
    16  			unknowns[k] = struct{}{}
    17  			c.unknownKeys = append(c.unknownKeys, k)
    18  		}
    19  	}
    20  	for _, k := range c2.unknownKeys {
    21  		_, present := unknowns[k]
    22  		if !present {
    23  			unknowns[k] = struct{}{}
    24  			c.unknownKeys = append(c.unknownKeys, k)
    25  		}
    26  	}
    27  
    28  	// Merge Atlas configuration. This is a dumb one overrides the other
    29  	// sort of merge.
    30  	c.Atlas = c1.Atlas
    31  	if c2.Atlas != nil {
    32  		c.Atlas = c2.Atlas
    33  	}
    34  
    35  	// Merge the Terraform configuration
    36  	if c1.Terraform != nil {
    37  		c.Terraform = c1.Terraform
    38  		if c2.Terraform != nil {
    39  			c.Terraform.Merge(c2.Terraform)
    40  		}
    41  	} else {
    42  		c.Terraform = c2.Terraform
    43  	}
    44  
    45  	// NOTE: Everything below is pretty gross. Due to the lack of generics
    46  	// in Go, there is some hoop-jumping involved to make this merging a
    47  	// little more test-friendly and less repetitive. Ironically, making it
    48  	// less repetitive involves being a little repetitive, but I prefer to
    49  	// be repetitive with things that are less error prone than things that
    50  	// are more error prone (more logic). Type conversions to an interface
    51  	// are pretty low-error.
    52  
    53  	var m1, m2, mresult []merger
    54  
    55  	// Modules
    56  	m1 = make([]merger, 0, len(c1.Modules))
    57  	m2 = make([]merger, 0, len(c2.Modules))
    58  	for _, v := range c1.Modules {
    59  		m1 = append(m1, v)
    60  	}
    61  	for _, v := range c2.Modules {
    62  		m2 = append(m2, v)
    63  	}
    64  	mresult = mergeSlice(m1, m2)
    65  	if len(mresult) > 0 {
    66  		c.Modules = make([]*Module, len(mresult))
    67  		for i, v := range mresult {
    68  			c.Modules[i] = v.(*Module)
    69  		}
    70  	}
    71  
    72  	// Outputs
    73  	m1 = make([]merger, 0, len(c1.Outputs))
    74  	m2 = make([]merger, 0, len(c2.Outputs))
    75  	for _, v := range c1.Outputs {
    76  		m1 = append(m1, v)
    77  	}
    78  	for _, v := range c2.Outputs {
    79  		m2 = append(m2, v)
    80  	}
    81  	mresult = mergeSlice(m1, m2)
    82  	if len(mresult) > 0 {
    83  		c.Outputs = make([]*Output, len(mresult))
    84  		for i, v := range mresult {
    85  			c.Outputs[i] = v.(*Output)
    86  		}
    87  	}
    88  
    89  	// Provider Configs
    90  	m1 = make([]merger, 0, len(c1.ProviderConfigs))
    91  	m2 = make([]merger, 0, len(c2.ProviderConfigs))
    92  	for _, v := range c1.ProviderConfigs {
    93  		m1 = append(m1, v)
    94  	}
    95  	for _, v := range c2.ProviderConfigs {
    96  		m2 = append(m2, v)
    97  	}
    98  	mresult = mergeSlice(m1, m2)
    99  	if len(mresult) > 0 {
   100  		c.ProviderConfigs = make([]*ProviderConfig, len(mresult))
   101  		for i, v := range mresult {
   102  			c.ProviderConfigs[i] = v.(*ProviderConfig)
   103  		}
   104  	}
   105  
   106  	// Resources
   107  	m1 = make([]merger, 0, len(c1.Resources))
   108  	m2 = make([]merger, 0, len(c2.Resources))
   109  	for _, v := range c1.Resources {
   110  		m1 = append(m1, v)
   111  	}
   112  	for _, v := range c2.Resources {
   113  		m2 = append(m2, v)
   114  	}
   115  	mresult = mergeSlice(m1, m2)
   116  	if len(mresult) > 0 {
   117  		c.Resources = make([]*Resource, len(mresult))
   118  		for i, v := range mresult {
   119  			c.Resources[i] = v.(*Resource)
   120  		}
   121  	}
   122  
   123  	// Variables
   124  	m1 = make([]merger, 0, len(c1.Variables))
   125  	m2 = make([]merger, 0, len(c2.Variables))
   126  	for _, v := range c1.Variables {
   127  		m1 = append(m1, v)
   128  	}
   129  	for _, v := range c2.Variables {
   130  		m2 = append(m2, v)
   131  	}
   132  	mresult = mergeSlice(m1, m2)
   133  	if len(mresult) > 0 {
   134  		c.Variables = make([]*Variable, len(mresult))
   135  		for i, v := range mresult {
   136  			c.Variables[i] = v.(*Variable)
   137  		}
   138  	}
   139  
   140  	// Local Values
   141  	// These are simpler than the other config elements because they are just
   142  	// flat values and so no deep merging is required.
   143  	if localsCount := len(c1.Locals) + len(c2.Locals); localsCount != 0 {
   144  		// Explicit length check above because we want c.Locals to remain
   145  		// nil if the result would be empty.
   146  		c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals))
   147  		c.Locals = append(c.Locals, c1.Locals...)
   148  		c.Locals = append(c.Locals, c2.Locals...)
   149  	}
   150  
   151  	return c, nil
   152  }
   153  
   154  // merger is an interface that must be implemented by types that are
   155  // merge-able. This simplifies the implementation of Merge for the various
   156  // components of a Config.
   157  type merger interface {
   158  	mergerName() string
   159  	mergerMerge(merger) merger
   160  }
   161  
   162  // mergeSlice merges a slice of mergers.
   163  func mergeSlice(m1, m2 []merger) []merger {
   164  	r := make([]merger, len(m1), len(m1)+len(m2))
   165  	copy(r, m1)
   166  
   167  	m := map[string]struct{}{}
   168  	for _, v2 := range m2 {
   169  		// If we already saw it, just append it because its a
   170  		// duplicate and invalid...
   171  		name := v2.mergerName()
   172  		if _, ok := m[name]; ok {
   173  			r = append(r, v2)
   174  			continue
   175  		}
   176  		m[name] = struct{}{}
   177  
   178  		// Find an original to override
   179  		var original merger
   180  		originalIndex := -1
   181  		for i, v := range m1 {
   182  			if v.mergerName() == name {
   183  				originalIndex = i
   184  				original = v
   185  				break
   186  			}
   187  		}
   188  
   189  		var v merger
   190  		if original == nil {
   191  			v = v2
   192  		} else {
   193  			v = original.mergerMerge(v2)
   194  		}
   195  
   196  		if originalIndex == -1 {
   197  			r = append(r, v)
   198  		} else {
   199  			r[originalIndex] = v
   200  		}
   201  	}
   202  
   203  	return r
   204  }