github.com/ali-iotechsys/cli@v20.10.0+incompatible/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  			reflect.TypeOf(&types.UlimitsConfig{}):           mergeUlimitsConfig,
    61  			reflect.TypeOf(&types.ServiceNetworkConfig{}):    mergeServiceNetworkConfig,
    62  		},
    63  	}
    64  	for name, overrideService := range overrideServices {
    65  		overrideService := overrideService
    66  		if baseService, ok := baseServices[name]; ok {
    67  			if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
    68  				return base, errors.Wrapf(err, "cannot merge service %s", name)
    69  			}
    70  			baseServices[name] = baseService
    71  			continue
    72  		}
    73  		baseServices[name] = overrideService
    74  	}
    75  	services := []types.ServiceConfig{}
    76  	for _, baseService := range baseServices {
    77  		services = append(services, baseService)
    78  	}
    79  	sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
    80  	return services, nil
    81  }
    82  
    83  func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
    84  	secrets, ok := s.([]types.ServiceSecretConfig)
    85  	if !ok {
    86  		return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
    87  	}
    88  	m := map[interface{}]interface{}{}
    89  	for _, secret := range secrets {
    90  		m[secret.Source] = secret
    91  	}
    92  	return m, nil
    93  }
    94  
    95  func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
    96  	secrets, ok := s.([]types.ServiceConfigObjConfig)
    97  	if !ok {
    98  		return nil, errors.Errorf("not a serviceSecretConfig: %v", s)
    99  	}
   100  	m := map[interface{}]interface{}{}
   101  	for _, secret := range secrets {
   102  		m[secret.Source] = secret
   103  	}
   104  	return m, nil
   105  }
   106  
   107  func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) {
   108  	ports, ok := s.([]types.ServicePortConfig)
   109  	if !ok {
   110  		return nil, errors.Errorf("not a servicePortConfig slice: %v", s)
   111  	}
   112  	m := map[interface{}]interface{}{}
   113  	for _, p := range ports {
   114  		m[p.Published] = p
   115  	}
   116  	return m, nil
   117  }
   118  
   119  func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
   120  	s := []types.ServiceSecretConfig{}
   121  	for _, v := range m {
   122  		s = append(s, v.(types.ServiceSecretConfig))
   123  	}
   124  	sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
   125  	dst.Set(reflect.ValueOf(s))
   126  	return nil
   127  }
   128  
   129  func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
   130  	s := []types.ServiceConfigObjConfig{}
   131  	for _, v := range m {
   132  		s = append(s, v.(types.ServiceConfigObjConfig))
   133  	}
   134  	sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source })
   135  	dst.Set(reflect.ValueOf(s))
   136  	return nil
   137  }
   138  
   139  func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error {
   140  	s := []types.ServicePortConfig{}
   141  	for _, v := range m {
   142  		s = append(s, v.(types.ServicePortConfig))
   143  	}
   144  	sort.Slice(s, func(i, j int) bool { return s[i].Published < s[j].Published })
   145  	dst.Set(reflect.ValueOf(s))
   146  	return nil
   147  }
   148  
   149  type tomapFn func(s interface{}) (map[interface{}]interface{}, error)
   150  type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error
   151  
   152  func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error {
   153  	return func(dst, src reflect.Value) error {
   154  		if src.IsNil() {
   155  			return nil
   156  		}
   157  		if dst.IsNil() {
   158  			dst.Set(src)
   159  			return nil
   160  		}
   161  		return mergeFn(dst, src)
   162  	}
   163  }
   164  
   165  func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error {
   166  	return func(dst, src reflect.Value) error {
   167  		dstMap, err := sliceToMap(tomap, dst)
   168  		if err != nil {
   169  			return err
   170  		}
   171  		srcMap, err := sliceToMap(tomap, src)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil {
   176  			return err
   177  		}
   178  		return writeValue(dst, dstMap)
   179  	}
   180  }
   181  
   182  func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) {
   183  	// check if valid
   184  	if !v.IsValid() {
   185  		return nil, errors.Errorf("invalid value : %+v", v)
   186  	}
   187  	return tomap(v.Interface())
   188  }
   189  
   190  func mergeLoggingConfig(dst, src reflect.Value) error {
   191  	// Same driver, merging options
   192  	if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
   193  		getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
   194  		if getLoggingDriver(dst.Elem()) == "" {
   195  			dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
   196  		}
   197  		dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
   198  		srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
   199  		return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride)
   200  	}
   201  	// Different driver, override with src
   202  	dst.Set(src)
   203  	return nil
   204  }
   205  
   206  //nolint: unparam
   207  func mergeUlimitsConfig(dst, src reflect.Value) error {
   208  	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
   209  		dst.Elem().Set(src.Elem())
   210  	}
   211  	return nil
   212  }
   213  
   214  //nolint: unparam
   215  func mergeServiceNetworkConfig(dst, src reflect.Value) error {
   216  	if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
   217  		dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
   218  		if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" {
   219  			dst.Elem().FieldByName("Ipv4Address").SetString(ipv4)
   220  		}
   221  		if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" {
   222  			dst.Elem().FieldByName("Ipv6Address").SetString(ipv6)
   223  		}
   224  	}
   225  	return nil
   226  }
   227  
   228  func getLoggingDriver(v reflect.Value) string {
   229  	return v.FieldByName("Driver").String()
   230  }
   231  
   232  func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
   233  	m := map[string]types.ServiceConfig{}
   234  	for _, service := range services {
   235  		m[service.Name] = service
   236  	}
   237  	return m
   238  }
   239  
   240  func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
   241  	err := mergo.Map(&base, &override, mergo.WithOverride)
   242  	return base, err
   243  }
   244  
   245  func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
   246  	err := mergo.Map(&base, &override, mergo.WithOverride)
   247  	return base, err
   248  }
   249  
   250  func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
   251  	err := mergo.Map(&base, &override, mergo.WithOverride)
   252  	return base, err
   253  }
   254  
   255  func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
   256  	err := mergo.Map(&base, &override, mergo.WithOverride)
   257  	return base, err
   258  }