From ead03f2271ae3571f2da66a572b3a24b9f2f6468 Mon Sep 17 00:00:00 2001 From: pnkcaht Date: Wed, 14 Jan 2026 17:15:49 -0500 Subject: [PATCH 1/3] compose: honor gw_priority ordering when building NetworkingConfig Signed-off-by: pnkcaht --- pkg/compose/create.go | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/pkg/compose/create.go b/pkg/compose/create.go index e4293cefbd..83aca4ffdd 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -25,6 +25,7 @@ import ( "os" "path/filepath" "slices" + "sort" "strconv" "strings" @@ -519,8 +520,17 @@ func defaultNetworkSettings(project *types.Project, } else { primaryNetworkKey = "default" } + primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name - primaryNetworkEndpoint := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases) + primaryNetworkEndpoint := createEndpointSettings( + project, + service, + serviceIndex, + primaryNetworkKey, + links, + useNetworkAliases, + ) + endpointsConfig := map[string]*network.EndpointSettings{} // Starting from API version 1.44, the Engine will take several EndpointsConfigs @@ -532,7 +542,14 @@ func defaultNetworkSettings(project *types.Project, serviceNetworks := service.NetworksByPriority() for _, networkKey := range serviceNetworks[1:] { mobyNetworkName := project.Networks[networkKey].Name - epSettings := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases) + epSettings := createEndpointSettings( + project, + service, + serviceIndex, + networkKey, + links, + useNetworkAliases, + ) endpointsConfig[mobyNetworkName] = epSettings } } @@ -544,14 +561,19 @@ func defaultNetworkSettings(project *types.Project, if versions.LessThan(version, APIVersion149) { for _, config := range service.Networks { if config != nil && config.InterfaceName != "" { - return "", nil, fmt.Errorf("interface_name requires Docker Engine %s or later", DockerEngineV28_1) + return "", nil, fmt.Errorf( + "interface_name requires Docker Engine %s or later", + DockerEngineV28_1, + ) } } } + // Include the primary network before ordering. endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint + networkConfig := &network.NetworkingConfig{ - EndpointsConfig: endpointsConfig, + EndpointsConfig: orderEndpointsByGwPriority(endpointsConfig), } // From the Engine API docs: @@ -560,6 +582,33 @@ func defaultNetworkSettings(project *types.Project, return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig, nil } +type endpointWithName struct { + name string + settings *network.EndpointSettings +} + +func orderEndpointsByGwPriority(endpoints map[string]*network.EndpointSettings) map[string]*network.EndpointSettings { + if len(endpoints) <= 1 { + return endpoints + } + + ordered := make([]endpointWithName, 0, len(endpoints)) + for name, ep := range endpoints { + ordered = append(ordered, endpointWithName{name, ep}) + } + + sort.SliceStable(ordered, func(i, j int) bool { + return ordered[i].settings.GwPriority > ordered[j].settings.GwPriority + }) + + orderedEndpoints := make(map[string]*network.EndpointSettings, len(ordered)) + for _, ep := range ordered { + orderedEndpoints[ep.name] = ep.settings + } + + return orderedEndpoints +} + func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy { var restart container.RestartPolicy if service.Restart != "" { From 1e3fc17ee82d8e347bb81ec435d7b06a118461b2 Mon Sep 17 00:00:00 2001 From: pnkcaht Date: Wed, 14 Jan 2026 17:23:05 -0500 Subject: [PATCH 2/3] compose: honor gw_priority ordering when building NetworkingConfig Signed-off-by: pnkcaht --- pkg/compose/convergence_test.go | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go index 843689a112..14ad532675 100644 --- a/pkg/compose/convergence_test.go +++ b/pkg/compose/convergence_test.go @@ -30,6 +30,7 @@ import ( "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" + "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "gotest.tools/v3/assert" @@ -425,3 +426,49 @@ func TestCreateMobyContainer(t *testing.T) { assert.NilError(t, err) }) } + +func TestDefaultNetworkSettingsOrdersEndpointsByGwPriority(t *testing.T) { + project := &types.Project{ + Name: "test", + Networks: map[string]types.NetworkConfig{ + "net1": {Name: "net1"}, + "net2": {Name: "net2"}, + }, + } + + service := types.ServiceConfig{ + Name: "svc", + Networks: map[string]*types.ServiceNetworkConfig{ + "net1": {GatewayPriority: 0}, + "net2": {GatewayPriority: 100}, + }, + } + + mode, netConfig, err := defaultNetworkSettings( + project, + service, + 0, + nil, + false, + APIVersion144, + ) + + require.NoError(t, err) + require.NotNil(t, netConfig) + + endpoints := netConfig.EndpointsConfig + require.Len(t, endpoints, 2) + + keys := make([]string, 0, len(endpoints)) + for k := range endpoints { + keys = append(keys, k) + } + + // Highest gw_priority must come first in EndpointsConfig + assert.Equal(t, keys[0], "net2") + assert.Equal(t, keys[1], "net1") + + // NetworkMode must remain the primary network + assert.Equal(t, mode, container.NetworkMode("net1")) + +} From b73e3fe0f575eac82bf8bbeff94568d24d54650f Mon Sep 17 00:00:00 2001 From: pnkcaht Date: Thu, 15 Jan 2026 13:10:21 -0500 Subject: [PATCH 3/3] compose: do not rely on endpoint ordering for gw_priority Signed-off-by: pnkcaht --- pkg/compose/convergence_test.go | 18 ++++++------------ pkg/compose/create.go | 32 ++------------------------------ 2 files changed, 8 insertions(+), 42 deletions(-) diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go index 14ad532675..2a02f7e175 100644 --- a/pkg/compose/convergence_test.go +++ b/pkg/compose/convergence_test.go @@ -33,6 +33,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" "github.com/docker/compose/v5/pkg/api" "github.com/docker/compose/v5/pkg/mocks" @@ -427,7 +428,7 @@ func TestCreateMobyContainer(t *testing.T) { }) } -func TestDefaultNetworkSettingsOrdersEndpointsByGwPriority(t *testing.T) { +func TestDefaultNetworkSettingsGwPrioritySelectsPrimaryNetwork(t *testing.T) { project := &types.Project{ Name: "test", Networks: map[string]types.NetworkConfig{ @@ -459,16 +460,9 @@ func TestDefaultNetworkSettingsOrdersEndpointsByGwPriority(t *testing.T) { endpoints := netConfig.EndpointsConfig require.Len(t, endpoints, 2) - keys := make([]string, 0, len(endpoints)) - for k := range endpoints { - keys = append(keys, k) - } - - // Highest gw_priority must come first in EndpointsConfig - assert.Equal(t, keys[0], "net2") - assert.Equal(t, keys[1], "net1") - - // NetworkMode must remain the primary network - assert.Equal(t, mode, container.NetworkMode("net1")) + assert.Assert(t, cmp.Contains(endpoints, "net1")) + assert.Assert(t, cmp.Contains(endpoints, "net2")) + // gw_priority must only affect primary network selection + assert.Assert(t, cmp.Equal(container.NetworkMode("net1"), mode)) } diff --git a/pkg/compose/create.go b/pkg/compose/create.go index 83aca4ffdd..29e281fb27 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -25,7 +25,6 @@ import ( "os" "path/filepath" "slices" - "sort" "strconv" "strings" @@ -569,11 +568,11 @@ func defaultNetworkSettings(project *types.Project, } } - // Include the primary network before ordering. + // Include the primary network endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint networkConfig := &network.NetworkingConfig{ - EndpointsConfig: orderEndpointsByGwPriority(endpointsConfig), + EndpointsConfig: endpointsConfig, } // From the Engine API docs: @@ -582,33 +581,6 @@ func defaultNetworkSettings(project *types.Project, return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig, nil } -type endpointWithName struct { - name string - settings *network.EndpointSettings -} - -func orderEndpointsByGwPriority(endpoints map[string]*network.EndpointSettings) map[string]*network.EndpointSettings { - if len(endpoints) <= 1 { - return endpoints - } - - ordered := make([]endpointWithName, 0, len(endpoints)) - for name, ep := range endpoints { - ordered = append(ordered, endpointWithName{name, ep}) - } - - sort.SliceStable(ordered, func(i, j int) bool { - return ordered[i].settings.GwPriority > ordered[j].settings.GwPriority - }) - - orderedEndpoints := make(map[string]*network.EndpointSettings, len(ordered)) - for _, ep := range ordered { - orderedEndpoints[ep.name] = ep.settings - } - - return orderedEndpoints -} - func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy { var restart container.RestartPolicy if service.Restart != "" {