introduce networks.interface_name

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2025-04-23 16:21:54 +02:00 committed by Guillaume Lours
parent 60385e6065
commit 955e4ed94e
6 changed files with 68 additions and 19 deletions

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/Microsoft/go-winio v0.6.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.6.1
github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76
github.com/containerd/containerd/v2 v2.0.5
github.com/containerd/platforms v1.0.0-rc.1
github.com/davecgh/go-spew v1.1.1

4
go.sum
View File

@ -82,8 +82,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.6.1 h1:276YiQKRcGGtgkxiymzWHJ2CTv5joQA+7DTNrUA+rys=
github.com/compose-spec/compose-go/v2 v2.6.1/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76 h1:KZvD41eTRr9/n43zccAcGPBRgzHXdbLZY4IXSeJxqIw=
github.com/compose-spec/compose-go/v2 v2.6.2-0.20250423090706-30ff01d36f76/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=

View File

@ -154,6 +154,7 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
return ids, nil
}
//nolint:gocyclo
func (s *composeService) getCreateConfigs(ctx context.Context,
p *types.Project,
service types.ServiceConfig,
@ -246,7 +247,10 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
if err != nil {
return createConfigs{}, err
}
networkMode, networkingConfig := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion)
networkMode, networkingConfig, err := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion)
if err != nil {
return createConfigs{}, err
}
portBindings := buildContainerPortBindingOptions(service)
// MISC
@ -356,7 +360,7 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
}
if len(withMacAddress) > 1 {
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine 1.44 or later (currently: %s)", strings.Join(withMacAddress, ", "), version)
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine v25 or later", strings.Join(withMacAddress, ", "))
}
if mainNw != nil && mainNw.MacAddress != "" {
@ -379,6 +383,8 @@ func getAliases(project *types.Project, service types.ServiceConfig, serviceInde
}
func createEndpointSettings(p *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, links []string, useNetworkAliases bool) *network.EndpointSettings {
const ifname = "com.docker.network.endpoint.ifname"
config := service.Networks[networkKey]
var ipam *network.EndpointIPAMConfig
var (
@ -398,6 +404,15 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
}
macAddress = config.MacAddress
driverOpts = config.DriverOpts
if config.InterfaceName != "" {
if driverOpts == nil {
driverOpts = map[string]string{}
}
if name, ok := driverOpts[ifname]; ok && name != config.InterfaceName {
logrus.Warnf("ignoring services.%s.networks.%s.interface_name as %s driver_opts is already declared", service.Name, networkKey, ifname)
}
driverOpts[ifname] = config.InterfaceName
}
gwPriority = config.GatewayPriority
}
return &network.EndpointSettings{
@ -471,20 +486,17 @@ func (s *composeService) prepareLabels(labels types.Labels, service types.Servic
}
// defaultNetworkSettings determines the container.NetworkMode and corresponding network.NetworkingConfig (nil if not applicable).
func defaultNetworkSettings(
project *types.Project,
service types.ServiceConfig,
serviceIndex int,
links []string,
useNetworkAliases bool,
func defaultNetworkSettings(project *types.Project,
service types.ServiceConfig, serviceIndex int,
links []string, useNetworkAliases bool,
version string,
) (container.NetworkMode, *network.NetworkingConfig) {
) (container.NetworkMode, *network.NetworkingConfig, error) {
if service.NetworkMode != "" {
return container.NetworkMode(service.NetworkMode), nil
return container.NetworkMode(service.NetworkMode), nil, nil
}
if len(project.Networks) == 0 {
return "none", nil
return "none", nil, nil
}
var primaryNetworkKey string
@ -515,6 +527,14 @@ func defaultNetworkSettings(
}
}
if versions.LessThan(version, "1.49") {
for _, config := range service.Networks {
if config != nil && config.InterfaceName != "" {
return "", nil, fmt.Errorf("interface_name requires Docker Engine v28.1 or later")
}
}
}
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
networkConfig := &network.NetworkingConfig{
EndpointsConfig: endpointsConfig,
@ -523,7 +543,7 @@ func defaultNetworkSettings(
// From the Engine API docs:
// > Supported standard values are: bridge, host, none, and container:<name|id>.
// > Any other value is taken as a custom network's name to which this container should connect to.
return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig
return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig, nil
}
func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {

View File

@ -219,7 +219,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "myProject_myNetwork2")
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork2"))
@ -247,7 +248,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "myProject_default")
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_default"))
@ -264,7 +266,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
},
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "none")
assert.Check(t, cmp.Nil(networkConfig))
})
@ -284,7 +287,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "host")
assert.Check(t, cmp.Nil(networkConfig))
})

View File

@ -0,0 +1,7 @@
services:
test:
image: alpine
command: ip link show
networks:
default:
interface_name: foobar

View File

@ -181,3 +181,21 @@ func TestMacAddress(t *testing.T) {
res := c.RunDockerCmd(t, "inspect", fmt.Sprintf("%s-test-1", projectName), "-f", "{{ (index .NetworkSettings.Networks \"network_mac_address_default\" ).MacAddress }}")
res.Assert(t, icmd.Expected{Out: "00:e0:84:35:d0:e8"})
}
func TestInterfaceName(t *testing.T) {
c := NewCLI(t)
version := c.RunDockerCmd(t, "version", "-f", "{{.Server.Version}}")
major, _, found := strings.Cut(version.Combined(), ".")
assert.Assert(t, found)
if major == "26" || major == "27" {
t.Skip("Skipping test due to docker version < 28")
}
const projectName = "network_interface_name"
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-interface-name/compose.yaml", "--project-name", projectName, "run", "test")
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})
res.Assert(t, icmd.Expected{Out: "foobar@"})
}