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