github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 }