github.com/jgadling/terraform@v0.3.8-0.20150227214559-abd68c2c87bc/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 }