Browse code

Move ipvlan and macvlan tests on their own folder…

… making each folder/suites quicker to run

Signed-off-by: Vincent Demeester <vincent@sbr.pm>

Vincent Demeester authored on 2018/04/11 18:14:58
Showing 7 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,85 @@
0
+package network
1
+
2
+import (
3
+	"context"
4
+	"fmt"
5
+	"testing"
6
+
7
+	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/client"
9
+	"github.com/docker/docker/pkg/parsers/kernel"
10
+	"github.com/gotestyourself/gotestyourself/assert/cmp"
11
+	"github.com/gotestyourself/gotestyourself/icmd"
12
+)
13
+
14
+// CreateMasterDummy creates a dummy network interface
15
+func CreateMasterDummy(t *testing.T, master string) {
16
+	// ip link add <dummy_name> type dummy
17
+	icmd.RunCommand("ip", "link", "add", master, "type", "dummy").Assert(t, icmd.Success)
18
+	icmd.RunCommand("ip", "link", "set", master, "up").Assert(t, icmd.Success)
19
+}
20
+
21
+// CreateVlanInterface creates a vlan network interface
22
+func CreateVlanInterface(t *testing.T, master, slave, id string) {
23
+	// ip link add link <master> name <master>.<VID> type vlan id <VID>
24
+	icmd.RunCommand("ip", "link", "add", "link", master, "name", slave, "type", "vlan", "id", id).Assert(t, icmd.Success)
25
+	// ip link set <sub_interface_name> up
26
+	icmd.RunCommand("ip", "link", "set", slave, "up").Assert(t, icmd.Success)
27
+}
28
+
29
+// DeleteInterface deletes a network interface
30
+func DeleteInterface(t *testing.T, ifName string) {
31
+	icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
32
+	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
33
+	icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
34
+}
35
+
36
+// LinkExists verifies that a link exists
37
+func LinkExists(t *testing.T, master string) {
38
+	// verify the specified link exists, ip link show <link_name>
39
+	icmd.RunCommand("ip", "link", "show", master).Assert(t, icmd.Success)
40
+}
41
+
42
+// IsNetworkAvailable provides a comparison to check if a docker network is available
43
+func IsNetworkAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
44
+	return func() cmp.Result {
45
+		networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
46
+		if err != nil {
47
+			return cmp.ResultFromError(err)
48
+		}
49
+		for _, network := range networks {
50
+			if network.Name == name {
51
+				return cmp.ResultSuccess
52
+			}
53
+		}
54
+		return cmp.ResultFailure(fmt.Sprintf("could not find network %s", name))
55
+	}
56
+}
57
+
58
+// IsNetworkNotAvailable provides a comparison to check if a docker network is not available
59
+func IsNetworkNotAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
60
+	return func() cmp.Result {
61
+		networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
62
+		if err != nil {
63
+			return cmp.ResultFromError(err)
64
+		}
65
+		for _, network := range networks {
66
+			if network.Name == name {
67
+				return cmp.ResultFailure(fmt.Sprintf("network %s is still present", name))
68
+			}
69
+		}
70
+		return cmp.ResultSuccess
71
+	}
72
+}
73
+
74
+// CheckKernelMajorVersionGreaterOrEqualThen returns whether the kernel version is greater or equal than the one provided
75
+func CheckKernelMajorVersionGreaterOrEqualThen(kernelVersion int, majorVersion int) bool {
76
+	kv, err := kernel.GetKernelVersion()
77
+	if err != nil {
78
+		return false
79
+	}
80
+	if kv.Kernel < kernelVersion || (kv.Kernel == kernelVersion && kv.Major < majorVersion) {
81
+		return false
82
+	}
83
+	return true
84
+}
0 85
new file mode 100644
... ...
@@ -0,0 +1,541 @@
0
+package ipvlan
1
+
2
+import (
3
+	"strings"
4
+	"testing"
5
+	"time"
6
+
7
+	"github.com/docker/docker/api/types"
8
+	"github.com/docker/docker/api/types/network"
9
+	dclient "github.com/docker/docker/client"
10
+	"github.com/docker/docker/integration-cli/daemon"
11
+	"github.com/docker/docker/integration/internal/container"
12
+	n "github.com/docker/docker/integration/network"
13
+	"github.com/gotestyourself/gotestyourself/assert"
14
+	"github.com/gotestyourself/gotestyourself/skip"
15
+	"golang.org/x/net/context"
16
+)
17
+
18
+func TestDockerNetworkIpvlanPersistance(t *testing.T) {
19
+	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
20
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
21
+	skip.If(t, testEnv.IsRemoteDaemon())
22
+	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
23
+
24
+	d := daemon.New(t, "", "dockerd", daemon.Config{
25
+		Experimental: true,
26
+	})
27
+	d.StartWithBusybox(t)
28
+	defer d.Stop(t)
29
+
30
+	// master dummy interface 'di' notation represent 'docker ipvlan'
31
+	master := "di-dummy0"
32
+	n.CreateMasterDummy(t, master)
33
+	defer n.DeleteInterface(t, master)
34
+
35
+	client, err := d.NewClient()
36
+	assert.NilError(t, err)
37
+
38
+	// create a network specifying the desired sub-interface name
39
+	_, err = client.NetworkCreate(context.Background(), "di-persist", types.NetworkCreate{
40
+		Driver: "ipvlan",
41
+		Options: map[string]string{
42
+			"parent": "di-dummy0.70",
43
+		},
44
+	})
45
+	assert.NilError(t, err)
46
+	assert.Check(t, n.IsNetworkAvailable(client, "di-persist"))
47
+	// Restart docker daemon to test the config has persisted to disk
48
+	d.Restart(t)
49
+	assert.Check(t, n.IsNetworkAvailable(client, "di-persist"))
50
+}
51
+
52
+func TestDockerNetworkIpvlan(t *testing.T) {
53
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
54
+	skip.If(t, testEnv.IsRemoteDaemon())
55
+	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
56
+
57
+	for _, tc := range []struct {
58
+		name string
59
+		test func(dclient.APIClient) func(*testing.T)
60
+	}{
61
+		{
62
+			name: "Subinterface",
63
+			test: testIpvlanSubinterface,
64
+		}, {
65
+			name: "OverlapParent",
66
+			test: testIpvlanOverlapParent,
67
+		}, {
68
+			name: "L2NilParent",
69
+			test: testIpvlanL2NilParent,
70
+		}, {
71
+			name: "L2InternalMode",
72
+			test: testIpvlanL2InternalMode,
73
+		}, {
74
+			name: "L3NilParent",
75
+			test: testIpvlanL3NilParent,
76
+		}, {
77
+			name: "L3InternalMode",
78
+			test: testIpvlanL3InternalMode,
79
+		}, {
80
+			name: "L2MultiSubnet",
81
+			test: testIpvlanL2MultiSubnet,
82
+		}, {
83
+			name: "L3MultiSubnet",
84
+			test: testIpvlanL3MultiSubnet,
85
+		}, {
86
+			name: "Addressing",
87
+			test: testIpvlanAddressing,
88
+		},
89
+	} {
90
+		d := daemon.New(t, "", "dockerd", daemon.Config{
91
+			Experimental: true,
92
+		})
93
+		d.StartWithBusybox(t)
94
+
95
+		client, err := d.NewClient()
96
+		assert.NilError(t, err)
97
+
98
+		t.Run(tc.name, tc.test(client))
99
+
100
+		d.Stop(t)
101
+		// FIXME(vdemeester) clean network
102
+	}
103
+}
104
+
105
+func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) {
106
+	return func(t *testing.T) {
107
+		master := "di-dummy0"
108
+		n.CreateMasterDummy(t, master)
109
+		defer n.DeleteInterface(t, master)
110
+
111
+		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
112
+			Driver: "ipvlan",
113
+			Options: map[string]string{
114
+				"parent": "di-dummy0.60",
115
+			},
116
+		})
117
+		assert.NilError(t, err)
118
+		assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface"))
119
+
120
+		// delete the network while preserving the parent link
121
+		err = client.NetworkRemove(context.Background(), "di-subinterface")
122
+		assert.NilError(t, err)
123
+
124
+		assert.Check(t, n.IsNetworkNotAvailable(client, "di-subinterface"))
125
+		// verify the network delete did not delete the predefined link
126
+		n.LinkExists(t, "di-dummy0")
127
+	}
128
+}
129
+
130
+func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) {
131
+	return func(t *testing.T) {
132
+		// verify the same parent interface cannot be used if already in use by an existing network
133
+		master := "di-dummy0"
134
+		n.CreateMasterDummy(t, master)
135
+		defer n.DeleteInterface(t, master)
136
+		n.CreateVlanInterface(t, master, "di-dummy0.30", "30")
137
+
138
+		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
139
+			Driver: "ipvlan",
140
+			Options: map[string]string{
141
+				"parent": "di-dummy0.30",
142
+			},
143
+		})
144
+		assert.NilError(t, err)
145
+		assert.Check(t, n.IsNetworkAvailable(client, "di-subinterface"))
146
+
147
+		_, err = client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
148
+			Driver: "ipvlan",
149
+			Options: map[string]string{
150
+				"parent": "di-dummy0.30",
151
+			},
152
+		})
153
+		// verify that the overlap returns an error
154
+		assert.Check(t, err != nil)
155
+	}
156
+}
157
+
158
+func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
159
+	return func(t *testing.T) {
160
+		// ipvlan l2 mode - dummy parent interface is provisioned dynamically
161
+		_, err := client.NetworkCreate(context.Background(), "di-nil-parent", types.NetworkCreate{
162
+			Driver: "ipvlan",
163
+		})
164
+		assert.NilError(t, err)
165
+		assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent"))
166
+
167
+		ctx := context.Background()
168
+		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
169
+		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
170
+
171
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
172
+		assert.NilError(t, err)
173
+	}
174
+}
175
+
176
+func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
177
+	return func(t *testing.T) {
178
+		_, err := client.NetworkCreate(context.Background(), "di-internal", types.NetworkCreate{
179
+			Driver:   "ipvlan",
180
+			Internal: true,
181
+		})
182
+		assert.NilError(t, err)
183
+		assert.Check(t, n.IsNetworkAvailable(client, "di-internal"))
184
+
185
+		ctx := context.Background()
186
+		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
187
+		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
188
+
189
+		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
190
+		defer cancel()
191
+		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
192
+		// FIXME(vdemeester) check the time of error ?
193
+		assert.Check(t, err != nil)
194
+		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
195
+
196
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
197
+		assert.NilError(t, err)
198
+	}
199
+}
200
+
201
+func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
202
+	return func(t *testing.T) {
203
+		_, err := client.NetworkCreate(context.Background(), "di-nil-parent-l3", types.NetworkCreate{
204
+			Driver: "ipvlan",
205
+			Options: map[string]string{
206
+				"ipvlan_mode": "l3",
207
+			},
208
+			IPAM: &network.IPAM{
209
+				Config: []network.IPAMConfig{
210
+					{
211
+						Subnet:     "172.28.230.0/24",
212
+						AuxAddress: map[string]string{},
213
+					},
214
+					{
215
+						Subnet:     "172.28.220.0/24",
216
+						AuxAddress: map[string]string{},
217
+					},
218
+				},
219
+			},
220
+		})
221
+		assert.NilError(t, err)
222
+		assert.Check(t, n.IsNetworkAvailable(client, "di-nil-parent-l3"))
223
+
224
+		ctx := context.Background()
225
+		id1 := container.Run(t, ctx, client,
226
+			container.WithNetworkMode("di-nil-parent-l3"),
227
+			container.WithIPv4("di-nil-parent-l3", "172.28.220.10"),
228
+		)
229
+		id2 := container.Run(t, ctx, client,
230
+			container.WithNetworkMode("di-nil-parent-l3"),
231
+			container.WithIPv4("di-nil-parent-l3", "172.28.230.10"),
232
+		)
233
+
234
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
235
+		assert.NilError(t, err)
236
+	}
237
+}
238
+
239
+func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
240
+	return func(t *testing.T) {
241
+		_, err := client.NetworkCreate(context.Background(), "di-internal-l3", types.NetworkCreate{
242
+			Driver:   "ipvlan",
243
+			Internal: true,
244
+			Options: map[string]string{
245
+				"ipvlan_mode": "l3",
246
+			},
247
+			IPAM: &network.IPAM{
248
+				Config: []network.IPAMConfig{
249
+					{
250
+						Subnet:     "172.28.230.0/24",
251
+						AuxAddress: map[string]string{},
252
+					},
253
+					{
254
+						Subnet:     "172.28.220.0/24",
255
+						AuxAddress: map[string]string{},
256
+					},
257
+				},
258
+			},
259
+		})
260
+		assert.NilError(t, err)
261
+		assert.Check(t, n.IsNetworkAvailable(client, "di-internal-l3"))
262
+
263
+		ctx := context.Background()
264
+		id1 := container.Run(t, ctx, client,
265
+			container.WithNetworkMode("di-internal-l3"),
266
+			container.WithIPv4("di-internal-l3", "172.28.220.10"),
267
+		)
268
+		id2 := container.Run(t, ctx, client,
269
+			container.WithNetworkMode("di-internal-l3"),
270
+			container.WithIPv4("di-internal-l3", "172.28.230.10"),
271
+		)
272
+
273
+		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
274
+		defer cancel()
275
+		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
276
+		// FIXME(vdemeester) check the time of error ?
277
+		assert.Check(t, err != nil)
278
+		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
279
+
280
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
281
+		assert.NilError(t, err)
282
+	}
283
+}
284
+
285
+func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
286
+	return func(t *testing.T) {
287
+		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
288
+			Driver:     "ipvlan",
289
+			EnableIPv6: true,
290
+			IPAM: &network.IPAM{
291
+				Config: []network.IPAMConfig{
292
+					{
293
+						Subnet:     "172.28.200.0/24",
294
+						AuxAddress: map[string]string{},
295
+					},
296
+					{
297
+						Subnet:     "172.28.202.0/24",
298
+						Gateway:    "172.28.202.254",
299
+						AuxAddress: map[string]string{},
300
+					},
301
+					{
302
+						Subnet:     "2001:db8:abc8::/64",
303
+						AuxAddress: map[string]string{},
304
+					},
305
+					{
306
+						Subnet:     "2001:db8:abc6::/64",
307
+						Gateway:    "2001:db8:abc6::254",
308
+						AuxAddress: map[string]string{},
309
+					},
310
+				},
311
+			},
312
+		})
313
+		assert.NilError(t, err)
314
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2"))
315
+
316
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
317
+		ctx := context.Background()
318
+		id1 := container.Run(t, ctx, client,
319
+			container.WithNetworkMode("dualstackl2"),
320
+			container.WithIPv4("dualstackl2", "172.28.200.20"),
321
+			container.WithIPv6("dualstackl2", "2001:db8:abc8::20"),
322
+		)
323
+		id2 := container.Run(t, ctx, client,
324
+			container.WithNetworkMode("dualstackl2"),
325
+			container.WithIPv4("dualstackl2", "172.28.200.21"),
326
+			container.WithIPv6("dualstackl2", "2001:db8:abc8::21"),
327
+		)
328
+		c1, err := client.ContainerInspect(ctx, id1)
329
+		assert.NilError(t, err)
330
+
331
+		// verify ipv4 connectivity to the explicit --ipv address second to first
332
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].IPAddress})
333
+		assert.NilError(t, err)
334
+		// verify ipv6 connectivity to the explicit --ipv6 address second to first
335
+		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
336
+		assert.NilError(t, err)
337
+
338
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
339
+		id3 := container.Run(t, ctx, client,
340
+			container.WithNetworkMode("dualstackl2"),
341
+			container.WithIPv4("dualstackl2", "172.28.202.20"),
342
+			container.WithIPv6("dualstackl2", "2001:db8:abc6::20"),
343
+		)
344
+		id4 := container.Run(t, ctx, client,
345
+			container.WithNetworkMode("dualstackl2"),
346
+			container.WithIPv4("dualstackl2", "172.28.202.21"),
347
+			container.WithIPv6("dualstackl2", "2001:db8:abc6::21"),
348
+		)
349
+		c3, err := client.ContainerInspect(ctx, id3)
350
+		assert.NilError(t, err)
351
+
352
+		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
353
+		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].IPAddress})
354
+		assert.NilError(t, err)
355
+		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
356
+		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
357
+		assert.NilError(t, err)
358
+
359
+		// Inspect the v4 gateway to ensure the proper default GW was assigned
360
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.200.1")
361
+		// Inspect the v6 gateway to ensure the proper default GW was assigned
362
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc8::1")
363
+		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
364
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.202.254")
365
+		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
366
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc6::254")
367
+	}
368
+}
369
+
370
+func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
371
+	return func(t *testing.T) {
372
+		_, err := client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
373
+			Driver:     "ipvlan",
374
+			EnableIPv6: true,
375
+			Options: map[string]string{
376
+				"ipvlan_mode": "l3",
377
+			},
378
+			IPAM: &network.IPAM{
379
+				Config: []network.IPAMConfig{
380
+					{
381
+						Subnet:     "172.28.10.0/24",
382
+						AuxAddress: map[string]string{},
383
+					},
384
+					{
385
+						Subnet:     "172.28.12.0/24",
386
+						Gateway:    "172.28.12.254",
387
+						AuxAddress: map[string]string{},
388
+					},
389
+					{
390
+						Subnet:     "2001:db8:abc9::/64",
391
+						AuxAddress: map[string]string{},
392
+					},
393
+					{
394
+						Subnet:     "2001:db8:abc7::/64",
395
+						Gateway:    "2001:db8:abc7::254",
396
+						AuxAddress: map[string]string{},
397
+					},
398
+				},
399
+			},
400
+		})
401
+		assert.NilError(t, err)
402
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3"))
403
+
404
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
405
+		ctx := context.Background()
406
+		id1 := container.Run(t, ctx, client,
407
+			container.WithNetworkMode("dualstackl3"),
408
+			container.WithIPv4("dualstackl3", "172.28.10.20"),
409
+			container.WithIPv6("dualstackl3", "2001:db8:abc9::20"),
410
+		)
411
+		id2 := container.Run(t, ctx, client,
412
+			container.WithNetworkMode("dualstackl3"),
413
+			container.WithIPv4("dualstackl3", "172.28.10.21"),
414
+			container.WithIPv6("dualstackl3", "2001:db8:abc9::21"),
415
+		)
416
+		c1, err := client.ContainerInspect(ctx, id1)
417
+		assert.NilError(t, err)
418
+
419
+		// verify ipv4 connectivity to the explicit --ipv address second to first
420
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].IPAddress})
421
+		assert.NilError(t, err)
422
+		// verify ipv6 connectivity to the explicit --ipv6 address second to first
423
+		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
424
+		assert.NilError(t, err)
425
+
426
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
427
+		id3 := container.Run(t, ctx, client,
428
+			container.WithNetworkMode("dualstackl3"),
429
+			container.WithIPv4("dualstackl3", "172.28.12.20"),
430
+			container.WithIPv6("dualstackl3", "2001:db8:abc7::20"),
431
+		)
432
+		id4 := container.Run(t, ctx, client,
433
+			container.WithNetworkMode("dualstackl3"),
434
+			container.WithIPv4("dualstackl3", "172.28.12.21"),
435
+			container.WithIPv6("dualstackl3", "2001:db8:abc7::21"),
436
+		)
437
+		c3, err := client.ContainerInspect(ctx, id3)
438
+		assert.NilError(t, err)
439
+
440
+		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
441
+		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].IPAddress})
442
+		assert.NilError(t, err)
443
+		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
444
+		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
445
+		assert.NilError(t, err)
446
+
447
+		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
448
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].Gateway, "")
449
+		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
450
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
451
+		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
452
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].Gateway, "")
453
+		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
454
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
455
+	}
456
+}
457
+
458
+func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
459
+	return func(t *testing.T) {
460
+		// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
461
+		// for either an explicitly set route by the user or inferred via default IPAM
462
+		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
463
+			Driver:     "ipvlan",
464
+			EnableIPv6: true,
465
+			Options: map[string]string{
466
+				"ipvlan_mode": "l2",
467
+			},
468
+			IPAM: &network.IPAM{
469
+				Config: []network.IPAMConfig{
470
+					{
471
+						Subnet:     "172.28.140.0/24",
472
+						Gateway:    "172.28.140.254",
473
+						AuxAddress: map[string]string{},
474
+					},
475
+					{
476
+						Subnet:     "2001:db8:abcb::/64",
477
+						AuxAddress: map[string]string{},
478
+					},
479
+				},
480
+			},
481
+		})
482
+		assert.NilError(t, err)
483
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl2"))
484
+
485
+		ctx := context.Background()
486
+		id1 := container.Run(t, ctx, client,
487
+			container.WithNetworkMode("dualstackl2"),
488
+		)
489
+		// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
490
+		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
491
+		assert.NilError(t, err)
492
+		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
493
+		// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
494
+		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
495
+		assert.NilError(t, err)
496
+		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
497
+
498
+		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
499
+		_, err = client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
500
+			Driver:     "ipvlan",
501
+			EnableIPv6: true,
502
+			Options: map[string]string{
503
+				"ipvlan_mode": "l3",
504
+			},
505
+			IPAM: &network.IPAM{
506
+				Config: []network.IPAMConfig{
507
+					{
508
+						Subnet:     "172.28.160.0/24",
509
+						Gateway:    "172.28.160.254",
510
+						AuxAddress: map[string]string{},
511
+					},
512
+					{
513
+						Subnet:     "2001:db8:abcd::/64",
514
+						Gateway:    "2001:db8:abcd::254",
515
+						AuxAddress: map[string]string{},
516
+					},
517
+				},
518
+			},
519
+		})
520
+		assert.NilError(t, err)
521
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackl3"))
522
+
523
+		id2 := container.Run(t, ctx, client,
524
+			container.WithNetworkMode("dualstackl3"),
525
+		)
526
+		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
527
+		result, err = container.Exec(ctx, client, id2, []string{"ip", "route"})
528
+		assert.NilError(t, err)
529
+		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
530
+		// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
531
+		result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"})
532
+		assert.NilError(t, err)
533
+		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
534
+	}
535
+}
536
+
537
+// ensure Kernel version is >= v4.2 for ipvlan support
538
+func ipvlanKernelSupport() bool {
539
+	return n.CheckKernelMajorVersionGreaterOrEqualThen(4, 2)
540
+}
0 541
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"testing"
6
+
7
+	"github.com/docker/docker/internal/test/environment"
8
+)
9
+
10
+var testEnv *environment.Execution
11
+
12
+func TestMain(m *testing.M) {
13
+	var err error
14
+	testEnv, err = environment.New()
15
+	if err != nil {
16
+		fmt.Println(err)
17
+		os.Exit(1)
18
+	}
19
+	err = environment.EnsureFrozenImagesLinux(testEnv)
20
+	if err != nil {
21
+		fmt.Println(err)
22
+		os.Exit(1)
23
+	}
24
+
25
+	testEnv.Print()
26
+	os.Exit(m.Run())
27
+}
28
+
29
+func setupTest(t *testing.T) func() {
30
+	environment.ProtectAll(t, testEnv)
31
+	return func() { testEnv.Clean(t) }
32
+}
0 33
deleted file mode 100644
... ...
@@ -1,543 +0,0 @@
1
-package network
2
-
3
-import (
4
-	"strings"
5
-	"testing"
6
-	"time"
7
-
8
-	"github.com/docker/docker/api/types"
9
-	"github.com/docker/docker/api/types/network"
10
-	dclient "github.com/docker/docker/client"
11
-	"github.com/docker/docker/integration-cli/daemon"
12
-	"github.com/docker/docker/integration/internal/container"
13
-	"github.com/gotestyourself/gotestyourself/assert"
14
-	"github.com/gotestyourself/gotestyourself/skip"
15
-	"golang.org/x/net/context"
16
-)
17
-
18
-func TestDockerNetworkIpvlanPersistance(t *testing.T) {
19
-	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
20
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
21
-	skip.If(t, testEnv.IsRemoteDaemon())
22
-	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
23
-
24
-	d := daemon.New(t, "", "dockerd", daemon.Config{
25
-		Experimental: true,
26
-	})
27
-	d.StartWithBusybox(t)
28
-	defer d.Stop(t)
29
-
30
-	// master dummy interface 'di' notation represent 'docker ipvlan'
31
-	master := "di-dummy0"
32
-	createMasterDummy(t, master)
33
-	defer deleteInterface(t, master)
34
-
35
-	client, err := d.NewClient()
36
-	assert.NilError(t, err)
37
-
38
-	// create a network specifying the desired sub-interface name
39
-	_, err = client.NetworkCreate(context.Background(), "di-persist", types.NetworkCreate{
40
-		Driver: "ipvlan",
41
-		Options: map[string]string{
42
-			"parent": "di-dummy0.70",
43
-		},
44
-	})
45
-	assert.NilError(t, err)
46
-	assert.Check(t, isNetworkAvailable(client, "di-persist"))
47
-	// Restart docker daemon to test the config has persisted to disk
48
-	d.Restart(t)
49
-	assert.Check(t, isNetworkAvailable(client, "di-persist"))
50
-}
51
-
52
-func TestDockerNetworkIpvlan(t *testing.T) {
53
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
54
-	skip.If(t, testEnv.IsRemoteDaemon())
55
-	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
56
-
57
-	for _, tc := range []struct {
58
-		name string
59
-		test func(dclient.APIClient) func(*testing.T)
60
-	}{
61
-		{
62
-			name: "Subinterface",
63
-			test: testIpvlanSubinterface,
64
-		}, {
65
-			name: "OverlapParent",
66
-			test: testIpvlanOverlapParent,
67
-		}, {
68
-			name: "L2NilParent",
69
-			test: testIpvlanL2NilParent,
70
-		}, {
71
-			name: "L2InternalMode",
72
-			test: testIpvlanL2InternalMode,
73
-		}, {
74
-			name: "L3NilParent",
75
-			test: testIpvlanL3NilParent,
76
-		}, {
77
-			name: "L3InternalMode",
78
-			test: testIpvlanL3InternalMode,
79
-		}, {
80
-			name: "L2MultiSubnet",
81
-			test: testIpvlanL2MultiSubnet,
82
-		}, {
83
-			name: "L3MultiSubnet",
84
-			test: testIpvlanL3MultiSubnet,
85
-		}, {
86
-			name: "Addressing",
87
-			test: testIpvlanAddressing,
88
-		},
89
-	} {
90
-		d := daemon.New(t, "", "dockerd", daemon.Config{
91
-			Experimental: true,
92
-		})
93
-		d.StartWithBusybox(t)
94
-
95
-		client, err := d.NewClient()
96
-		assert.NilError(t, err)
97
-
98
-		t.Run(tc.name, tc.test(client))
99
-
100
-		d.Stop(t)
101
-		// FIXME(vdemeester) clean network
102
-	}
103
-}
104
-
105
-func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) {
106
-	return func(t *testing.T) {
107
-		master := "di-dummy0"
108
-		createMasterDummy(t, master)
109
-		defer deleteInterface(t, master)
110
-
111
-		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
112
-			Driver: "ipvlan",
113
-			Options: map[string]string{
114
-				"parent": "di-dummy0.60",
115
-			},
116
-		})
117
-		assert.NilError(t, err)
118
-		assert.Check(t, isNetworkAvailable(client, "di-subinterface"))
119
-
120
-		// delete the network while preserving the parent link
121
-		err = client.NetworkRemove(context.Background(), "di-subinterface")
122
-		assert.NilError(t, err)
123
-
124
-		assert.Check(t, isNetworkNotAvailable(client, "di-subinterface"))
125
-		// verify the network delete did not delete the predefined link
126
-		linkExists(t, "di-dummy0")
127
-	}
128
-}
129
-
130
-func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) {
131
-	return func(t *testing.T) {
132
-		// verify the same parent interface cannot be used if already in use by an existing network
133
-		master := "di-dummy0"
134
-		createMasterDummy(t, master)
135
-		defer deleteInterface(t, master)
136
-		createVlanInterface(t, master, "di-dummy0.30", "30")
137
-
138
-		_, err := client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
139
-			Driver: "ipvlan",
140
-			Options: map[string]string{
141
-				"parent": "di-dummy0.30",
142
-			},
143
-		})
144
-		assert.NilError(t, err)
145
-		assert.Check(t, isNetworkAvailable(client, "di-subinterface"))
146
-
147
-		_, err = client.NetworkCreate(context.Background(), "di-subinterface", types.NetworkCreate{
148
-			Driver: "ipvlan",
149
-			Options: map[string]string{
150
-				"parent": "di-dummy0.30",
151
-			},
152
-		})
153
-		// verify that the overlap returns an error
154
-		assert.Check(t, err != nil)
155
-	}
156
-}
157
-
158
-func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
159
-	return func(t *testing.T) {
160
-		// ipvlan l2 mode - dummy parent interface is provisioned dynamically
161
-		_, err := client.NetworkCreate(context.Background(), "di-nil-parent", types.NetworkCreate{
162
-			Driver: "ipvlan",
163
-		})
164
-		assert.NilError(t, err)
165
-		assert.Check(t, isNetworkAvailable(client, "di-nil-parent"))
166
-
167
-		ctx := context.Background()
168
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
169
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-nil-parent"))
170
-
171
-		t.Log(time.Now())
172
-
173
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
174
-		assert.NilError(t, err)
175
-		t.Log(time.Now())
176
-	}
177
-}
178
-
179
-func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
180
-	return func(t *testing.T) {
181
-		_, err := client.NetworkCreate(context.Background(), "di-internal", types.NetworkCreate{
182
-			Driver:   "ipvlan",
183
-			Internal: true,
184
-		})
185
-		assert.NilError(t, err)
186
-		assert.Check(t, isNetworkAvailable(client, "di-internal"))
187
-
188
-		ctx := context.Background()
189
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
190
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode("di-internal"))
191
-
192
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
193
-		defer cancel()
194
-		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
195
-		// FIXME(vdemeester) check the time of error ?
196
-		assert.Check(t, err != nil)
197
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
198
-
199
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
200
-		assert.NilError(t, err)
201
-	}
202
-}
203
-
204
-func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
205
-	return func(t *testing.T) {
206
-		_, err := client.NetworkCreate(context.Background(), "di-nil-parent-l3", types.NetworkCreate{
207
-			Driver: "ipvlan",
208
-			Options: map[string]string{
209
-				"ipvlan_mode": "l3",
210
-			},
211
-			IPAM: &network.IPAM{
212
-				Config: []network.IPAMConfig{
213
-					{
214
-						Subnet:     "172.28.230.0/24",
215
-						AuxAddress: map[string]string{},
216
-					},
217
-					{
218
-						Subnet:     "172.28.220.0/24",
219
-						AuxAddress: map[string]string{},
220
-					},
221
-				},
222
-			},
223
-		})
224
-		assert.NilError(t, err)
225
-		assert.Check(t, isNetworkAvailable(client, "di-nil-parent-l3"))
226
-
227
-		ctx := context.Background()
228
-		id1 := container.Run(t, ctx, client,
229
-			container.WithNetworkMode("di-nil-parent-l3"),
230
-			container.WithIPv4("di-nil-parent-l3", "172.28.220.10"),
231
-		)
232
-		id2 := container.Run(t, ctx, client,
233
-			container.WithNetworkMode("di-nil-parent-l3"),
234
-			container.WithIPv4("di-nil-parent-l3", "172.28.230.10"),
235
-		)
236
-
237
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
238
-		assert.NilError(t, err)
239
-	}
240
-}
241
-
242
-func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
243
-	return func(t *testing.T) {
244
-		_, err := client.NetworkCreate(context.Background(), "di-internal-l3", types.NetworkCreate{
245
-			Driver:   "ipvlan",
246
-			Internal: true,
247
-			Options: map[string]string{
248
-				"ipvlan_mode": "l3",
249
-			},
250
-			IPAM: &network.IPAM{
251
-				Config: []network.IPAMConfig{
252
-					{
253
-						Subnet:     "172.28.230.0/24",
254
-						AuxAddress: map[string]string{},
255
-					},
256
-					{
257
-						Subnet:     "172.28.220.0/24",
258
-						AuxAddress: map[string]string{},
259
-					},
260
-				},
261
-			},
262
-		})
263
-		assert.NilError(t, err)
264
-		assert.Check(t, isNetworkAvailable(client, "di-internal-l3"))
265
-
266
-		ctx := context.Background()
267
-		id1 := container.Run(t, ctx, client,
268
-			container.WithNetworkMode("di-internal-l3"),
269
-			container.WithIPv4("di-internal-l3", "172.28.220.10"),
270
-		)
271
-		id2 := container.Run(t, ctx, client,
272
-			container.WithNetworkMode("di-internal-l3"),
273
-			container.WithIPv4("di-internal-l3", "172.28.230.10"),
274
-		)
275
-
276
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
277
-		defer cancel()
278
-		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
279
-		// FIXME(vdemeester) check the time of error ?
280
-		assert.Check(t, err != nil)
281
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
282
-
283
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
284
-		assert.NilError(t, err)
285
-	}
286
-}
287
-
288
-func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
289
-	return func(t *testing.T) {
290
-		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
291
-			Driver:     "ipvlan",
292
-			EnableIPv6: true,
293
-			IPAM: &network.IPAM{
294
-				Config: []network.IPAMConfig{
295
-					{
296
-						Subnet:     "172.28.200.0/24",
297
-						AuxAddress: map[string]string{},
298
-					},
299
-					{
300
-						Subnet:     "172.28.202.0/24",
301
-						Gateway:    "172.28.202.254",
302
-						AuxAddress: map[string]string{},
303
-					},
304
-					{
305
-						Subnet:     "2001:db8:abc8::/64",
306
-						AuxAddress: map[string]string{},
307
-					},
308
-					{
309
-						Subnet:     "2001:db8:abc6::/64",
310
-						Gateway:    "2001:db8:abc6::254",
311
-						AuxAddress: map[string]string{},
312
-					},
313
-				},
314
-			},
315
-		})
316
-		assert.NilError(t, err)
317
-		assert.Check(t, isNetworkAvailable(client, "dualstackl2"))
318
-
319
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
320
-		ctx := context.Background()
321
-		id1 := container.Run(t, ctx, client,
322
-			container.WithNetworkMode("dualstackl2"),
323
-			container.WithIPv4("dualstackl2", "172.28.200.20"),
324
-			container.WithIPv6("dualstackl2", "2001:db8:abc8::20"),
325
-		)
326
-		id2 := container.Run(t, ctx, client,
327
-			container.WithNetworkMode("dualstackl2"),
328
-			container.WithIPv4("dualstackl2", "172.28.200.21"),
329
-			container.WithIPv6("dualstackl2", "2001:db8:abc8::21"),
330
-		)
331
-		c1, err := client.ContainerInspect(ctx, id1)
332
-		assert.NilError(t, err)
333
-
334
-		// verify ipv4 connectivity to the explicit --ipv address second to first
335
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].IPAddress})
336
-		assert.NilError(t, err)
337
-		// verify ipv6 connectivity to the explicit --ipv6 address second to first
338
-		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
339
-		assert.NilError(t, err)
340
-
341
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
342
-		id3 := container.Run(t, ctx, client,
343
-			container.WithNetworkMode("dualstackl2"),
344
-			container.WithIPv4("dualstackl2", "172.28.202.20"),
345
-			container.WithIPv6("dualstackl2", "2001:db8:abc6::20"),
346
-		)
347
-		id4 := container.Run(t, ctx, client,
348
-			container.WithNetworkMode("dualstackl2"),
349
-			container.WithIPv4("dualstackl2", "172.28.202.21"),
350
-			container.WithIPv6("dualstackl2", "2001:db8:abc6::21"),
351
-		)
352
-		c3, err := client.ContainerInspect(ctx, id3)
353
-		assert.NilError(t, err)
354
-
355
-		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
356
-		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].IPAddress})
357
-		assert.NilError(t, err)
358
-		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
359
-		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl2"].GlobalIPv6Address})
360
-		assert.NilError(t, err)
361
-
362
-		// Inspect the v4 gateway to ensure the proper default GW was assigned
363
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.200.1")
364
-		// Inspect the v6 gateway to ensure the proper default GW was assigned
365
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc8::1")
366
-		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
367
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].Gateway, "172.28.202.254")
368
-		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
369
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl2"].IPv6Gateway, "2001:db8:abc6::254")
370
-	}
371
-}
372
-
373
-func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
374
-	return func(t *testing.T) {
375
-		_, err := client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
376
-			Driver:     "ipvlan",
377
-			EnableIPv6: true,
378
-			Options: map[string]string{
379
-				"ipvlan_mode": "l3",
380
-			},
381
-			IPAM: &network.IPAM{
382
-				Config: []network.IPAMConfig{
383
-					{
384
-						Subnet:     "172.28.10.0/24",
385
-						AuxAddress: map[string]string{},
386
-					},
387
-					{
388
-						Subnet:     "172.28.12.0/24",
389
-						Gateway:    "172.28.12.254",
390
-						AuxAddress: map[string]string{},
391
-					},
392
-					{
393
-						Subnet:     "2001:db8:abc9::/64",
394
-						AuxAddress: map[string]string{},
395
-					},
396
-					{
397
-						Subnet:     "2001:db8:abc7::/64",
398
-						Gateway:    "2001:db8:abc7::254",
399
-						AuxAddress: map[string]string{},
400
-					},
401
-				},
402
-			},
403
-		})
404
-		assert.NilError(t, err)
405
-		assert.Check(t, isNetworkAvailable(client, "dualstackl3"))
406
-
407
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
408
-		ctx := context.Background()
409
-		id1 := container.Run(t, ctx, client,
410
-			container.WithNetworkMode("dualstackl3"),
411
-			container.WithIPv4("dualstackl3", "172.28.10.20"),
412
-			container.WithIPv6("dualstackl3", "2001:db8:abc9::20"),
413
-		)
414
-		id2 := container.Run(t, ctx, client,
415
-			container.WithNetworkMode("dualstackl3"),
416
-			container.WithIPv4("dualstackl3", "172.28.10.21"),
417
-			container.WithIPv6("dualstackl3", "2001:db8:abc9::21"),
418
-		)
419
-		c1, err := client.ContainerInspect(ctx, id1)
420
-		assert.NilError(t, err)
421
-
422
-		// verify ipv4 connectivity to the explicit --ipv address second to first
423
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].IPAddress})
424
-		assert.NilError(t, err)
425
-		// verify ipv6 connectivity to the explicit --ipv6 address second to first
426
-		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
427
-		assert.NilError(t, err)
428
-
429
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
430
-		id3 := container.Run(t, ctx, client,
431
-			container.WithNetworkMode("dualstackl3"),
432
-			container.WithIPv4("dualstackl3", "172.28.12.20"),
433
-			container.WithIPv6("dualstackl3", "2001:db8:abc7::20"),
434
-		)
435
-		id4 := container.Run(t, ctx, client,
436
-			container.WithNetworkMode("dualstackl3"),
437
-			container.WithIPv4("dualstackl3", "172.28.12.21"),
438
-			container.WithIPv6("dualstackl3", "2001:db8:abc7::21"),
439
-		)
440
-		c3, err := client.ContainerInspect(ctx, id3)
441
-		assert.NilError(t, err)
442
-
443
-		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
444
-		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].IPAddress})
445
-		assert.NilError(t, err)
446
-		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
447
-		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackl3"].GlobalIPv6Address})
448
-		assert.NilError(t, err)
449
-
450
-		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
451
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].Gateway, "")
452
-		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
453
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
454
-		// Inspect the v4 gateway to ensure no next hop is assigned in L3 mode
455
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].Gateway, "")
456
-		// Inspect the v6 gateway to ensure the explicitly specified default GW is ignored per L3 mode enabled
457
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackl3"].IPv6Gateway, "")
458
-	}
459
-}
460
-
461
-func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
462
-	return func(t *testing.T) {
463
-		// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
464
-		// for either an explicitly set route by the user or inferred via default IPAM
465
-		_, err := client.NetworkCreate(context.Background(), "dualstackl2", types.NetworkCreate{
466
-			Driver:     "ipvlan",
467
-			EnableIPv6: true,
468
-			Options: map[string]string{
469
-				"ipvlan_mode": "l2",
470
-			},
471
-			IPAM: &network.IPAM{
472
-				Config: []network.IPAMConfig{
473
-					{
474
-						Subnet:     "172.28.140.0/24",
475
-						Gateway:    "172.28.140.254",
476
-						AuxAddress: map[string]string{},
477
-					},
478
-					{
479
-						Subnet:     "2001:db8:abcb::/64",
480
-						AuxAddress: map[string]string{},
481
-					},
482
-				},
483
-			},
484
-		})
485
-		assert.NilError(t, err)
486
-		assert.Check(t, isNetworkAvailable(client, "dualstackl2"))
487
-
488
-		ctx := context.Background()
489
-		id1 := container.Run(t, ctx, client,
490
-			container.WithNetworkMode("dualstackl2"),
491
-		)
492
-		// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
493
-		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
494
-		assert.NilError(t, err)
495
-		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.140.254 dev eth0"))
496
-		// Validate ipvlan l2 mode sets the v6 gateway to the user specified default gateway/next-hop
497
-		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
498
-		assert.NilError(t, err)
499
-		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abcb::1 dev eth0"))
500
-
501
-		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
502
-		_, err = client.NetworkCreate(context.Background(), "dualstackl3", types.NetworkCreate{
503
-			Driver:     "ipvlan",
504
-			EnableIPv6: true,
505
-			Options: map[string]string{
506
-				"ipvlan_mode": "l3",
507
-			},
508
-			IPAM: &network.IPAM{
509
-				Config: []network.IPAMConfig{
510
-					{
511
-						Subnet:     "172.28.160.0/24",
512
-						Gateway:    "172.28.160.254",
513
-						AuxAddress: map[string]string{},
514
-					},
515
-					{
516
-						Subnet:     "2001:db8:abcd::/64",
517
-						Gateway:    "2001:db8:abcd::254",
518
-						AuxAddress: map[string]string{},
519
-					},
520
-				},
521
-			},
522
-		})
523
-		assert.NilError(t, err)
524
-		assert.Check(t, isNetworkAvailable(client, "dualstackl3"))
525
-
526
-		id2 := container.Run(t, ctx, client,
527
-			container.WithNetworkMode("dualstackl3"),
528
-		)
529
-		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
530
-		result, err = container.Exec(ctx, client, id2, []string{"ip", "route"})
531
-		assert.NilError(t, err)
532
-		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
533
-		// Validate ipvlan l3 mode sets the v6 gateway to dev eth0 and disregards any explicit or inferred next-hops
534
-		result, err = container.Exec(ctx, client, id2, []string{"ip", "-6", "route"})
535
-		assert.NilError(t, err)
536
-		assert.Check(t, strings.Contains(result.Combined(), "default dev eth0"))
537
-	}
538
-}
539
-
540
-// ensure Kernel version is >= v4.2 for ipvlan support
541
-func ipvlanKernelSupport() bool {
542
-	return checkKernelMajorVersionGreaterOrEqualThen(4, 2)
543
-}
544 1
new file mode 100644
... ...
@@ -0,0 +1,321 @@
0
+package macvlan
1
+
2
+import (
3
+	"context"
4
+	"strings"
5
+	"testing"
6
+	"time"
7
+
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/api/types/network"
10
+	"github.com/docker/docker/client"
11
+	"github.com/docker/docker/integration/internal/container"
12
+	n "github.com/docker/docker/integration/network"
13
+	"github.com/docker/docker/internal/test/daemon"
14
+	"github.com/gotestyourself/gotestyourself/assert"
15
+	"github.com/gotestyourself/gotestyourself/skip"
16
+)
17
+
18
+func TestDockerNetworkMacvlanPersistance(t *testing.T) {
19
+	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
20
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
21
+	skip.If(t, testEnv.IsRemoteDaemon())
22
+	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
23
+
24
+	d := daemon.New(t)
25
+	d.StartWithBusybox(t)
26
+	defer d.Stop(t)
27
+
28
+	master := "dm-dummy0"
29
+	n.CreateMasterDummy(t, master)
30
+	defer n.DeleteInterface(t, master)
31
+
32
+	client, err := d.NewClient()
33
+	assert.NilError(t, err)
34
+
35
+	_, err = client.NetworkCreate(context.Background(), "dm-persist", types.NetworkCreate{
36
+		Driver: "macvlan",
37
+		Options: map[string]string{
38
+			"parent": "dm-dummy0.60",
39
+		},
40
+	})
41
+	assert.NilError(t, err)
42
+	assert.Check(t, n.IsNetworkAvailable(client, "dm-persist"))
43
+	d.Restart(t)
44
+	assert.Check(t, n.IsNetworkAvailable(client, "dm-persist"))
45
+}
46
+
47
+func TestDockerNetworkMacvlan(t *testing.T) {
48
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
49
+	skip.If(t, testEnv.IsRemoteDaemon())
50
+	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
51
+
52
+	for _, tc := range []struct {
53
+		name string
54
+		test func(client.APIClient) func(*testing.T)
55
+	}{
56
+		{
57
+			name: "Subinterface",
58
+			test: testMacvlanSubinterface,
59
+		}, {
60
+			name: "OverlapParent",
61
+			test: testMacvlanOverlapParent,
62
+		}, {
63
+			name: "NilParent",
64
+			test: testMacvlanNilParent,
65
+		}, {
66
+			name: "InternalMode",
67
+			test: testMacvlanInternalMode,
68
+		}, {
69
+			name: "Addressing",
70
+			test: testMacvlanAddressing,
71
+		},
72
+	} {
73
+		d := daemon.New(t)
74
+		d.StartWithBusybox(t)
75
+
76
+		client, err := d.NewClient()
77
+		assert.NilError(t, err)
78
+
79
+		t.Run(tc.name, tc.test(client))
80
+
81
+		d.Stop(t)
82
+		// FIXME(vdemeester) clean network
83
+	}
84
+}
85
+
86
+func testMacvlanOverlapParent(client client.APIClient) func(*testing.T) {
87
+	return func(t *testing.T) {
88
+		// verify the same parent interface cannot be used if already in use by an existing network
89
+		master := "dm-dummy0"
90
+		n.CreateMasterDummy(t, master)
91
+		defer n.DeleteInterface(t, master)
92
+
93
+		_, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
94
+			Driver: "macvlan",
95
+			Options: map[string]string{
96
+				"parent": "dm-dummy0.40",
97
+			},
98
+		})
99
+		assert.NilError(t, err)
100
+		assert.Check(t, n.IsNetworkAvailable(client, "dm-subinterface"))
101
+
102
+		_, err = client.NetworkCreate(context.Background(), "dm-parent-net-overlap", types.NetworkCreate{
103
+			Driver: "macvlan",
104
+			Options: map[string]string{
105
+				"parent": "dm-dummy0.40",
106
+			},
107
+		})
108
+		assert.Check(t, err != nil)
109
+		// delete the network while preserving the parent link
110
+		err = client.NetworkRemove(context.Background(), "dm-subinterface")
111
+		assert.NilError(t, err)
112
+
113
+		assert.Check(t, n.IsNetworkNotAvailable(client, "dm-subinterface"))
114
+		// verify the network delete did not delete the predefined link
115
+		n.LinkExists(t, "dm-dummy0")
116
+	}
117
+}
118
+
119
+func testMacvlanSubinterface(client client.APIClient) func(*testing.T) {
120
+	return func(t *testing.T) {
121
+		// verify the same parent interface cannot be used if already in use by an existing network
122
+		master := "dm-dummy0"
123
+		n.CreateMasterDummy(t, master)
124
+		defer n.DeleteInterface(t, master)
125
+		n.CreateVlanInterface(t, master, "dm-dummy0.20", "20")
126
+
127
+		_, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
128
+			Driver: "macvlan",
129
+			Options: map[string]string{
130
+				"parent": "dm-dummy0.20",
131
+			},
132
+		})
133
+		assert.NilError(t, err)
134
+		assert.Check(t, n.IsNetworkAvailable(client, "dm-subinterface"))
135
+
136
+		// delete the network while preserving the parent link
137
+		err = client.NetworkRemove(context.Background(), "dm-subinterface")
138
+		assert.NilError(t, err)
139
+
140
+		assert.Check(t, n.IsNetworkNotAvailable(client, "dm-subinterface"))
141
+		// verify the network delete did not delete the predefined link
142
+		n.LinkExists(t, "dm-dummy0.20")
143
+	}
144
+}
145
+
146
+func testMacvlanNilParent(client client.APIClient) func(*testing.T) {
147
+	return func(t *testing.T) {
148
+		// macvlan bridge mode - dummy parent interface is provisioned dynamically
149
+		_, err := client.NetworkCreate(context.Background(), "dm-nil-parent", types.NetworkCreate{
150
+			Driver: "macvlan",
151
+		})
152
+		assert.NilError(t, err)
153
+		assert.Check(t, n.IsNetworkAvailable(client, "dm-nil-parent"))
154
+
155
+		ctx := context.Background()
156
+		id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"))
157
+		id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"))
158
+
159
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
160
+		assert.Check(t, err == nil)
161
+	}
162
+}
163
+
164
+func testMacvlanInternalMode(client client.APIClient) func(*testing.T) {
165
+	return func(t *testing.T) {
166
+		// macvlan bridge mode - dummy parent interface is provisioned dynamically
167
+		_, err := client.NetworkCreate(context.Background(), "dm-internal", types.NetworkCreate{
168
+			Driver:   "macvlan",
169
+			Internal: true,
170
+		})
171
+		assert.NilError(t, err)
172
+		assert.Check(t, n.IsNetworkAvailable(client, "dm-internal"))
173
+
174
+		ctx := context.Background()
175
+		id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"))
176
+		id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"))
177
+
178
+		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
179
+		defer cancel()
180
+		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
181
+		// FIXME(vdemeester) check the time of error ?
182
+		assert.Check(t, err != nil)
183
+		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
184
+
185
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
186
+		assert.Check(t, err == nil)
187
+	}
188
+}
189
+
190
+func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
191
+	return func(t *testing.T) {
192
+		_, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{
193
+			Driver:     "macvlan",
194
+			EnableIPv6: true,
195
+			IPAM: &network.IPAM{
196
+				Config: []network.IPAMConfig{
197
+					{
198
+						Subnet:     "172.28.100.0/24",
199
+						AuxAddress: map[string]string{},
200
+					},
201
+					{
202
+						Subnet:     "172.28.102.0/24",
203
+						Gateway:    "172.28.102.254",
204
+						AuxAddress: map[string]string{},
205
+					},
206
+					{
207
+						Subnet:     "2001:db8:abc2::/64",
208
+						AuxAddress: map[string]string{},
209
+					},
210
+					{
211
+						Subnet:     "2001:db8:abc4::/64",
212
+						Gateway:    "2001:db8:abc4::254",
213
+						AuxAddress: map[string]string{},
214
+					},
215
+				},
216
+			},
217
+		})
218
+		assert.NilError(t, err)
219
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackbridge"))
220
+
221
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
222
+		ctx := context.Background()
223
+		id1 := container.Run(t, ctx, client,
224
+			container.WithNetworkMode("dualstackbridge"),
225
+			container.WithIPv4("dualstackbridge", "172.28.100.20"),
226
+			container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
227
+		)
228
+		id2 := container.Run(t, ctx, client,
229
+			container.WithNetworkMode("dualstackbridge"),
230
+			container.WithIPv4("dualstackbridge", "172.28.100.21"),
231
+			container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
232
+		)
233
+		c1, err := client.ContainerInspect(ctx, id1)
234
+		assert.NilError(t, err)
235
+
236
+		// verify ipv4 connectivity to the explicit --ipv address second to first
237
+		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
238
+		assert.NilError(t, err)
239
+		// verify ipv6 connectivity to the explicit --ipv6 address second to first
240
+		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
241
+		assert.NilError(t, err)
242
+
243
+		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
244
+		id3 := container.Run(t, ctx, client,
245
+			container.WithNetworkMode("dualstackbridge"),
246
+			container.WithIPv4("dualstackbridge", "172.28.102.20"),
247
+			container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
248
+		)
249
+		id4 := container.Run(t, ctx, client,
250
+			container.WithNetworkMode("dualstackbridge"),
251
+			container.WithIPv4("dualstackbridge", "172.28.102.21"),
252
+			container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
253
+		)
254
+		c3, err := client.ContainerInspect(ctx, id3)
255
+		assert.NilError(t, err)
256
+
257
+		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
258
+		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
259
+		assert.NilError(t, err)
260
+		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
261
+		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
262
+		assert.NilError(t, err)
263
+
264
+		// Inspect the v4 gateway to ensure the proper default GW was assigned
265
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.100.1")
266
+		// Inspect the v6 gateway to ensure the proper default GW was assigned
267
+		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc2::1")
268
+		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
269
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
270
+		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
271
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8.abc4::254")
272
+	}
273
+}
274
+
275
+func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
276
+	return func(t *testing.T) {
277
+		// Ensure the default gateways, next-hops and default dev devices are properly set
278
+		_, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{
279
+			Driver:     "macvlan",
280
+			EnableIPv6: true,
281
+			Options: map[string]string{
282
+				"macvlan_mode": "bridge",
283
+			},
284
+			IPAM: &network.IPAM{
285
+				Config: []network.IPAMConfig{
286
+					{
287
+						Subnet:     "172.28.130.0/24",
288
+						AuxAddress: map[string]string{},
289
+					},
290
+					{
291
+						Subnet:     "2001:db8:abca::/64",
292
+						Gateway:    "2001:db8:abca::254",
293
+						AuxAddress: map[string]string{},
294
+					},
295
+				},
296
+			},
297
+		})
298
+		assert.NilError(t, err)
299
+		assert.Check(t, n.IsNetworkAvailable(client, "dualstackbridge"))
300
+
301
+		ctx := context.Background()
302
+		id1 := container.Run(t, ctx, client,
303
+			container.WithNetworkMode("dualstackbridge"),
304
+		)
305
+
306
+		// Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
307
+		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
308
+		assert.NilError(t, err)
309
+		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.130.1 dev eth0"))
310
+		// Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
311
+		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
312
+		assert.NilError(t, err)
313
+		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
314
+	}
315
+}
316
+
317
+// ensure Kernel version is >= v3.9 for macvlan support
318
+func macvlanKernelSupport() bool {
319
+	return n.CheckKernelMajorVersionGreaterOrEqualThen(3, 9)
320
+}
0 321
new file mode 100644
... ...
@@ -0,0 +1,33 @@
0
+package macvlan // import "github.com/docker/docker/integration/network/macvlan"
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"testing"
6
+
7
+	"github.com/docker/docker/internal/test/environment"
8
+)
9
+
10
+var testEnv *environment.Execution
11
+
12
+func TestMain(m *testing.M) {
13
+	var err error
14
+	testEnv, err = environment.New()
15
+	if err != nil {
16
+		fmt.Println(err)
17
+		os.Exit(1)
18
+	}
19
+	err = environment.EnsureFrozenImagesLinux(testEnv)
20
+	if err != nil {
21
+		fmt.Println(err)
22
+		os.Exit(1)
23
+	}
24
+
25
+	testEnv.Print()
26
+	os.Exit(m.Run())
27
+}
28
+
29
+func setupTest(t *testing.T) func() {
30
+	environment.ProtectAll(t, testEnv)
31
+	return func() { testEnv.Clean(t) }
32
+}
0 33
deleted file mode 100644
... ...
@@ -1,390 +0,0 @@
1
-package network
2
-
3
-import (
4
-	"context"
5
-	"fmt"
6
-	"strings"
7
-	"testing"
8
-	"time"
9
-
10
-	"github.com/docker/docker/api/types"
11
-	"github.com/docker/docker/api/types/network"
12
-	"github.com/docker/docker/client"
13
-	"github.com/docker/docker/integration/internal/container"
14
-	"github.com/docker/docker/internal/test/daemon"
15
-	"github.com/docker/docker/pkg/parsers/kernel"
16
-	"github.com/gotestyourself/gotestyourself/assert"
17
-	"github.com/gotestyourself/gotestyourself/assert/cmp"
18
-	"github.com/gotestyourself/gotestyourself/icmd"
19
-	"github.com/gotestyourself/gotestyourself/skip"
20
-)
21
-
22
-func TestDockerNetworkMacvlanPersistance(t *testing.T) {
23
-	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
24
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
25
-	skip.If(t, testEnv.IsRemoteDaemon())
26
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
27
-
28
-	d := daemon.New(t)
29
-	d.StartWithBusybox(t)
30
-	defer d.Stop(t)
31
-
32
-	master := "dm-dummy0"
33
-	createMasterDummy(t, master)
34
-	defer deleteInterface(t, master)
35
-
36
-	client, err := d.NewClient()
37
-	assert.NilError(t, err)
38
-
39
-	_, err = client.NetworkCreate(context.Background(), "dm-persist", types.NetworkCreate{
40
-		Driver: "macvlan",
41
-		Options: map[string]string{
42
-			"parent": "dm-dummy0.60",
43
-		},
44
-	})
45
-	assert.NilError(t, err)
46
-	assert.Check(t, isNetworkAvailable(client, "dm-persist"))
47
-	d.Restart(t)
48
-	assert.Check(t, isNetworkAvailable(client, "dm-persist"))
49
-}
50
-
51
-func TestDockerNetworkMacvlan(t *testing.T) {
52
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
53
-	skip.If(t, testEnv.IsRemoteDaemon())
54
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
55
-
56
-	for _, tc := range []struct {
57
-		name string
58
-		test func(client.APIClient) func(*testing.T)
59
-	}{
60
-		{
61
-			name: "Subinterface",
62
-			test: testMacvlanSubinterface,
63
-		}, {
64
-			name: "OverlapParent",
65
-			test: testMacvlanOverlapParent,
66
-		}, {
67
-			name: "NilParent",
68
-			test: testMacvlanNilParent,
69
-		}, {
70
-			name: "InternalMode",
71
-			test: testMacvlanInternalMode,
72
-		}, {
73
-			name: "Addressing",
74
-			test: testMacvlanAddressing,
75
-		},
76
-	} {
77
-		d := daemon.New(t)
78
-		d.StartWithBusybox(t)
79
-
80
-		client, err := d.NewClient()
81
-		assert.NilError(t, err)
82
-
83
-		t.Run(tc.name, tc.test(client))
84
-
85
-		d.Stop(t)
86
-		// FIXME(vdemeester) clean network
87
-	}
88
-}
89
-
90
-func testMacvlanOverlapParent(client client.APIClient) func(*testing.T) {
91
-	return func(t *testing.T) {
92
-		// verify the same parent interface cannot be used if already in use by an existing network
93
-		master := "dm-dummy0"
94
-		createMasterDummy(t, master)
95
-		defer deleteInterface(t, master)
96
-
97
-		_, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
98
-			Driver: "macvlan",
99
-			Options: map[string]string{
100
-				"parent": "dm-dummy0.40",
101
-			},
102
-		})
103
-		assert.NilError(t, err)
104
-		assert.Check(t, isNetworkAvailable(client, "dm-subinterface"))
105
-
106
-		_, err = client.NetworkCreate(context.Background(), "dm-parent-net-overlap", types.NetworkCreate{
107
-			Driver: "macvlan",
108
-			Options: map[string]string{
109
-				"parent": "dm-dummy0.40",
110
-			},
111
-		})
112
-		assert.Check(t, err != nil)
113
-		// delete the network while preserving the parent link
114
-		err = client.NetworkRemove(context.Background(), "dm-subinterface")
115
-		assert.NilError(t, err)
116
-
117
-		assert.Check(t, isNetworkNotAvailable(client, "dm-subinterface"))
118
-		// verify the network delete did not delete the predefined link
119
-		linkExists(t, "dm-dummy0")
120
-	}
121
-}
122
-
123
-func testMacvlanSubinterface(client client.APIClient) func(*testing.T) {
124
-	return func(t *testing.T) {
125
-		// verify the same parent interface cannot be used if already in use by an existing network
126
-		master := "dm-dummy0"
127
-		createMasterDummy(t, master)
128
-		defer deleteInterface(t, master)
129
-		createVlanInterface(t, master, "dm-dummy0.20", "20")
130
-
131
-		_, err := client.NetworkCreate(context.Background(), "dm-subinterface", types.NetworkCreate{
132
-			Driver: "macvlan",
133
-			Options: map[string]string{
134
-				"parent": "dm-dummy0.20",
135
-			},
136
-		})
137
-		assert.NilError(t, err)
138
-		assert.Check(t, isNetworkAvailable(client, "dm-subinterface"))
139
-
140
-		// delete the network while preserving the parent link
141
-		err = client.NetworkRemove(context.Background(), "dm-subinterface")
142
-		assert.NilError(t, err)
143
-
144
-		assert.Check(t, isNetworkNotAvailable(client, "dm-subinterface"))
145
-		// verify the network delete did not delete the predefined link
146
-		linkExists(t, "dm-dummy0.20")
147
-	}
148
-}
149
-
150
-func testMacvlanNilParent(client client.APIClient) func(*testing.T) {
151
-	return func(t *testing.T) {
152
-		// macvlan bridge mode - dummy parent interface is provisioned dynamically
153
-		_, err := client.NetworkCreate(context.Background(), "dm-nil-parent", types.NetworkCreate{
154
-			Driver: "macvlan",
155
-		})
156
-		assert.NilError(t, err)
157
-		assert.Check(t, isNetworkAvailable(client, "dm-nil-parent"))
158
-
159
-		ctx := context.Background()
160
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"))
161
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-nil-parent"))
162
-
163
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
164
-		assert.Check(t, err == nil)
165
-	}
166
-}
167
-
168
-func testMacvlanInternalMode(client client.APIClient) func(*testing.T) {
169
-	return func(t *testing.T) {
170
-		// macvlan bridge mode - dummy parent interface is provisioned dynamically
171
-		_, err := client.NetworkCreate(context.Background(), "dm-internal", types.NetworkCreate{
172
-			Driver:   "macvlan",
173
-			Internal: true,
174
-		})
175
-		assert.NilError(t, err)
176
-		assert.Check(t, isNetworkAvailable(client, "dm-internal"))
177
-
178
-		ctx := context.Background()
179
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"))
180
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode("dm-internal"))
181
-
182
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
183
-		defer cancel()
184
-		_, err = container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
185
-		// FIXME(vdemeester) check the time of error ?
186
-		assert.Check(t, err != nil)
187
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
188
-
189
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
190
-		assert.Check(t, err == nil)
191
-	}
192
-}
193
-
194
-func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
195
-	return func(t *testing.T) {
196
-		// t.Skip("Temporarily skipping while investigating sporadic v6 CI issues")
197
-		_, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{
198
-			Driver:     "macvlan",
199
-			EnableIPv6: true,
200
-			IPAM: &network.IPAM{
201
-				Config: []network.IPAMConfig{
202
-					{
203
-						Subnet:     "172.28.100.0/24",
204
-						AuxAddress: map[string]string{},
205
-					},
206
-					{
207
-						Subnet:     "172.28.102.0/24",
208
-						Gateway:    "172.28.102.254",
209
-						AuxAddress: map[string]string{},
210
-					},
211
-					{
212
-						Subnet:     "2001:db8:abc2::/64",
213
-						AuxAddress: map[string]string{},
214
-					},
215
-					{
216
-						Subnet:     "2001:db8:abc4::/64",
217
-						Gateway:    "2001:db8:abc4::254",
218
-						AuxAddress: map[string]string{},
219
-					},
220
-				},
221
-			},
222
-		})
223
-		assert.NilError(t, err)
224
-		assert.Check(t, isNetworkAvailable(client, "dualstackbridge"))
225
-
226
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
227
-		ctx := context.Background()
228
-		id1 := container.Run(t, ctx, client,
229
-			container.WithNetworkMode("dualstackbridge"),
230
-			container.WithIPv4("dualstackbridge", "172.28.100.20"),
231
-			container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
232
-		)
233
-		id2 := container.Run(t, ctx, client,
234
-			container.WithNetworkMode("dualstackbridge"),
235
-			container.WithIPv4("dualstackbridge", "172.28.100.21"),
236
-			container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
237
-		)
238
-		c1, err := client.ContainerInspect(ctx, id1)
239
-		assert.NilError(t, err)
240
-
241
-		// verify ipv4 connectivity to the explicit --ipv address second to first
242
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].IPAddress})
243
-		assert.NilError(t, err)
244
-		// verify ipv6 connectivity to the explicit --ipv6 address second to first
245
-		_, err = container.Exec(ctx, client, id2, []string{"ping6", "-c", "1", c1.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
246
-		assert.NilError(t, err)
247
-
248
-		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
249
-		id3 := container.Run(t, ctx, client,
250
-			container.WithNetworkMode("dualstackbridge"),
251
-			container.WithIPv4("dualstackbridge", "172.28.102.20"),
252
-			container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
253
-		)
254
-		id4 := container.Run(t, ctx, client,
255
-			container.WithNetworkMode("dualstackbridge"),
256
-			container.WithIPv4("dualstackbridge", "172.28.102.21"),
257
-			container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
258
-		)
259
-		c3, err := client.ContainerInspect(ctx, id3)
260
-		assert.NilError(t, err)
261
-
262
-		// verify ipv4 connectivity to the explicit --ipv address from third to fourth
263
-		_, err = container.Exec(ctx, client, id4, []string{"ping", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].IPAddress})
264
-		assert.NilError(t, err)
265
-		// verify ipv6 connectivity to the explicit --ipv6 address from third to fourth
266
-		_, err = container.Exec(ctx, client, id4, []string{"ping6", "-c", "1", c3.NetworkSettings.Networks["dualstackbridge"].GlobalIPv6Address})
267
-		assert.NilError(t, err)
268
-
269
-		// Inspect the v4 gateway to ensure the proper default GW was assigned
270
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.100.1")
271
-		// Inspect the v6 gateway to ensure the proper default GW was assigned
272
-		assert.Equal(t, c1.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc2::1")
273
-		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
274
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
275
-		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
276
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8.abc4::254")
277
-	}
278
-}
279
-
280
-func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
281
-	return func(t *testing.T) {
282
-		// Ensure the default gateways, next-hops and default dev devices are properly set
283
-		_, err := client.NetworkCreate(context.Background(), "dualstackbridge", types.NetworkCreate{
284
-			Driver:     "macvlan",
285
-			EnableIPv6: true,
286
-			Options: map[string]string{
287
-				"macvlan_mode": "bridge",
288
-			},
289
-			IPAM: &network.IPAM{
290
-				Config: []network.IPAMConfig{
291
-					{
292
-						Subnet:     "172.28.130.0/24",
293
-						AuxAddress: map[string]string{},
294
-					},
295
-					{
296
-						Subnet:     "2001:db8:abca::/64",
297
-						Gateway:    "2001:db8:abca::254",
298
-						AuxAddress: map[string]string{},
299
-					},
300
-				},
301
-			},
302
-		})
303
-		assert.NilError(t, err)
304
-		assert.Check(t, isNetworkAvailable(client, "dualstackbridge"))
305
-
306
-		ctx := context.Background()
307
-		id1 := container.Run(t, ctx, client,
308
-			container.WithNetworkMode("dualstackbridge"),
309
-		)
310
-
311
-		// Validate macvlan bridge mode defaults gateway sets the default IPAM next-hop inferred from the subnet
312
-		result, err := container.Exec(ctx, client, id1, []string{"ip", "route"})
313
-		assert.NilError(t, err)
314
-		assert.Check(t, strings.Contains(result.Combined(), "default via 172.28.130.1 dev eth0"))
315
-		// Validate macvlan bridge mode sets the v6 gateway to the user specified default gateway/next-hop
316
-		result, err = container.Exec(ctx, client, id1, []string{"ip", "-6", "route"})
317
-		assert.NilError(t, err)
318
-		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
319
-	}
320
-}
321
-
322
-func isNetworkAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
323
-	return func() cmp.Result {
324
-		networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
325
-		if err != nil {
326
-			return cmp.ResultFromError(err)
327
-		}
328
-		for _, network := range networks {
329
-			if network.Name == name {
330
-				return cmp.ResultSuccess
331
-			}
332
-		}
333
-		return cmp.ResultFailure(fmt.Sprintf("could not find network %s", name))
334
-	}
335
-}
336
-
337
-func isNetworkNotAvailable(c client.NetworkAPIClient, name string) cmp.Comparison {
338
-	return func() cmp.Result {
339
-		networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
340
-		if err != nil {
341
-			return cmp.ResultFromError(err)
342
-		}
343
-		for _, network := range networks {
344
-			if network.Name == name {
345
-				return cmp.ResultFailure(fmt.Sprintf("network %s is still present", name))
346
-			}
347
-		}
348
-		return cmp.ResultSuccess
349
-	}
350
-}
351
-
352
-func createMasterDummy(t *testing.T, master string) {
353
-	// ip link add <dummy_name> type dummy
354
-	icmd.RunCommand("ip", "link", "add", master, "type", "dummy").Assert(t, icmd.Success)
355
-	icmd.RunCommand("ip", "link", "set", master, "up").Assert(t, icmd.Success)
356
-}
357
-
358
-func createVlanInterface(t *testing.T, master, slave, id string) {
359
-	// ip link add link <master> name <master>.<VID> type vlan id <VID>
360
-	icmd.RunCommand("ip", "link", "add", "link", master, "name", slave, "type", "vlan", "id", id).Assert(t, icmd.Success)
361
-	// ip link set <sub_interface_name> up
362
-	icmd.RunCommand("ip", "link", "set", slave, "up").Assert(t, icmd.Success)
363
-}
364
-
365
-func deleteInterface(t *testing.T, ifName string) {
366
-	icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
367
-	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
368
-	icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
369
-}
370
-
371
-func linkExists(t *testing.T, master string) {
372
-	// verify the specified link exists, ip link show <link_name>
373
-	icmd.RunCommand("ip", "link", "show", master).Assert(t, icmd.Success)
374
-}
375
-
376
-// ensure Kernel version is >= v3.9 for macvlan support
377
-func macvlanKernelSupport() bool {
378
-	return checkKernelMajorVersionGreaterOrEqualThen(3, 9)
379
-}
380
-
381
-func checkKernelMajorVersionGreaterOrEqualThen(kernelVersion int, majorVersion int) bool {
382
-	kv, err := kernel.GetKernelVersion()
383
-	if err != nil {
384
-		return false
385
-	}
386
-	if kv.Kernel < kernelVersion || (kv.Kernel == kernelVersion && kv.Major < majorVersion) {
387
-		return false
388
-	}
389
-	return true
390
-}