diff --git a/cli/compose/loader/merge.go b/cli/compose/loader/merge.go index 015b1f5a55..0de8d8a1b6 100644 --- a/cli/compose/loader/merge.go +++ b/cli/compose/loader/merge.go @@ -57,6 +57,8 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice), reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice), reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice), + reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, + reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, }, } for name, overrideService := range overrideServices { @@ -201,6 +203,28 @@ func mergeLoggingConfig(dst, src reflect.Value) error { return nil } +//nolint: unparam +func mergeUlimitsConfig(dst, src reflect.Value) error { + if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { + dst.Elem().Set(src.Elem()) + } + return nil +} + +//nolint: unparam +func mergeServiceNetworkConfig(dst, src reflect.Value) error { + if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { + dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases")) + if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" { + dst.Elem().FieldByName("Ipv4Address").SetString(ipv4) + } + if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" { + dst.Elem().FieldByName("Ipv6Address").SetString(ipv6) + } + } + return nil +} + func getLoggingDriver(v reflect.Value) string { return v.FieldByName("Driver").String() } diff --git a/cli/compose/loader/merge_test.go b/cli/compose/loader/merge_test.go index 178d302da2..5055c68dba 100644 --- a/cli/compose/loader/merge_test.go +++ b/cli/compose/loader/merge_test.go @@ -1,9 +1,11 @@ package loader import ( + "reflect" "testing" "github.com/docker/cli/cli/compose/types" + "github.com/imdario/mergo" "gotest.tools/assert" ) @@ -1014,3 +1016,94 @@ func TestLoadMultipleNetworks(t *testing.T) { Configs: map[string]types.ConfigObjConfig{}, }, config) } + +func TestMergeUlimitsConfig(t *testing.T) { + specials := &specials{ + m: map[reflect.Type]func(dst, src reflect.Value) error{ + reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, + }, + } + base := map[string]*types.UlimitsConfig{ + "override-single": &types.UlimitsConfig{Single: 100}, + "override-single-with-soft-hard": &types.UlimitsConfig{Single: 200}, + "override-soft-hard": &types.UlimitsConfig{Soft: 300, Hard: 301}, + "override-soft-hard-with-single": &types.UlimitsConfig{Soft: 400, Hard: 401}, + "dont-override": &types.UlimitsConfig{Single: 500}, + } + override := map[string]*types.UlimitsConfig{ + "override-single": &types.UlimitsConfig{Single: 110}, + "override-single-with-soft-hard": &types.UlimitsConfig{Soft: 210, Hard: 211}, + "override-soft-hard": &types.UlimitsConfig{Soft: 310, Hard: 311}, + "override-soft-hard-with-single": &types.UlimitsConfig{Single: 410}, + "add": &types.UlimitsConfig{Single: 610}, + } + err := mergo.Merge(&base, &override, mergo.WithOverride, mergo.WithTransformers(specials)) + assert.NilError(t, err) + assert.DeepEqual( + t, + base, + map[string]*types.UlimitsConfig{ + "override-single": &types.UlimitsConfig{Single: 110}, + "override-single-with-soft-hard": &types.UlimitsConfig{Soft: 210, Hard: 211}, + "override-soft-hard": &types.UlimitsConfig{Soft: 310, Hard: 311}, + "override-soft-hard-with-single": &types.UlimitsConfig{Single: 410}, + "dont-override": &types.UlimitsConfig{Single: 500}, + "add": &types.UlimitsConfig{Single: 610}, + }, + ) +} + +func TestMergeServiceNetworkConfig(t *testing.T) { + specials := &specials{ + m: map[reflect.Type]func(dst, src reflect.Value) error{ + reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, + }, + } + base := map[string]*types.ServiceNetworkConfig{ + "override-aliases": &types.ServiceNetworkConfig{ + Aliases: []string{"100", "101"}, + Ipv4Address: "127.0.0.1", + Ipv6Address: "0:0:0:0:0:0:0:1", + }, + "dont-override": &types.ServiceNetworkConfig{ + Aliases: []string{"200", "201"}, + Ipv4Address: "127.0.0.2", + Ipv6Address: "0:0:0:0:0:0:0:2", + }, + } + override := map[string]*types.ServiceNetworkConfig{ + "override-aliases": &types.ServiceNetworkConfig{ + Aliases: []string{"110", "111"}, + Ipv4Address: "127.0.1.1", + Ipv6Address: "0:0:0:0:0:0:1:1", + }, + "add": &types.ServiceNetworkConfig{ + Aliases: []string{"310", "311"}, + Ipv4Address: "127.0.3.1", + Ipv6Address: "0:0:0:0:0:0:3:1", + }, + } + err := mergo.Merge(&base, &override, mergo.WithOverride, mergo.WithTransformers(specials)) + assert.NilError(t, err) + assert.DeepEqual( + t, + base, + map[string]*types.ServiceNetworkConfig{ + "override-aliases": &types.ServiceNetworkConfig{ + Aliases: []string{"110", "111"}, + Ipv4Address: "127.0.1.1", + Ipv6Address: "0:0:0:0:0:0:1:1", + }, + "dont-override": &types.ServiceNetworkConfig{ + Aliases: []string{"200", "201"}, + Ipv4Address: "127.0.0.2", + Ipv6Address: "0:0:0:0:0:0:0:2", + }, + "add": &types.ServiceNetworkConfig{ + Aliases: []string{"310", "311"}, + Ipv4Address: "127.0.3.1", + Ipv6Address: "0:0:0:0:0:0:3:1", + }, + }, + ) +} diff --git a/vendor.conf b/vendor.conf index aaf3dd8e60..d2ea55c912 100755 --- a/vendor.conf +++ b/vendor.conf @@ -34,7 +34,7 @@ github.com/gorilla/mux 00bdffe0f3c77e27d2cf6f5c7023 github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3 -github.com/imdario/mergo 7c29201646fa3de8506f701213473dd407f19646 # v0.3.7 +github.com/imdario/mergo 1afb36080aec31e0d1528973ebe6721b191b0369 # v0.3.8 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0.0 github.com/jaguilar/vt100 ad4c4a5743050fb7f88ce968dca9422f72a0e3f2 git://github.com/tonistiigi/vt100.git github.com/json-iterator/go 0ff49de124c6f76f8494e194af75bde0f1a49a29 # 1.1.6 diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go index f8de6c5430..3fb6c64d05 100644 --- a/vendor/github.com/imdario/mergo/merge.go +++ b/vendor/github.com/imdario/mergo/merge.go @@ -26,10 +26,12 @@ func hasExportedField(dst reflect.Value) (exported bool) { } type Config struct { - Overwrite bool - AppendSlice bool - Transformers Transformers - overwriteWithEmptyValue bool + Overwrite bool + AppendSlice bool + TypeCheck bool + Transformers Transformers + overwriteWithEmptyValue bool + overwriteSliceWithEmptyValue bool } type Transformers interface { @@ -41,7 +43,9 @@ type Transformers interface { // short circuiting on recursive types. func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { overwrite := config.Overwrite + typeCheck := config.TypeCheck overwriteWithEmptySrc := config.overwriteWithEmptyValue + overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue config.overwriteWithEmptyValue = false if !src.IsValid() { @@ -128,11 +132,14 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co dstSlice = reflect.ValueOf(dstElement.Interface()) } - if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + if typeCheck && srcSlice.Type() != dstSlice.Type() { + return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) + } dstSlice = srcSlice } else if config.AppendSlice { if srcSlice.Type() != dstSlice.Type() { - return fmt.Errorf("cannot append two slice with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) + return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) } dstSlice = reflect.AppendSlice(dstSlice, srcSlice) } @@ -143,7 +150,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co continue } - if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { + if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } @@ -154,7 +161,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co if !dst.CanSet() { break } - if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { dst.Set(src) } else if config.AppendSlice { if src.Type() != dst.Type() { @@ -168,11 +175,21 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co if src.IsNil() { break } - if src.Kind() != reflect.Interface { + + if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) { if dst.IsNil() || overwrite { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } + } + break + } + + if src.Kind() != reflect.Interface { + if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } } else if src.Kind() == reflect.Ptr { if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { return @@ -198,6 +215,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co dst.Set(src) } } + return } @@ -209,7 +227,7 @@ func Merge(dst, src interface{}, opts ...func(*Config)) error { return merge(dst, src, opts...) } -// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by // non-empty src attribute values. // Deprecated: use Merge(…) with WithOverride func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { @@ -228,11 +246,21 @@ func WithOverride(config *Config) { config.Overwrite = true } -// WithAppendSlice will make merge append slices instead of overwriting it +// WithOverride will make merge override empty dst slice with empty src slice. +func WithOverrideEmptySlice(config *Config) { + config.overwriteSliceWithEmptyValue = true +} + +// WithAppendSlice will make merge append slices instead of overwriting it. func WithAppendSlice(config *Config) { config.AppendSlice = true } +// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride). +func WithTypeCheck(config *Config) { + config.TypeCheck = true +} + func merge(dst, src interface{}, opts ...func(*Config)) error { var ( vDst, vSrc reflect.Value