Browse code

api: Make EnableIPv6 optional

Currently, starting dockerd with
`--default-network-opt=bridge=com.docker.network.enable_ipv6=true` has
no effect as `NetworkCreateRequest.EnableIPv6` is a basic bool.

This change makes it a `*bool` to make it optional. If clients don't
specify it, the default-network-opt will be applied.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>

Albin Kerouanton authored on 2024/05/28 19:23:34
Showing 7 changed files
... ...
@@ -451,7 +451,7 @@ type NetworkCreate struct {
451 451
 	CheckDuplicate bool                     `json:",omitempty"`
452 452
 	Driver         string                   // Driver is the driver-name used to create the network (e.g. `bridge`, `overlay`)
453 453
 	Scope          string                   // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level).
454
-	EnableIPv6     bool                     // EnableIPv6 represents whether to enable IPv6.
454
+	EnableIPv6     *bool                    `json:",omitempty"` // EnableIPv6 represents whether to enable IPv6.
455 455
 	IPAM           *network.IPAM            // IPAM is the network's IP Address Management.
456 456
 	Internal       bool                     // Internal represents if the network is used internal only.
457 457
 	Attachable     bool                     // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
... ...
@@ -65,9 +65,10 @@ func TestNetworkCreate(t *testing.T) {
65 65
 		}),
66 66
 	}
67 67
 
68
+	enableIPv6 := true
68 69
 	networkResponse, err := client.NetworkCreate(context.Background(), "mynetwork", types.NetworkCreate{
69 70
 		Driver:     "mydriver",
70
-		EnableIPv6: true,
71
+		EnableIPv6: &enableIPv6,
71 72
 		Internal:   true,
72 73
 		Options: map[string]string{
73 74
 			"opt-key": "opt-value",
... ...
@@ -195,10 +195,12 @@ func BasicNetworkCreateToGRPC(create basictypes.NetworkCreateRequest) swarmapi.N
195 195
 			Name:    create.Driver,
196 196
 			Options: create.Options,
197 197
 		},
198
-		Ipv6Enabled: create.EnableIPv6,
199
-		Internal:    create.Internal,
200
-		Attachable:  create.Attachable,
201
-		Ingress:     create.Ingress,
198
+		Internal:   create.Internal,
199
+		Attachable: create.Attachable,
200
+		Ingress:    create.Ingress,
201
+	}
202
+	if create.EnableIPv6 != nil {
203
+		ns.Ipv6Enabled = *create.EnableIPv6
202 204
 	}
203 205
 	if create.IPAM != nil {
204 206
 		driver := create.IPAM.Driver
... ...
@@ -621,13 +621,14 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ
621 621
 		return clustertypes.NetworkCreateRequest{}, errors.New("container: unknown network referenced")
622 622
 	}
623 623
 
624
+	ipv6Enabled := na.Network.Spec.Ipv6Enabled
624 625
 	options := types.NetworkCreate{
625 626
 		// ID:     na.Network.ID,
626 627
 		Labels:     na.Network.Spec.Annotations.Labels,
627 628
 		Internal:   na.Network.Spec.Internal,
628 629
 		Attachable: na.Network.Spec.Attachable,
629 630
 		Ingress:    convert.IsIngressNetwork(na.Network),
630
-		EnableIPv6: na.Network.Spec.Ipv6Enabled,
631
+		EnableIPv6: &ipv6Enabled,
631 632
 		Scope:      scope.Swarm,
632 633
 	}
633 634
 
... ...
@@ -318,8 +318,19 @@ func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCrea
318 318
 		}
319 319
 	}
320 320
 
321
+	var enableIPv6 bool
322
+	if create.EnableIPv6 != nil {
323
+		enableIPv6 = *create.EnableIPv6
324
+	} else {
325
+		var err error
326
+		v, ok := networkOptions[netlabel.EnableIPv6]
327
+		if enableIPv6, err = strconv.ParseBool(v); ok && err != nil {
328
+			return nil, errdefs.InvalidParameter(fmt.Errorf("driver-opt %q is not a valid bool", netlabel.EnableIPv6))
329
+		}
330
+	}
331
+
321 332
 	nwOptions := []libnetwork.NetworkOption{
322
-		libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
333
+		libnetwork.NetworkOptionEnableIPv6(enableIPv6),
323 334
 		libnetwork.NetworkOptionDriverOpts(networkOptions),
324 335
 		libnetwork.NetworkOptionLabels(create.Labels),
325 336
 		libnetwork.NetworkOptionAttachable(create.Attachable),
... ...
@@ -331,7 +342,7 @@ func (daemon *Daemon) createNetwork(cfg *config.Config, create types.NetworkCrea
331 331
 		nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigOnly())
332 332
 	}
333 333
 
334
-	if err := network.ValidateIPAM(create.IPAM, create.EnableIPv6); err != nil {
334
+	if err := network.ValidateIPAM(create.IPAM, enableIPv6); err != nil {
335 335
 		if agent {
336 336
 			// This function is called with agent=false for all networks. For swarm-scoped
337 337
 			// networks, the configuration is validated but ManagerRedirectError is returned
... ...
@@ -15,7 +15,8 @@ func WithDriver(driver string) func(*types.NetworkCreate) {
15 15
 // WithIPv6 Enables IPv6 on the network
16 16
 func WithIPv6() func(*types.NetworkCreate) {
17 17
 	return func(n *types.NetworkCreate) {
18
-		n.EnableIPv6 = true
18
+		enableIPv6 := true
19
+		n.EnableIPv6 = &enableIPv6
19 20
 	}
20 21
 }
21 22
 
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/docker/docker/api/types/versions"
12 12
 	ctr "github.com/docker/docker/integration/internal/container"
13 13
 	"github.com/docker/docker/integration/internal/network"
14
+	"github.com/docker/docker/testutil/daemon"
14 15
 	"gotest.tools/v3/assert"
15 16
 	"gotest.tools/v3/skip"
16 17
 )
... ...
@@ -68,3 +69,31 @@ func TestCreateWithIPv6DefaultsToULAPrefix(t *testing.T) {
68 68
 
69 69
 	t.Fatalf("Network %s has no ULA prefix, expected one.", nwName)
70 70
 }
71
+
72
+func TestCreateWithIPv6WithoutEnableIPv6Flag(t *testing.T) {
73
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows") // d.Start fails on Windows with `protocol not available`
74
+	ctx := setupTest(t)
75
+
76
+	d := daemon.New(t)
77
+	d.StartWithBusybox(ctx, t, "-D", "--default-network-opt=bridge=com.docker.network.enable_ipv6=true")
78
+	defer d.Stop(t)
79
+
80
+	apiClient := d.NewClientT(t)
81
+	defer apiClient.Close()
82
+
83
+	const nwName = "testnetula"
84
+	network.CreateNoError(ctx, t, apiClient, nwName)
85
+	defer network.RemoveNoError(ctx, t, apiClient, nwName)
86
+
87
+	nw, err := apiClient.NetworkInspect(ctx, "testnetula", networktypes.InspectOptions{})
88
+	assert.NilError(t, err)
89
+
90
+	for _, ipam := range nw.IPAM.Config {
91
+		ipr := netip.MustParsePrefix(ipam.Subnet)
92
+		if netip.MustParsePrefix("fd00::/8").Overlaps(ipr) {
93
+			return
94
+		}
95
+	}
96
+
97
+	t.Fatalf("Network %s has no ULA prefix, expected one.", nwName)
98
+}