Skip to content

Commit d62b889

Browse files
authored
Merge pull request #6803 from thaJeztah/compose_fixes
cli/compose: assorted fixes and cleanups
2 parents 44ca067 + b35a2d0 commit d62b889

File tree

2 files changed

+75
-52
lines changed

2 files changed

+75
-52
lines changed

cli/compose/convert/service.go

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2+
//go:build go1.24
3+
14
package convert
25

36
import (
7+
"cmp"
48
"context"
59
"errors"
610
"fmt"
711
"net/netip"
812
"os"
13+
"slices"
914
"sort"
1015
"strings"
1116
"time"
@@ -567,21 +572,42 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
567572
return resources, nil
568573
}
569574

575+
// compareSwarmPortConfig returns the lexical ordering of a and b, and can be used
576+
// with [slices.SortFunc].
577+
//
578+
// The comparison is performed in the following priority order:
579+
//
580+
// 1. PublishedPort (host port)
581+
// 2. TargetPort (container port)
582+
// 3. Protocol
583+
// 4. PublishMode
584+
//
585+
// TODO(thaJeztah): define this on swarm.PortConfig itself to allow re-use.
586+
func compareSwarmPortConfig(a, b swarm.PortConfig) int {
587+
if n := cmp.Compare(a.PublishedPort, b.PublishedPort); n != 0 {
588+
return n
589+
}
590+
if n := cmp.Compare(a.TargetPort, b.TargetPort); n != 0 {
591+
return n
592+
}
593+
if n := cmp.Compare(a.Protocol, b.Protocol); n != 0 {
594+
return n
595+
}
596+
return cmp.Compare(a.PublishMode, b.PublishMode)
597+
}
598+
570599
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) *swarm.EndpointSpec {
571600
portConfigs := make([]swarm.PortConfig, 0, len(source))
572601
for _, port := range source {
573-
portConfig := swarm.PortConfig{
602+
portConfigs = append(portConfigs, swarm.PortConfig{
574603
Protocol: network.IPProtocol(port.Protocol),
575604
TargetPort: port.Target,
576605
PublishedPort: port.Published,
577606
PublishMode: swarm.PortConfigPublishMode(port.Mode),
578-
}
579-
portConfigs = append(portConfigs, portConfig)
607+
})
580608
}
581609

582-
sort.Slice(portConfigs, func(i, j int) bool {
583-
return portConfigs[i].PublishedPort < portConfigs[j].PublishedPort
584-
})
610+
slices.SortFunc(portConfigs, compareSwarmPortConfig)
585611

586612
return &swarm.EndpointSpec{
587613
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
@@ -702,28 +728,22 @@ func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpec
702728
}
703729

704730
func convertUlimits(origUlimits map[string]*composetypes.UlimitsConfig) []*container.Ulimit {
705-
newUlimits := make(map[string]*container.Ulimit)
731+
ulimits := make([]*container.Ulimit, 0, len(origUlimits))
706732
for name, u := range origUlimits {
733+
soft, hard := int64(u.Soft), int64(u.Hard)
707734
if u.Single != 0 {
708-
newUlimits[name] = &container.Ulimit{
709-
Name: name,
710-
Soft: int64(u.Single),
711-
Hard: int64(u.Single),
712-
}
713-
} else {
714-
newUlimits[name] = &container.Ulimit{
715-
Name: name,
716-
Soft: int64(u.Soft),
717-
Hard: int64(u.Hard),
718-
}
735+
soft, hard = int64(u.Single), int64(u.Single)
719736
}
737+
738+
ulimits = append(ulimits, &container.Ulimit{
739+
Name: name,
740+
Soft: soft,
741+
Hard: hard,
742+
})
720743
}
721-
ulimits := make([]*container.Ulimit, 0, len(newUlimits))
722-
for _, ulimit := range newUlimits {
723-
ulimits = append(ulimits, ulimit)
724-
}
725-
sort.SliceStable(ulimits, func(i, j int) bool {
726-
return ulimits[i].Name < ulimits[j].Name
744+
745+
slices.SortFunc(ulimits, func(a, b *container.Ulimit) int {
746+
return cmp.Compare(a.Name, b.Name)
727747
})
728748
return ulimits
729749
}

cli/compose/loader/merge.go

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
package loader
55

66
import (
7+
"cmp"
78
"fmt"
89
"reflect"
10+
"slices"
911
"sort"
1012

1113
"dario.cat/mergo"
@@ -52,10 +54,10 @@ func merge(configs []*types.Config) (*types.Config, error) {
5254
}
5355

5456
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
55-
baseServices := mapByName(base)
56-
overrideServices := mapByName(override)
57-
specials := &specials{
58-
m: map[reflect.Type]func(dst, src reflect.Value) error{
57+
mergeOpts := []func(*mergo.Config){
58+
mergo.WithAppendSlice,
59+
mergo.WithOverride,
60+
mergo.WithTransformers(&specials{m: map[reflect.Type]func(dst, src reflect.Value) error{
5961
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
6062
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
6163
reflect.TypeFor[[]types.ServiceSecretConfig](): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
@@ -65,23 +67,34 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
6567
reflect.TypeFor[types.ShellCommand](): mergeShellCommand,
6668
reflect.PointerTo(reflect.TypeFor[types.ServiceNetworkConfig]()): mergeServiceNetworkConfig,
6769
reflect.PointerTo(reflect.TypeFor[uint64]()): mergeUint64,
68-
},
70+
}}),
6971
}
70-
for name, overrideService := range overrideServices {
71-
if baseService, ok := baseServices[name]; ok {
72-
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
73-
return base, fmt.Errorf("cannot merge service %s: %w", name, err)
72+
73+
baseServices := make(map[string]types.ServiceConfig, len(base))
74+
for _, s := range base {
75+
baseServices[s.Name] = s
76+
}
77+
78+
for _, overrideService := range override {
79+
if baseService, ok := baseServices[overrideService.Name]; ok {
80+
if err := mergo.Merge(&baseService, &overrideService, mergeOpts...); err != nil {
81+
return base, fmt.Errorf("cannot merge service %s: %w", overrideService.Name, err)
7482
}
75-
baseServices[name] = baseService
83+
baseServices[overrideService.Name] = baseService
7684
continue
7785
}
78-
baseServices[name] = overrideService
86+
baseServices[overrideService.Name] = overrideService
7987
}
88+
8089
services := make([]types.ServiceConfig, 0, len(baseServices))
8190
for _, baseService := range baseServices {
8291
services = append(services, baseService)
8392
}
84-
sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
93+
94+
slices.SortFunc(services, func(a, b types.ServiceConfig) int {
95+
return cmp.Compare(a.Name, b.Name)
96+
})
97+
8598
return services, nil
8699
}
87100

@@ -217,11 +230,13 @@ func sliceToMap(tomap tomapFn, v reflect.Value) (map[any]any, error) {
217230
}
218231

219232
func mergeLoggingConfig(dst, src reflect.Value) error {
233+
dstDriver := dst.Elem().FieldByName("Driver").String()
234+
srcDriver := src.Elem().FieldByName("Driver").String()
235+
220236
// Same driver, merging options
221-
if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) ||
222-
getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" {
223-
if getLoggingDriver(dst.Elem()) == "" {
224-
dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem()))
237+
if dstDriver == srcDriver || dstDriver == "" || srcDriver == "" {
238+
if dstDriver == "" {
239+
dst.Elem().FieldByName("Driver").SetString(srcDriver)
225240
}
226241
dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string)
227242
srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string)
@@ -270,18 +285,6 @@ func mergeUint64(dst, src reflect.Value) error {
270285
return nil
271286
}
272287

273-
func getLoggingDriver(v reflect.Value) string {
274-
return v.FieldByName("Driver").String()
275-
}
276-
277-
func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
278-
m := map[string]types.ServiceConfig{}
279-
for _, service := range services {
280-
m[service.Name] = service
281-
}
282-
return m
283-
}
284-
285288
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
286289
err := mergo.Map(&base, &override, mergo.WithOverride)
287290
return base, err

0 commit comments

Comments
 (0)