Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions cmd/nerdctl/network/network_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,63 @@ func TestNetworkInspect(t *testing.T) {
}
},
},
{
Description: "Test container network details",
Setup: func(data test.Data, helpers test.Helpers) {
helpers.Ensure("network", "create", data.Identifier("test-network"))

// See https://github.com/containerd/nerdctl/issues/4322
if runtime.GOOS == "windows" {
time.Sleep(time.Second)
}

// Create and start a container on this network
helpers.Ensure("run", "-d", "--name", data.Identifier("test-container"),
"--network", data.Identifier("test-network"),
testutil.CommonImage, "sleep", nerdtest.Infinity)

// Get container ID for later use
containerID := strings.Trim(helpers.Capture("inspect", data.Identifier("test-container"), "--format", "{{.Id}}"), "\n")
data.Labels().Set("containerID", containerID)
},
Cleanup: func(data test.Data, helpers test.Helpers) {
helpers.Anyhow("rm", "-f", data.Identifier("test-container"))
helpers.Anyhow("network", "remove", data.Identifier("test-network"))
},
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
return helpers.Command("network", "inspect", data.Identifier("test-network"))
},
Expected: func(data test.Data, helpers test.Helpers) *test.Expected {
return &test.Expected{
Output: func(stdout string, t tig.T) {
var dc []dockercompat.Network
err := json.Unmarshal([]byte(stdout), &dc)
assert.NilError(t, err, "Unable to unmarshal output")
assert.Equal(t, 1, len(dc), "Expected exactly one network")

network := dc[0]
assert.Equal(t, network.Name, data.Identifier("test-network"))
assert.Equal(t, 1, len(network.Containers), "Expected exactly one container")

// Get the container details
containerID := data.Labels().Get("containerID")
container := network.Containers[containerID]

// Test container name
assert.Equal(t, container.Name, data.Identifier("test-container"))

// Test IPv4 address field exists in response
assert.Assert(t, true, "IPv4Address field exists: %q", container.IPv4Address)

// Test MAC address field exists in response
assert.Assert(t, true, "MacAddress field exists: %q", container.MacAddress)

// Test IPv6 address field exists in response
assert.Assert(t, true, "IPv6Address field exists: %q", container.IPv6Address)
},
}
},
},
}

testCase.Run(t)
Expand Down
53 changes: 45 additions & 8 deletions pkg/inspecttypes/dockercompat/dockercompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -929,9 +929,9 @@ type Network struct {
type EndpointResource struct {
Name string `json:"Name"`
// EndpointID string `json:"EndpointID"`
// MacAddress string `json:"MacAddress"`
// IPv4Address string `json:"IPv4Address"`
// IPv6Address string `json:"IPv6Address"`
MacAddress string `json:"MacAddress"`
IPv4Address string `json:"IPv4Address"`
IPv6Address string `json:"IPv6Address"`
}

type structuredCNI struct {
Expand Down Expand Up @@ -975,13 +975,50 @@ func NetworkFromNative(n *native.Network) (*Network, error) {

res.Containers = make(map[string]EndpointResource)
for _, container := range n.Containers {
res.Containers[container.ID] = EndpointResource{
endpoint := EndpointResource{
Name: container.Labels[labels.Name],
// EndpointID: container.EndpointID,
// MacAddress: container.MacAddress,
// IPv4Address: container.IPv4Address,
// IPv6Address: container.IPv6Address,
}

// Extract network information from container's NetNS if available
if container.Process != nil && container.Process.NetNS != nil {
for _, x := range container.Process.NetNS.Interfaces {
if x.Interface.Flags&net.FlagLoopback != 0 {
continue
}
if x.Interface.Flags&net.FlagUp == 0 {
continue
}

// Set MAC address
endpoint.MacAddress = x.HardwareAddr

// Extract IPv4 and IPv6 addresses
for _, a := range x.Addrs {
ip, _, err := net.ParseCIDR(a)
if err != nil {
continue
}
if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
continue
}

// Check for IPv4
if ip4 := ip.To4(); ip4 != nil {
endpoint.IPv4Address = ip4.String()
} else if ip6 := ip.To16(); ip6 != nil {
// It's IPv6
endpoint.IPv6Address = ip6.String()
}
}

// Use the primary interface if available, otherwise use the first valid interface
if x.Index == container.Process.NetNS.PrimaryInterface || endpoint.IPv4Address != "" {
break
}
}
}

res.Containers[container.ID] = endpoint
}

return &res, nil
Expand Down
Loading