mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-02-21 14:43:32 +00:00
Merge pull request #136635 from dims/automated-cherry-pick-of-#136529-#136554-upstream-release-1.34
Automated cherry pick of #136529: test: Read /proc/net/nf_conntrack instead of using conntrack binary #136554: test: Fix KubeProxy CLOSE_WAIT test for IPv6 environments (and where /proc/net/nf_conntrack may be missing)
This commit is contained in:
@@ -48,6 +48,18 @@ import (
|
||||
netutils "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
// expandIPv6ForConntrack expands an IPv6 address to the format used in /proc/net/nf_conntrack.
|
||||
// e.g., "fc00:f853:ccd:e793::3" -> "fc00:f853:0ccd:e793:0000:0000:0000:0003"
|
||||
func expandIPv6ForConntrack(ipStr string) string {
|
||||
ip := netutils.ParseIPSloppy(ipStr)
|
||||
if !netutils.IsIPv6(ip) {
|
||||
return ipStr
|
||||
}
|
||||
return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||
ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7],
|
||||
ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15])
|
||||
}
|
||||
|
||||
var kubeProxyE2eImage = imageutils.GetE2EImage(imageutils.Agnhost)
|
||||
|
||||
var _ = common.SIGDescribe("KubeProxy", func() {
|
||||
@@ -207,51 +219,56 @@ var _ = common.SIGDescribe("KubeProxy", func() {
|
||||
e2epod.NewPodClient(fr).CreateSync(ctx, clientPodSpec)
|
||||
|
||||
ginkgo.By("Checking conntrack entries for the timeout")
|
||||
// These must be synchronized from the default values set in
|
||||
// pkg/apis/../defaults.go ConntrackTCPCloseWaitTimeout. The
|
||||
// current defaults are hidden in the initialization code.
|
||||
const epsilonSeconds = 60
|
||||
const expectedTimeoutSeconds = 60 * 60
|
||||
// the conntrack file uses the IPv6 expanded format
|
||||
|
||||
// Detect conntrack method and build command
|
||||
ip := serverNodeInfo.nodeIP
|
||||
ipFamily := "ipv4"
|
||||
if netutils.IsIPv6String(ip) {
|
||||
ipFamily = "ipv6"
|
||||
}
|
||||
// Obtain the corresponding conntrack entry on the host checking
|
||||
// the nf_conntrack file from the pod e2e-net-exec.
|
||||
// It retries in a loop if the entry is not found.
|
||||
cmd := fmt.Sprintf("conntrack -L -f %s -d %v "+
|
||||
"| grep -m 1 'CLOSE_WAIT.*dport=%v' ",
|
||||
ipFamily, ip, testDaemonTCPPort)
|
||||
|
||||
var cmd, dumpCmd string
|
||||
var timeoutIdx int
|
||||
if _, err := e2epodoutput.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", "test -f /proc/net/nf_conntrack"); err == nil {
|
||||
procIP := ip
|
||||
if ipFamily == "ipv6" {
|
||||
procIP = expandIPv6ForConntrack(ip)
|
||||
}
|
||||
cmd = fmt.Sprintf("cat /proc/net/nf_conntrack | grep -m 1 -E 'CLOSE_WAIT.*dst=%s.*dport=%d'", procIP, testDaemonTCPPort)
|
||||
dumpCmd = "cat /proc/net/nf_conntrack"
|
||||
timeoutIdx = 4 // ipv4 2 tcp 6 <timeout> CLOSE_WAIT ...
|
||||
} else if _, err := e2epodoutput.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", "which conntrack"); err == nil {
|
||||
cmd = fmt.Sprintf("conntrack -L -f %s -d %s 2>/dev/null | grep -m 1 'CLOSE_WAIT.*dport=%d'", ipFamily, ip, testDaemonTCPPort)
|
||||
dumpCmd = "conntrack -L 2>/dev/null"
|
||||
timeoutIdx = 2 // tcp 6 <timeout> CLOSE_WAIT ...
|
||||
} else {
|
||||
e2eskipper.Skipf("Neither /proc/net/nf_conntrack nor conntrack binary available")
|
||||
}
|
||||
|
||||
if err := wait.PollImmediate(2*time.Second, epsilonSeconds*time.Second, func() (bool, error) {
|
||||
result, err := e2epodoutput.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", cmd)
|
||||
// retry if we can't obtain the conntrack entry
|
||||
if err != nil {
|
||||
framework.Logf("failed to obtain conntrack entry: %v %v", result, err)
|
||||
framework.Logf("failed to obtain conntrack entry: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
framework.Logf("conntrack entry for node %v and port %v: %v", serverNodeInfo.nodeIP, testDaemonTCPPort, result)
|
||||
// Timeout in seconds is available as the third column of the matched entry
|
||||
line := strings.Fields(result)
|
||||
if len(line) < 3 {
|
||||
return false, fmt.Errorf("conntrack entry does not have a timeout field: %v", line)
|
||||
fields := strings.Fields(result)
|
||||
if len(fields) <= timeoutIdx {
|
||||
return false, nil
|
||||
}
|
||||
timeoutSeconds, err := strconv.Atoi(line[2])
|
||||
timeoutSeconds, err := strconv.Atoi(fields[timeoutIdx])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to convert matched timeout %s to integer: %w", line[2], err)
|
||||
return false, nil
|
||||
}
|
||||
framework.Logf("conntrack timeout for %v:%v = %v", serverNodeInfo.nodeIP, testDaemonTCPPort, timeoutSeconds)
|
||||
if math.Abs(float64(timeoutSeconds-expectedTimeoutSeconds)) < epsilonSeconds {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("wrong TCP CLOSE_WAIT timeout: %v expected: %v", timeoutSeconds, expectedTimeoutSeconds)
|
||||
}); err != nil {
|
||||
// Dump all conntrack entries for debugging
|
||||
result, err2 := e2epodoutput.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", "conntrack -L")
|
||||
if err2 != nil {
|
||||
framework.Logf("failed to obtain conntrack entry: %v %v", result, err2)
|
||||
}
|
||||
framework.Logf("conntrack entries for node %v: %v", serverNodeInfo.nodeIP, result)
|
||||
result, _ := e2epodoutput.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", dumpCmd)
|
||||
framework.Logf("conntrack entries: %v", result)
|
||||
framework.Failf("no valid conntrack entry for port %d on node %s: %v", testDaemonTCPPort, serverNodeInfo.nodeIP, err)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user