github.com/tam7t/terraform@v0.7.0-rc2.0.20160705125922-be2469a05c5e/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  	// NOTE: Everything below is pretty gross. Due to the lack of generics
    36  	// in Go, there is some hoop-jumping involved to make this merging a
    37  	// little more test-friendly and less repetitive. Ironically, making it
    38  	// less repetitive involves being a little repetitive, but I prefer to
    39  	// be repetitive with things that are less error prone than things that
    40  	// are more error prone (more logic). Type conversions to an interface
    41  	// are pretty low-error.
    42  
    43  	var m1, m2, mresult []merger
    44  
    45  	// Modules
    46  	m1 = make([]merger, 0, len(c1.Modules))
    47  	m2 = make([]merger, 0, len(c2.Modules))
    48  	for _, v := range c1.Modules {
    49  		m1 = append(m1, v)
    50  	}
    51  	for _, v := range c2.Modules {
    52  		m2 = append(m2, v)
    53  	}
    54  	mresult = mergeSlice(m1, m2)
    55  	if len(mresult) > 0 {
    56  		c.Modules = make([]*Module, len(mresult))
    57  		for i, v := range mresult {
    58  			c.Modules[i] = v.(*Module)
    59  		}
    60  	}
    61  
    62  	// Outputs
    63  	m1 = make([]merger, 0, len(c1.Outputs))
    64  	m2 = make([]merger, 0, len(c2.Outputs))
    65  	for _, v := range c1.Outputs {
    66  		m1 = append(m1, v)
    67  	}
    68  	for _, v := range c2.Outputs {
    69  		m2 = append(m2, v)
    70  	}
    71  	mresult = mergeSlice(m1, m2)
    72  	if len(mresult) > 0 {
    73  		c.Outputs = make([]*Output, len(mresult))
    74  		for i, v := range mresult {
    75  			c.Outputs[i] = v.(*Output)
    76  		}
    77  	}
    78  
    79  	// Provider Configs
    80  	m1 = make([]merger, 0, len(c1.ProviderConfigs))
    81  	m2 = make([]merger, 0, len(c2.ProviderConfigs))
    82  	for _, v := range c1.ProviderConfigs {
    83  		m1 = append(m1, v)
    84  	}
    85  	for _, v := range c2.ProviderConfigs {
    86  		m2 = append(m2, v)
    87  	}
    88  	mresult = mergeSlice(m1, m2)
    89  	if len(mresult) > 0 {
    90  		c.ProviderConfigs = make([]*ProviderConfig, len(mresult))
    91  		for i, v := range mresult {
    92  			c.ProviderConfigs[i] = v.(*ProviderConfig)
    93  		}
    94  	}
    95  
    96  	// Resources
    97  	m1 = make([]merger, 0, len(c1.Resources))
    98  	m2 = make([]merger, 0, len(c2.Resources))
    99  	for _, v := range c1.Resources {
   100  		m1 = append(m1, v)
   101  	}
   102  	for _, v := range c2.Resources {
   103  		m2 = append(m2, v)
   104  	}
   105  	mresult = mergeSlice(m1, m2)
   106  	if len(mresult) > 0 {
   107  		c.Resources = make([]*Resource, len(mresult))
   108  		for i, v := range mresult {
   109  			c.Resources[i] = v.(*Resource)
   110  		}
   111  	}
   112  
   113  	// Variables
   114  	m1 = make([]merger, 0, len(c1.Variables))
   115  	m2 = make([]merger, 0, len(c2.Variables))
   116  	for _, v := range c1.Variables {
   117  		m1 = append(m1, v)
   118  	}
   119  	for _, v := range c2.Variables {
   120  		m2 = append(m2, v)
   121  	}
   122  	mresult = mergeSlice(m1, m2)
   123  	if len(mresult) > 0 {
   124  		c.Variables = make([]*Variable, len(mresult))
   125  		for i, v := range mresult {
   126  			c.Variables[i] = v.(*Variable)
   127  		}
   128  	}
   129  
   130  	return c, nil
   131  }
   132  
   133  // merger is an interface that must be implemented by types that are
   134  // merge-able. This simplifies the implementation of Merge for the various
   135  // components of a Config.
   136  type merger interface {
   137  	mergerName() string
   138  	mergerMerge(merger) merger
   139  }
   140  
   141  // mergeSlice merges a slice of mergers.
   142  func mergeSlice(m1, m2 []merger) []merger {
   143  	r := make([]merger, len(m1), len(m1)+len(m2))
   144  	copy(r, m1)
   145  
   146  	m := map[string]struct{}{}
   147  	for _, v2 := range m2 {
   148  		// If we already saw it, just append it because its a
   149  		// duplicate and invalid...
   150  		name := v2.mergerName()
   151  		if _, ok := m[name]; ok {
   152  			r = append(r, v2)
   153  			continue
   154  		}
   155  		m[name] = struct{}{}
   156  
   157  		// Find an original to override
   158  		var original merger
   159  		originalIndex := -1
   160  		for i, v := range m1 {
   161  			if v.mergerName() == name {
   162  				originalIndex = i
   163  				original = v
   164  				break
   165  			}
   166  		}
   167  
   168  		var v merger
   169  		if original == nil {
   170  			v = v2
   171  		} else {
   172  			v = original.mergerMerge(v2)
   173  		}
   174  
   175  		if originalIndex == -1 {
   176  			r = append(r, v)
   177  		} else {
   178  			r[originalIndex] = v
   179  		}
   180  	}
   181  
   182  	return r
   183  }