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