Skip to content

Commit 6d2a7ad

Browse files
committed
add MAC, IPv4, IPv6 addresses to nework inspect
Signed-off-by: Arjun Raja Yogidas <arjunry@amazon.com>
1 parent d5bc0f0 commit 6d2a7ad

File tree

2 files changed

+330
-53
lines changed

2 files changed

+330
-53
lines changed

cmd/nerdctl/network/network_inspect_test.go

Lines changed: 231 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package network
1919
import (
2020
"encoding/json"
2121
"errors"
22+
"net"
2223
"os/exec"
2324
"runtime"
2425
"strings"
@@ -37,7 +38,7 @@ import (
3738
"github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
3839
)
3940

40-
func TestNetworkInspect(t *testing.T) {
41+
func TestNetworkInspectBasic(t *testing.T) {
4142
testCase := nerdtest.Setup()
4243

4344
const (
@@ -46,15 +47,6 @@ func TestNetworkInspect(t *testing.T) {
4647
testIPRange = "10.24.24.0/25"
4748
)
4849

49-
testCase.Setup = func(data test.Data, helpers test.Helpers) {
50-
helpers.Ensure("network", "create", data.Identifier("basenet"))
51-
data.Labels().Set("basenet", data.Identifier("basenet"))
52-
}
53-
54-
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
55-
helpers.Anyhow("network", "rm", data.Identifier("basenet"))
56-
}
57-
5850
testCase.SubTests = []*test.Case{
5951
{
6052
Description: "non existent network",
@@ -133,6 +125,59 @@ func TestNetworkInspect(t *testing.T) {
133125
assert.Equal(t, dc[0].Name, "custom")
134126
}),
135127
},
128+
{
129+
Description: "basic",
130+
// FIXME: IPAMConfig is not implemented on Windows yet
131+
Require: require.Not(require.Windows),
132+
Setup: func(data test.Data, helpers test.Helpers) {
133+
helpers.Ensure("network", "create", "--label", "tag=testNetwork", "--subnet", testSubnet,
134+
"--gateway", testGateway, "--ip-range", testIPRange, data.Identifier())
135+
},
136+
Cleanup: func(data test.Data, helpers test.Helpers) {
137+
helpers.Anyhow("network", "rm", data.Identifier())
138+
},
139+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
140+
return helpers.Command("network", "inspect", data.Identifier())
141+
},
142+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
143+
return &test.Expected{
144+
ExitCode: 0,
145+
Output: func(stdout string, t tig.T) {
146+
var dc []dockercompat.Network
147+
148+
err := json.Unmarshal([]byte(stdout), &dc)
149+
assert.NilError(t, err, "Unable to unmarshal output\n")
150+
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
151+
got := dc[0]
152+
153+
assert.Equal(t, got.Name, data.Identifier())
154+
assert.Equal(t, got.Labels["tag"], "testNetwork")
155+
assert.Equal(t, len(got.IPAM.Config), 1)
156+
assert.Equal(t, got.IPAM.Config[0].Subnet, testSubnet)
157+
assert.Equal(t, got.IPAM.Config[0].Gateway, testGateway)
158+
assert.Equal(t, got.IPAM.Config[0].IPRange, testIPRange)
159+
},
160+
}
161+
},
162+
},
163+
}
164+
165+
testCase.Run(t)
166+
}
167+
168+
func TestNetworkInspectByID(t *testing.T) {
169+
testCase := nerdtest.Setup()
170+
171+
testCase.Setup = func(data test.Data, helpers test.Helpers) {
172+
helpers.Ensure("network", "create", data.Identifier("basenet"))
173+
data.Labels().Set("basenet", data.Identifier("basenet"))
174+
}
175+
176+
testCase.Cleanup = func(data test.Data, helpers test.Helpers) {
177+
helpers.Anyhow("network", "rm", data.Identifier("basenet"))
178+
}
179+
180+
testCase.SubTests = []*test.Case{
136181
{
137182
Description: "match exact id",
138183
// See notes below
@@ -201,41 +246,6 @@ func TestNetworkInspect(t *testing.T) {
201246
}
202247
},
203248
},
204-
{
205-
Description: "basic",
206-
// FIXME: IPAMConfig is not implemented on Windows yet
207-
Require: require.Not(require.Windows),
208-
Setup: func(data test.Data, helpers test.Helpers) {
209-
helpers.Ensure("network", "create", "--label", "tag=testNetwork", "--subnet", testSubnet,
210-
"--gateway", testGateway, "--ip-range", testIPRange, data.Identifier())
211-
},
212-
Cleanup: func(data test.Data, helpers test.Helpers) {
213-
helpers.Anyhow("network", "rm", data.Identifier())
214-
},
215-
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
216-
return helpers.Command("network", "inspect", data.Identifier())
217-
},
218-
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
219-
return &test.Expected{
220-
ExitCode: 0,
221-
Output: func(stdout string, t tig.T) {
222-
var dc []dockercompat.Network
223-
224-
err := json.Unmarshal([]byte(stdout), &dc)
225-
assert.NilError(t, err, "Unable to unmarshal output\n")
226-
assert.Equal(t, 1, len(dc), "Unexpectedly got multiple results\n")
227-
got := dc[0]
228-
229-
assert.Equal(t, got.Name, data.Identifier())
230-
assert.Equal(t, got.Labels["tag"], "testNetwork")
231-
assert.Equal(t, len(got.IPAM.Config), 1)
232-
assert.Equal(t, got.IPAM.Config[0].Subnet, testSubnet)
233-
assert.Equal(t, got.IPAM.Config[0].Gateway, testGateway)
234-
assert.Equal(t, got.IPAM.Config[0].IPRange, testIPRange)
235-
},
236-
}
237-
},
238-
},
239249
{
240250
Description: "with namespace",
241251
Require: require.Not(nerdtest.Docker),
@@ -287,6 +297,15 @@ func TestNetworkInspect(t *testing.T) {
287297
}
288298
},
289299
},
300+
}
301+
302+
testCase.Run(t)
303+
}
304+
305+
func TestNetworkInspectWithContainers(t *testing.T) {
306+
testCase := nerdtest.Setup()
307+
308+
testCase.SubTests = []*test.Case{
290309
{
291310
Description: "Verify that only active containers appear in the network inspect output",
292311
Setup: func(data test.Data, helpers test.Helpers) {
@@ -397,6 +416,173 @@ func TestNetworkInspect(t *testing.T) {
397416
}
398417
},
399418
},
419+
{
420+
Description: "Test container network details",
421+
Setup: func(data test.Data, helpers test.Helpers) {
422+
helpers.Ensure("network", "create", data.Identifier("test-network"))
423+
424+
// See https://github.com/containerd/nerdctl/issues/4322
425+
if runtime.GOOS == "windows" {
426+
time.Sleep(time.Second)
427+
}
428+
429+
// Create and start a container on this network
430+
helpers.Ensure("run", "-d", "--name", data.Identifier("test-container"),
431+
"--network", data.Identifier("test-network"),
432+
testutil.CommonImage, "sleep", nerdtest.Infinity)
433+
434+
// Get container ID for later use
435+
containerID := strings.Trim(helpers.Capture("inspect", data.Identifier("test-container"), "--format", "{{.Id}}"), "\n")
436+
data.Labels().Set("containerID", containerID)
437+
},
438+
Cleanup: func(data test.Data, helpers test.Helpers) {
439+
helpers.Anyhow("rm", "-f", data.Identifier("test-container"))
440+
helpers.Anyhow("network", "remove", data.Identifier("test-network"))
441+
},
442+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
443+
return helpers.Command("network", "inspect", data.Identifier("test-network"))
444+
},
445+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
446+
return &test.Expected{
447+
Output: func(stdout string, t tig.T) {
448+
var dc []dockercompat.Network
449+
err := json.Unmarshal([]byte(stdout), &dc)
450+
assert.NilError(t, err, "Unable to unmarshal output")
451+
assert.Equal(t, 1, len(dc), "Expected exactly one network")
452+
453+
network := dc[0]
454+
assert.Equal(t, network.Name, data.Identifier("test-network"))
455+
assert.Equal(t, 1, len(network.Containers), "Expected exactly one container")
456+
457+
// Get the container details
458+
containerID := data.Labels().Get("containerID")
459+
container := network.Containers[containerID]
460+
461+
// Test container name
462+
assert.Equal(t, container.Name, data.Identifier("test-container"))
463+
464+
// Windows InspectNetNS is not implemented
465+
if runtime.GOOS != "windows" {
466+
// Verify IPv4Address is not empty and has CIDR notation
467+
assert.Assert(t, container.IPv4Address != "", "IPv4Address should not be empty")
468+
assert.Assert(t, strings.Contains(container.IPv4Address, "/"), "IPv4Address should contain CIDR notation with /")
469+
470+
// Verify IPv4Address is within the network's subnet
471+
if len(network.IPAM.Config) > 0 && network.IPAM.Config[0].Subnet != "" {
472+
_, subnet, err := net.ParseCIDR(network.IPAM.Config[0].Subnet)
473+
assert.NilError(t, err, "Failed to parse network subnet")
474+
475+
containerIP, _, err := net.ParseCIDR(container.IPv4Address)
476+
assert.NilError(t, err, "Failed to parse container IPv4Address")
477+
assert.Assert(t, subnet.Contains(containerIP), "IPv4Address should be within the network's subnet")
478+
}
479+
480+
// Test MacAddress is present and has valid format
481+
assert.Assert(t, container.MacAddress != "", "MacAddress should not be empty")
482+
483+
// Test IPv6Address is empty for IPv4-only network
484+
assert.Equal(t, "", container.IPv6Address, "IPv6Address should be empty for IPv4-only network")
485+
}
486+
},
487+
}
488+
},
489+
},
490+
}
491+
492+
testCase.Run(t)
493+
}
494+
495+
func TestNetworkInspectDualStack(t *testing.T) {
496+
testCase := nerdtest.Setup()
497+
498+
testCase.SubTests = []*test.Case{
499+
{
500+
Description: "Test dual-stack network with both IPv4 and IPv6",
501+
Require: require.Not(require.Windows), // NetNS not implemented on Windows
502+
Setup: func(data test.Data, helpers test.Helpers) {
503+
helpers.Ensure("network", "create",
504+
"--ipv6",
505+
"--subnet", "10.1.0.0/24",
506+
"--subnet", "fd00::/64",
507+
data.Identifier("test-dual-stack"))
508+
509+
// See https://github.com/containerd/nerdctl/issues/4322
510+
if runtime.GOOS == "windows" {
511+
time.Sleep(time.Second)
512+
}
513+
514+
// Create and start a container on this dual-stack network
515+
helpers.Ensure("run", "-d",
516+
"--name", data.Identifier("test-container"),
517+
"--network", data.Identifier("test-dual-stack"),
518+
testutil.CommonImage, "sleep", nerdtest.Infinity)
519+
520+
// Get container ID for later use
521+
containerID := strings.Trim(helpers.Capture("inspect", data.Identifier("test-container"), "--format", "{{.Id}}"), "\n")
522+
data.Labels().Set("containerID", containerID)
523+
},
524+
Cleanup: func(data test.Data, helpers test.Helpers) {
525+
helpers.Anyhow("rm", "-f", data.Identifier("test-container"))
526+
helpers.Anyhow("network", "remove", data.Identifier("test-dual-stack"))
527+
},
528+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
529+
return helpers.Command("network", "inspect", data.Identifier("test-dual-stack"))
530+
},
531+
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
532+
return &test.Expected{
533+
Output: func(stdout string, t tig.T) {
534+
var dc []dockercompat.Network
535+
err := json.Unmarshal([]byte(stdout), &dc)
536+
assert.NilError(t, err, "Unable to unmarshal output")
537+
assert.Equal(t, 1, len(dc), "Expected exactly one network")
538+
539+
network := dc[0]
540+
assert.Equal(t, network.Name, data.Identifier("test-dual-stack"))
541+
assert.Equal(t, 2, len(network.IPAM.Config), "Expected two subnets (IPv4 and IPv6)")
542+
543+
// Get the container details
544+
containerID := data.Labels().Get("containerID")
545+
container := network.Containers[containerID]
546+
547+
// Test container name
548+
assert.Equal(t, container.Name, data.Identifier("test-container"))
549+
550+
// Parse both subnets
551+
var ipv4Subnet, ipv6Subnet *net.IPNet
552+
for _, config := range network.IPAM.Config {
553+
if config.Subnet != "" {
554+
_, subnet, err := net.ParseCIDR(config.Subnet)
555+
assert.NilError(t, err, "Failed to parse subnet")
556+
if subnet.IP.To4() != nil {
557+
ipv4Subnet = subnet
558+
} else {
559+
ipv6Subnet = subnet
560+
}
561+
}
562+
}
563+
564+
// Verify IPv4 address is present and within subnet
565+
assert.Assert(t, container.IPv4Address != "", "IPv4Address should not be empty in dual-stack network")
566+
ipv4, _, err := net.ParseCIDR(container.IPv4Address)
567+
assert.NilError(t, err, "Failed to parse IPv4Address")
568+
if ipv4Subnet != nil {
569+
assert.Assert(t, ipv4Subnet.Contains(ipv4), "IPv4 address should be within the IPv4 subnet")
570+
}
571+
572+
// Verify IPv6 address is present and within subnet
573+
assert.Assert(t, container.IPv6Address != "", "IPv6Address should not be empty in dual-stack network")
574+
ipv6, _, err := net.ParseCIDR(container.IPv6Address)
575+
assert.NilError(t, err, "Failed to parse IPv6Address")
576+
if ipv6Subnet != nil {
577+
assert.Assert(t, ipv6Subnet.Contains(ipv6), "IPv6 address should be within the IPv6 subnet")
578+
}
579+
580+
// Verify MAC address is present
581+
assert.Assert(t, container.MacAddress != "", "MacAddress should not be empty")
582+
},
583+
}
584+
},
585+
},
400586
}
401587

402588
testCase.Run(t)

0 commit comments

Comments
 (0)