Skip to content

Commit 840aed2

Browse files
committed
fix: handle both nerdctl and docker test targets
Signed-off-by: Alessio Greggi <[email protected]>
1 parent f86baac commit 840aed2

File tree

3 files changed

+92
-14
lines changed

3 files changed

+92
-14
lines changed

cmd/nerdctl/container_kill_linux_test.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,30 @@ func TestKillCleanupForwards(t *testing.T) {
4949
ipt, err := iptables.New()
5050
assert.NilError(t, err)
5151

52-
cmd := base.Cmd("run", "-d",
52+
containerID := base.Cmd("run", "-d",
5353
"--restart=no",
5454
"--name", testContainerName,
5555
"-p", fmt.Sprintf("127.0.0.1:%d:80", hostPort),
56-
testutil.NginxAlpineImage)
57-
containerID := strings.TrimSpace(cmd.Run().Stdout())
58-
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, testutil.Namespace, containerID), true)
56+
testutil.NginxAlpineImage).Run().Stdout()
57+
containerID = strings.TrimSuffix(containerID, "\n")
58+
59+
containerIP := base.Cmd("inspect",
60+
"-f",
61+
"'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'",
62+
testContainerName).Run().Stdout()
63+
containerIP = strings.ReplaceAll(containerIP, "'", "")
64+
containerIP = strings.TrimSuffix(containerIP, "\n")
65+
66+
// define iptables chain name depending on the target (docker/nerdctl)
67+
var chain string
68+
if testutil.GetTarget() == testutil.Docker {
69+
chain = "DOCKER"
70+
} else {
71+
redirectChain := "CNI-HOSTPORT-DNAT"
72+
chain = iptablesutil.GetRedirectedChain(t, ipt, redirectChain, testutil.Namespace, containerID)
73+
}
74+
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, chain, containerIP, hostPort), true)
5975

6076
base.Cmd("kill", testContainerName).AssertOK()
61-
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, testutil.Namespace, containerID), false)
77+
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, chain, containerIP, hostPort), false)
6278
}

cmd/nerdctl/container_stop_linux_test.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,30 @@ func TestStopCleanupForwards(t *testing.T) {
108108
ipt, err := iptables.New()
109109
assert.NilError(t, err)
110110

111-
cmd := base.Cmd("run", "-d",
111+
containerID := base.Cmd("run", "-d",
112112
"--restart=no",
113113
"--name", testContainerName,
114114
"-p", fmt.Sprintf("127.0.0.1:%d:80", hostPort),
115-
testutil.NginxAlpineImage)
116-
containerID := strings.TrimSpace(cmd.Run().Stdout())
117-
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, testutil.Namespace, containerID), true)
115+
testutil.NginxAlpineImage).Run().Stdout()
116+
containerID = strings.TrimSuffix(containerID, "\n")
117+
118+
containerIP := base.Cmd("inspect",
119+
"-f",
120+
"'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'",
121+
testContainerName).Run().Stdout()
122+
containerIP = strings.ReplaceAll(containerIP, "'", "")
123+
containerIP = strings.TrimSuffix(containerIP, "\n")
124+
125+
// define iptables chain name depending on the target (docker/nerdctl)
126+
var chain string
127+
if testutil.GetTarget() == testutil.Docker {
128+
chain = "DOCKER"
129+
} else {
130+
redirectChain := "CNI-HOSTPORT-DNAT"
131+
chain = iptablesutil.GetRedirectedChain(t, ipt, redirectChain, testutil.Namespace, containerID)
132+
}
133+
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, chain, containerIP, hostPort), true)
118134

119135
base.Cmd("stop", testContainerName).AssertOK()
120-
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, testutil.Namespace, containerID), false)
136+
assert.Equal(t, iptablesutil.ForwardExists(t, ipt, chain, containerIP, hostPort), false)
121137
}

pkg/testutil/iptables/iptables.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@
1717
package iptables
1818

1919
import (
20+
"fmt"
2021
"regexp"
2122
"testing"
2223

2324
"github.com/coreos/go-iptables/iptables"
2425
)
2526

26-
// forwardExists check that at least 2 rules are present in the CNI-HOSTPORT-DNAT chain
27+
// ForwardExists check that at least 2 rules are present in the CNI-HOSTPORT-DNAT chain
2728
// and checks for regex matches in the list of rules
28-
func ForwardExists(t *testing.T, ipt *iptables.IPTables, namespace, containerID string) bool {
29-
rules, err := ipt.List("nat", "CNI-HOSTPORT-DNAT")
29+
func ForwardExists(t *testing.T, ipt *iptables.IPTables, chain, containerIP string, port int) bool {
30+
rules, err := ipt.List("nat", chain)
3031
if err != nil {
3132
t.Logf("error listing rules in chain: %q\n", err)
33+
return false
3234
}
3335

3436
if len(rules) < 1 {
@@ -39,8 +41,9 @@ func ForwardExists(t *testing.T, ipt *iptables.IPTables, namespace, containerID
3941
// here we check if at least one of the rules in the chain
4042
// matches the required string to identify that the rule was applied
4143
found := false
44+
matchRule := `--dport ` + fmt.Sprintf("%d", port) + ` .+ --to-destination ` + containerIP
4245
for _, rule := range rules {
43-
foundInRule, err := regexp.MatchString(namespace+"-"+containerID, rule)
46+
foundInRule, err := regexp.MatchString(matchRule, rule)
4447
if err != nil {
4548
t.Logf("error in match string: %q\n", err)
4649
return false
@@ -51,3 +54,46 @@ func ForwardExists(t *testing.T, ipt *iptables.IPTables, namespace, containerID
5154
}
5255
return found
5356
}
57+
58+
// GetRedirectedChain returns the chain where the traffic is being redirected.
59+
// This is how libcni manage its port maps.
60+
// Suppose you have the following rule:
61+
// -A CNI-HOSTPORT-DNAT -p tcp -m comment --comment "dnat name: \"bridge\" id: \"default-YYYYYY\"" -m multiport --dports 9999 -j CNI-DN-XXXXXX
62+
// So the chain where the traffic is redirected is CNI-DN-XXXXXX
63+
// Returns an empty string in case nothing was found.
64+
func GetRedirectedChain(t *testing.T, ipt *iptables.IPTables, chain, namespace, containerID string) string {
65+
rules, err := ipt.List("nat", chain)
66+
if err != nil {
67+
t.Logf("error listing rules in chain: %q\n", err)
68+
return ""
69+
}
70+
71+
if len(rules) < 1 {
72+
t.Logf("not enough rules: %d", len(rules))
73+
return ""
74+
}
75+
76+
var redirectedChain string
77+
re := regexp.MustCompile(`-j\s+([^ ]+)`)
78+
for _, rule := range rules {
79+
// first we verify the comment section is present: "dnat name: \"bridge\" id: \"default-YYYYYY\""
80+
matchesContainer, err := regexp.MatchString(namespace+"-"+containerID, rule)
81+
if err != nil {
82+
t.Logf("error in match string: %q\n", err)
83+
return ""
84+
}
85+
if matchesContainer {
86+
// then we find the appropriate chain in the rule
87+
matches := re.FindStringSubmatch(rule)
88+
fmt.Println(matches)
89+
if len(matches) >= 2 {
90+
redirectedChain = matches[1]
91+
}
92+
}
93+
}
94+
if redirectedChain == "" {
95+
t.Logf("no redirectced chain found")
96+
return ""
97+
}
98+
return redirectedChain
99+
}

0 commit comments

Comments
 (0)