@@ -18,63 +18,147 @@ package container
1818
1919import (
2020 "fmt"
21+ "strconv"
2122 "strings"
2223 "testing"
2324
2425 "github.com/coreos/go-iptables/iptables"
2526 "gotest.tools/v3/assert"
2627
27- "github.com/containerd/nerdctl/v2/pkg/rootlessutil"
28+ "github.com/containerd/nerdctl/mod/tigron/expect"
29+ "github.com/containerd/nerdctl/mod/tigron/require"
30+ "github.com/containerd/nerdctl/mod/tigron/test"
31+ "github.com/containerd/nerdctl/mod/tigron/tig"
32+
2833 "github.com/containerd/nerdctl/v2/pkg/testutil"
2934 iptablesutil "github.com/containerd/nerdctl/v2/pkg/testutil/iptables"
3035 "github.com/containerd/nerdctl/v2/pkg/testutil/nerdtest"
36+ "github.com/containerd/nerdctl/v2/pkg/testutil/portlock"
3137)
3238
3339// TestKillCleanupForwards runs a container that exposes a port and then kill it.
3440// The test checks that the kill command effectively clean up
35- // the iptables forwards creted from the run.
41+ // the iptables forwards created from the run.
3642func TestKillCleanupForwards (t * testing.T ) {
37- const (
38- hostPort = 9999
39- testContainerName = "ngx"
40- )
41- base := testutil .NewBase (t )
42- defer func () {
43- base .Cmd ("rm" , "-f" , testContainerName ).Run ()
44- }()
45-
46- // skip if rootless
47- if rootlessutil .IsRootless () {
48- t .Skip ("pkg/testutil/iptables does not support rootless" )
49- }
5043
5144 ipt , err := iptables .New ()
5245 assert .NilError (t , err )
5346
54- containerID := base .Cmd ("run" , "-d" ,
55- "--restart=no" ,
56- "--name" , testContainerName ,
57- "-p" , fmt .Sprintf ("127.0.0.1:%d:80" , hostPort ),
58- testutil .NginxAlpineImage ).Run ().Stdout ()
59- containerID = strings .TrimSuffix (containerID , "\n " )
60-
61- containerIP := base .Cmd ("inspect" ,
62- "-f" ,
63- "'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'" ,
64- testContainerName ).Run ().Stdout ()
65- containerIP = strings .ReplaceAll (containerIP , "'" , "" )
66- containerIP = strings .TrimSuffix (containerIP , "\n " )
67-
68- // define iptables chain name depending on the target (docker/nerdctl)
69- var chain string
70- if nerdtest .IsDocker () {
71- chain = "DOCKER"
72- } else {
73- redirectChain := "CNI-HOSTPORT-DNAT"
74- chain = iptablesutil .GetRedirectedChain (t , ipt , redirectChain , testutil .Namespace , containerID )
47+ testCase := nerdtest .Setup ()
48+
49+ testCase .Require = require .Not (nerdtest .Rootless )
50+
51+ testCase .Setup = func (data test.Data , helpers test.Helpers ) {
52+ hostPort , err := portlock .Acquire (0 )
53+ if err != nil {
54+ t .Logf ("Failed to acquire port: %v" , err )
55+ t .FailNow ()
56+ }
57+
58+ containerName := data .Identifier ()
59+
60+ helpers .Ensure (
61+ "run" , "-d" ,
62+ "--restart=no" ,
63+ "--name" , containerName ,
64+ "-p" , fmt .Sprintf ("127.0.0.1:%d:80" , hostPort ),
65+ testutil .NginxAlpineImage ,
66+ )
67+ nerdtest .EnsureContainerStarted (helpers , containerName )
68+
69+ containerID := strings .TrimSpace (
70+ helpers .Capture ("inspect" , "-f" , "{{.Id}}" , containerName ),
71+ )
72+
73+ containerIP := strings .TrimSpace (
74+ helpers .Capture (
75+ "inspect" ,
76+ "-f" , "{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}" ,
77+ containerName ,
78+ ),
79+ )
80+
81+ // define iptables chain name depending on the target (docker/nerdctl)
82+ var chain string
83+ if nerdtest .IsDocker () {
84+ chain = "DOCKER"
85+ } else {
86+ chain = iptablesutil .GetRedirectedChain (
87+ t ,
88+ ipt ,
89+ "CNI-HOSTPORT-DNAT" ,
90+ testutil .Namespace ,
91+ containerID ,
92+ )
93+ }
94+
95+ data .Labels ().Set ("containerName" , containerName )
96+ data .Labels ().Set ("containerIP" , containerIP )
97+ data .Labels ().Set ("hostPort" , strconv .Itoa (hostPort ))
98+ data .Labels ().Set ("chain" , chain )
99+ }
100+
101+ testCase .SubTests = []* test.Case {
102+ {
103+ Description : "iptables forwarding rule should exist before container is killed" ,
104+ NoParallel : true ,
105+ Command : func (data test.Data , helpers test.Helpers ) test.TestableCommand {
106+ return helpers .Custom ("iptables" , "-t" , "nat" , "-S" , data .Labels ().Get ("chain" ))
107+ },
108+ Expected : func (data test.Data , helpers test.Helpers ) * test.Expected {
109+ return & test.Expected {
110+ Output : func (stdout string , t tig.T ) {
111+ rules := strings .Split (stdout , "\n " )
112+ containerIP := data .Labels ().Get ("containerIP" )
113+ hostPort , err := strconv .Atoi (data .Labels ().Get ("hostPort" ))
114+ assert .NilError (t , err )
115+ found , err := iptablesutil .ForwardExistsFromRules (rules , containerIP , hostPort )
116+ assert .NilError (t , err )
117+ assert .Assert (t , found )
118+ },
119+ }
120+ },
121+ },
122+ {
123+ Description : "kill container" ,
124+ NoParallel : true ,
125+ Command : func (data test.Data , helpers test.Helpers ) test.TestableCommand {
126+ return helpers .Command ("kill" , data .Labels ().Get ("containerName" ))
127+ },
128+ Expected : test .Expects (expect .ExitCodeSuccess , nil , nil ),
129+ },
130+ {
131+ Description : "iptables forwarding rule should be removed after container is killed" ,
132+ NoParallel : true ,
133+ Command : func (data test.Data , helpers test.Helpers ) test.TestableCommand {
134+ return helpers .Custom ("iptables" , "-t" , "nat" , "-S" , data .Labels ().Get ("chain" ))
135+ },
136+ Expected : func (data test.Data , helpers test.Helpers ) * test.Expected {
137+ return & test.Expected {
138+ ExitCode : expect .ExitCodeGenericFail ,
139+ Output : func (stdout string , t tig.T ) {
140+ rules := strings .Split (stdout , "\n " )
141+ containerIP := data .Labels ().Get ("containerIP" )
142+ hostPort , err := strconv .Atoi (data .Labels ().Get ("hostPort" ))
143+ assert .NilError (t , err )
144+ found , err := iptablesutil .ForwardExistsFromRules (rules , containerIP , hostPort )
145+ assert .NilError (t , err )
146+ assert .Assert (t , ! found )
147+ },
148+ }
149+ },
150+ },
151+ }
152+
153+ testCase .Cleanup = func (data test.Data , helpers test.Helpers ) {
154+ helpers .Anyhow ("rm" , "-f" , data .Identifier ())
155+
156+ if p := data .Labels ().Get ("hostPort" ); p != "" {
157+ if port , err := strconv .Atoi (p ); err == nil {
158+ _ = portlock .Release (port )
159+ }
160+ }
75161 }
76- assert .Equal (t , iptablesutil .ForwardExists (t , ipt , chain , containerIP , hostPort ), true )
77162
78- base .Cmd ("kill" , testContainerName ).AssertOK ()
79- assert .Equal (t , iptablesutil .ForwardExists (t , ipt , chain , containerIP , hostPort ), false )
163+ testCase .Run (t )
80164}
0 commit comments