Repository: docker/compose
Pull request #11815 (2024-05-14 17:42:19)

Per endpoint driver opts
**What I did**

It's possible to set driver options when creating a network using compose ... [compose spec](https://github.com/robmry/compose-go/blob/b08e2159a8b1fd78444c2ef434bd4913d74697d1/schema/compose-spec.json#L637-L641) / [engine API](https://github.com/moby/moby/blob/9d07820b221db010bf1bdc26ca904468804ca712/api/swagger.yaml#L10134-L10138).

But not when connecting an endpoint to a network ... [compose spec](https://github.com/robmry/compose-go/blob/b08e2159a8b1fd78444c2ef434bd4913d74697d1/schema/compose-spec.json#L293-L300) / [engine API](https://github.com/moby/moby/blob/9d07820b221db010bf1bdc26ca904468804ca712/api/swagger.yaml#L2541-L2544).

The API field isn't new, but upcoming change https://github.com/moby/moby/pull/47686 means it'll be useful for setting per-interface sysctls.

https://github.com/compose-spec/compose-go/pull/627 adds the types.
https://github.com/compose-spec/compose-spec/pull/498 updates the spec to match.
This PR pulls in the `go-compose` changes, and copies the driver options to `EndpointSettings` for the API.

_I noticed a couple of fields in `EndpointSettings` that perhaps shouldn't be set ... the struct is an unfortunate mix of config and operational-data (used to report running state). Those fields will be cleared by the daemon, so the only potential problem is a bit of confusion - in particular, the container's IPv6 address is provided in the IPv6 gateway. But, probably best to deal with that separately, so I just left a FIXME comment in the new test._ 

**Related issue**
<!-- If this is a bug fix, make sure your description includes "fixes #xxxx", or "closes #xxxx" -->

**(not mandatory) A picture of a cute animal, if possible in relation to what you did**


robmry: I don't think the failing test looks related, is it just something that happens? ...

```
FAIL pkg/e2e.TestCascadeFail (2.19s)
      cascade_test.go:44: WARNING: docker-buildx cli-plugin not found, using default buildx installation.
      cascade_test.go:44: Running command: docker compose version
  Docker Compose version 0eef47d
      cascade_test.go:50: Running command: docker compose -f ./fixtures/cascade/compose.yaml --project-name compose-e2e-cascade-fail up --abort-on-container-failure
  Attaching to exit-1, fail-1, running-1
  
  exit-1 exited with code 0
  
  fail-1 exited with code 111
      cascade_test.go:55: assertion failed: 143 (res.ExitCode int) != 111 (int)
      cascade_test.go:47: Running command: docker compose --project-name compose-e2e-cascade-fail down
      framework.go:130: Contents of config dir:
      framework.go:132:   - /tmp/TestCascadeFail1299123561/001
      framework.go:132:   - /tmp/TestCascadeFail1299123561/001/cli-plugins
      framework.go:132:   - /tmp/TestCascadeFail1299123561/001/cli-plugins/docker-compose
      framework.go:132:   - /tmp/TestCascadeFail1299123561/001/cli-plugins/docker-scan
```

Patch:
diff --git a/go.mod b/go.mod
index a0fb6f388..f68b26785 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,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.1.1-0.20240516145816-197e6352c64a
+	github.com/compose-spec/compose-go/v2 v2.1.1
 	github.com/containerd/console v1.0.4
 	github.com/containerd/containerd v1.7.16
 	github.com/davecgh/go-spew v1.1.1
diff --git a/go.sum b/go.sum
index 454bd65fe..fca8ed461 100644
--- a/go.sum
+++ b/go.sum
@@ -90,8 +90,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
 github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 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.1.1-0.20240516145816-197e6352c64a h1:R0ufvPulvRvFa8JaFEWzSJmkhjfeArXD9vKk0jpmpM4=
-github.com/compose-spec/compose-go/v2 v2.1.1-0.20240516145816-197e6352c64a/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
+github.com/compose-spec/compose-go/v2 v2.1.1 h1:tKuYJwAVgxIryRrsvWJSf1kNviVOQVVqwyHsV6YoIUc=
+github.com/compose-spec/compose-go/v2 v2.1.1/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
 github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
 github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
 github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
diff --git a/pkg/compose/create.go b/pkg/compose/create.go
index a5aa9f9e2..73ff47372 100644
--- a/pkg/compose/create.go
+++ b/pkg/compose/create.go
@@ -439,6 +439,7 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
 		ipv4Address string
 		ipv6Address string
 		macAddress  string
+		driverOpts  types.Options
 	)
 	if config != nil {
 		ipv4Address = config.Ipv4Address
@@ -449,6 +450,7 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
 			LinkLocalIPs: config.LinkLocalIPs,
 		}
 		macAddress = config.MacAddress
+		driverOpts = config.DriverOpts
 	}
 	return &network.EndpointSettings{
 		Aliases:     getAliases(p, service, serviceIndex, networkKey, useNetworkAliases),
@@ -457,6 +459,7 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
 		IPv6Gateway: ipv6Address,
 		IPAMConfig:  ipam,
 		MacAddress:  macAddress,
+		DriverOpts:  driverOpts,
 	}
 }
 


Test patch:
diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go
index 81ce8e124..34d5f59c4 100644
--- a/pkg/compose/create_test.go
+++ b/pkg/compose/create_test.go
@@ -25,6 +25,7 @@ import (
 	"gotest.tools/v3/assert/cmp"
 
 	"github.com/docker/compose/v2/pkg/api"
+	"github.com/docker/docker/api/types/network"
 
 	composetypes "github.com/compose-spec/compose-go/v2/types"
 	moby "github.com/docker/docker/api/types"
@@ -275,3 +276,54 @@ func TestDefaultNetworkSettings(t *testing.T) {
 		assert.Check(t, cmp.Nil(networkConfig))
 	})
 }
+
+func TestCreateEndpointSettings(t *testing.T) {
+	eps := createEndpointSettings(
+		&composetypes.Project{
+			Name: "projName",
+		},
+		composetypes.ServiceConfig{
+			Name:          "serviceName",
+			ContainerName: "containerName",
+			Networks: map[string]*composetypes.ServiceNetworkConfig{
+				"netName": {
+					Priority:     100,
+					Aliases:      []string{"alias1", "alias2"},
+					Ipv4Address:  "10.16.17.18",
+					Ipv6Address:  "fdb4:7a7f:373a:3f0c::42",
+					LinkLocalIPs: []string{"169.254.10.20"},
+					MacAddress:   "10:00:00:00:01",
+					DriverOpts: composetypes.Options{
+						"driverOpt1": "optval1",
+						"driverOpt2": "optval2",
+					},
+				},
+			},
+		},
+		0,                          // serviceIndex
+		"netName",                  // networkKey
+		[]string{"link1", "link2"}, // links
+		true,                       // useNetworkAliases
+	)
+	assert.Check(t, cmp.DeepEqual(eps, &network.EndpointSettings{
+		IPAMConfig: &network.EndpointIPAMConfig{
+			IPv4Address:  "10.16.17.18",
+			IPv6Address:  "fdb4:7a7f:373a:3f0c::42",
+			LinkLocalIPs: []string{"169.254.10.20"},
+		},
+		Links:      []string{"link1", "link2"},
+		Aliases:    []string{"containerName", "serviceName", "alias1", "alias2"},
+		MacAddress: "10:00:00:00:01",
+		DriverOpts: map[string]string{
+			"driverOpt1": "optval1",
+			"driverOpt2": "optval2",
+		},
+
+		// FIXME(robmry) - IPAddress and IPv6Gateway are "operational data" fields...
+		//  - The IPv6 address here is the container's address, not the gateway.
+		//  - Both fields will be cleared by the daemon, but they could be removed from
+		//    the request.
+		IPAddress:   "10.16.17.18",
+		IPv6Gateway: "fdb4:7a7f:373a:3f0c::42",
+	}))
+}