github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/compose/loader/merge.go (about) 1 package loader 2 3 import ( 4 "reflect" 5 "sort" 6 7 "github.com/docker/cli/cli/compose/types" 8 "github.com/imdario/mergo" 9 "github.com/pkg/errors" 10 ) 11 12 type specials struct { 13 m map[reflect.Type]func(dst, src reflect.Value) error 14 } 15 16 func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error { 17 if fn, ok := s.m[t]; ok { 18 return fn 19 } 20 return nil 21 } 22 23 func merge(configs []*types.Config) (*types.Config, error) { 24 base := configs[0] 25 for _, override := range configs[1:] { 26 var err error 27 base.Services, err = mergeServices(base.Services, override.Services) 28 if err != nil { 29 return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename) 30 } 31 base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes) 32 if err != nil { 33 return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename) 34 } 35 base.Networks, err = mergeNetworks(base.Networks, override.Networks) 36 if err != nil { 37 return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename) 38 } 39 base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets) 40 if err != nil { 41 return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename) 42 } 43 base.Configs, err = mergeConfigs(base.Configs, override.Configs) 44 if err != nil { 45 return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename) 46 } 47 } 48 return base, nil 49 } 50 51 func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) { 52 baseServices := mapByName(base) 53 overrideServices := mapByName(override) 54 specials := &specials{ 55 m: map[reflect.Type]func(dst, src reflect.Value) error{ 56 reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig), 57 reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice), 58 reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice), 59 reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice), 60 }, 61 } 62 for name, overrideService := range overrideServices { 63 if baseService, ok := baseServices[name]; ok { 64 if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil { 65 return base, errors.Wrapf(err, "cannot merge service %s", name) 66 } 67 baseServices[name] = baseService 68 continue 69 } 70 baseServices[name] = overrideService 71 } 72 services := []types.ServiceConfig{} 73 for _, baseService := range baseServices { 74 services = append(services, baseService) 75 } 76 sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) 77 return services, nil 78 } 79 80 func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) { 81 secrets, ok := s.([]types.ServiceSecretConfig) 82 if !ok { 83 return nil, errors.Errorf("not a serviceSecretConfig: %v", s) 84 } 85 m := map[interface{}]interface{}{} 86 for _, secret := range secrets { 87 m[secret.Source] = secret 88 } 89 return m, nil 90 } 91 92 func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) { 93 secrets, ok := s.([]types.ServiceConfigObjConfig) 94 if !ok { 95 return nil, errors.Errorf("not a serviceSecretConfig: %v", s) 96 } 97 m := map[interface{}]interface{}{} 98 for _, secret := range secrets { 99 m[secret.Source] = secret 100 } 101 return m, nil 102 } 103 104 func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) { 105 ports, ok := s.([]types.ServicePortConfig) 106 if !ok { 107 return nil, errors.Errorf("not a servicePortConfig slice: %v", s) 108 } 109 m := map[interface{}]interface{}{} 110 for _, p := range ports { 111 m[p.Published] = p 112 } 113 return m, nil 114 } 115 116 func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { 117 s := []types.ServiceSecretConfig{} 118 for _, v := range m { 119 s = append(s, v.(types.ServiceSecretConfig)) 120 } 121 sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) 122 dst.Set(reflect.ValueOf(s)) 123 return nil 124 } 125 126 func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { 127 s := []types.ServiceConfigObjConfig{} 128 for _, v := range m { 129 s = append(s, v.(types.ServiceConfigObjConfig)) 130 } 131 sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) 132 dst.Set(reflect.ValueOf(s)) 133 return nil 134 } 135 136 func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { 137 s := []types.ServicePortConfig{} 138 for _, v := range m { 139 s = append(s, v.(types.ServicePortConfig)) 140 } 141 sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published }) 142 dst.Set(reflect.ValueOf(s)) 143 return nil 144 } 145 146 type tomapFn func(s interface{}) (map[interface{}]interface{}, error) 147 type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error 148 149 func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error { 150 return func(dst, src reflect.Value) error { 151 if src.IsNil() { 152 return nil 153 } 154 if dst.IsNil() { 155 dst.Set(src) 156 return nil 157 } 158 return mergeFn(dst, src) 159 } 160 } 161 162 func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error { 163 return func(dst, src reflect.Value) error { 164 dstMap, err := sliceToMap(tomap, dst) 165 if err != nil { 166 return err 167 } 168 srcMap, err := sliceToMap(tomap, src) 169 if err != nil { 170 return err 171 } 172 if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil { 173 return err 174 } 175 return writeValue(dst, dstMap) 176 } 177 } 178 179 func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) { 180 // check if valid 181 if !v.IsValid() { 182 return nil, errors.Errorf("invalid value : %+v", v) 183 } 184 return tomap(v.Interface()) 185 } 186 187 func mergeLoggingConfig(dst, src reflect.Value) error { 188 // Same driver, merging options 189 if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) || 190 getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" { 191 if getLoggingDriver(dst.Elem()) == "" { 192 dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem())) 193 } 194 dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string) 195 srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string) 196 return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride) 197 } 198 // Different driver, override with src 199 dst.Set(src) 200 return nil 201 } 202 203 func getLoggingDriver(v reflect.Value) string { 204 return v.FieldByName("Driver").String() 205 } 206 207 func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig { 208 m := map[string]types.ServiceConfig{} 209 for _, service := range services { 210 m[service.Name] = service 211 } 212 return m 213 } 214 215 func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) { 216 err := mergo.Map(&base, &override, mergo.WithOverride) 217 return base, err 218 } 219 220 func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) { 221 err := mergo.Map(&base, &override, mergo.WithOverride) 222 return base, err 223 } 224 225 func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) { 226 err := mergo.Map(&base, &override, mergo.WithOverride) 227 return base, err 228 } 229 230 func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) { 231 err := mergo.Map(&base, &override, mergo.WithOverride) 232 return base, err 233 }