github.com/magodo/terraform@v0.11.12-beta1/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 // Local Values 141 // These are simpler than the other config elements because they are just 142 // flat values and so no deep merging is required. 143 if localsCount := len(c1.Locals) + len(c2.Locals); localsCount != 0 { 144 // Explicit length check above because we want c.Locals to remain 145 // nil if the result would be empty. 146 c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals)) 147 c.Locals = append(c.Locals, c1.Locals...) 148 c.Locals = append(c.Locals, c2.Locals...) 149 } 150 151 return c, nil 152 } 153 154 // merger is an interface that must be implemented by types that are 155 // merge-able. This simplifies the implementation of Merge for the various 156 // components of a Config. 157 type merger interface { 158 mergerName() string 159 mergerMerge(merger) merger 160 } 161 162 // mergeSlice merges a slice of mergers. 163 func mergeSlice(m1, m2 []merger) []merger { 164 r := make([]merger, len(m1), len(m1)+len(m2)) 165 copy(r, m1) 166 167 m := map[string]struct{}{} 168 for _, v2 := range m2 { 169 // If we already saw it, just append it because its a 170 // duplicate and invalid... 171 name := v2.mergerName() 172 if _, ok := m[name]; ok { 173 r = append(r, v2) 174 continue 175 } 176 m[name] = struct{}{} 177 178 // Find an original to override 179 var original merger 180 originalIndex := -1 181 for i, v := range m1 { 182 if v.mergerName() == name { 183 originalIndex = i 184 original = v 185 break 186 } 187 } 188 189 var v merger 190 if original == nil { 191 v = v2 192 } else { 193 v = original.mergerMerge(v2) 194 } 195 196 if originalIndex == -1 { 197 r = append(r, v) 198 } else { 199 r[originalIndex] = v 200 } 201 } 202 203 return r 204 }