Skip to content

Commit 40a8a2c

Browse files
committed
test: refactor container_kill_linux_test.go to use Tigron
Signed-off-by: microness <youjungho92@naver.com>
1 parent 015837c commit 40a8a2c

File tree

2 files changed

+146
-39
lines changed

2 files changed

+146
-39
lines changed

cmd/nerdctl/container/container_kill_linux_test.go

Lines changed: 123 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,147 @@ package container
1818

1919
import (
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.
3642
func 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
}

pkg/testutil/iptables/iptables_linux.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,26 @@ func GetRedirectedChain(t *testing.T, ipt *iptables.IPTables, chain, namespace,
9797
}
9898
return redirectedChain
9999
}
100+
101+
// ForwardExistsFromRules checks whether any rule in the given slice
102+
// matches the expected DNAT forwarding pattern.
103+
func ForwardExistsFromRules(rules []string, containerIP string, port int) (bool, error) {
104+
if len(rules) < 1 {
105+
return false, fmt.Errorf("not enough rules: %d", len(rules))
106+
}
107+
108+
// here we check if at least one of the rules in the chain
109+
// matches the required string to identify that the rule was applied
110+
found := false
111+
matchRule := `--dport ` + fmt.Sprintf("%d", port) + ` .+ --to-destination ` + containerIP
112+
for _, rule := range rules {
113+
foundInRule, err := regexp.MatchString(matchRule, rule)
114+
if err != nil {
115+
return false, fmt.Errorf("error in match string: %q", err)
116+
}
117+
if foundInRule {
118+
found = foundInRule
119+
}
120+
}
121+
return found, nil
122+
}

0 commit comments

Comments
 (0)