github.com/jrasell/terraform@v0.6.17-0.20160523115548-2652f5232949/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 }