@@ -19,6 +19,7 @@ package network
1919import (
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