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