mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #90687 from aojea/connt
use conntrack instead of the /proc file for the e2e test TCP CLOSE_WAIT
This commit is contained in:
		@@ -116,6 +116,8 @@ dependencies:
 | 
			
		||||
      match: debian_iptables_version=
 | 
			
		||||
    - path: build/workspace.bzl
 | 
			
		||||
      match: tag =
 | 
			
		||||
    - path: test/utils/image/manifest.go
 | 
			
		||||
      match: configs\[DebianIptables\] = Config{buildImageRegistry, "debian-iptables", "v\d+\.\d+.\d+"}
 | 
			
		||||
 | 
			
		||||
  - name: "k8s.gcr.io/go-runner"
 | 
			
		||||
    version: 0.1.1
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ package network
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"net"
 | 
			
		||||
@@ -35,6 +34,7 @@ import (
 | 
			
		||||
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 | 
			
		||||
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
 | 
			
		||||
	imageutils "k8s.io/kubernetes/test/utils/image"
 | 
			
		||||
	netutils "k8s.io/utils/net"
 | 
			
		||||
 | 
			
		||||
	"github.com/onsi/ginkgo"
 | 
			
		||||
	"github.com/onsi/gomega"
 | 
			
		||||
@@ -81,8 +81,6 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Create a pod to check the conntrack entries on the host node
 | 
			
		||||
		// It mounts the host /proc/net folder to be able to access
 | 
			
		||||
		// the nf_conntrack file with the host conntrack entries
 | 
			
		||||
		privileged := true
 | 
			
		||||
 | 
			
		||||
		hostExecPod := &v1.Pod{
 | 
			
		||||
@@ -97,43 +95,17 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
				Containers: []v1.Container{
 | 
			
		||||
					{
 | 
			
		||||
						Name:            "e2e-net-exec",
 | 
			
		||||
						Image:           kubeProxyE2eImage,
 | 
			
		||||
						Image:           imageutils.GetE2EImage(imageutils.DebianIptables),
 | 
			
		||||
						ImagePullPolicy: v1.PullIfNotPresent,
 | 
			
		||||
						Args:            []string{"pause"},
 | 
			
		||||
						VolumeMounts: []v1.VolumeMount{
 | 
			
		||||
							{
 | 
			
		||||
								Name:      "proc-net",
 | 
			
		||||
								MountPath: "/rootfs/proc/net",
 | 
			
		||||
								ReadOnly:  true,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						Command:         []string{"sleep", "600"},
 | 
			
		||||
						SecurityContext: &v1.SecurityContext{
 | 
			
		||||
							Privileged: &privileged,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				Volumes: []v1.Volume{
 | 
			
		||||
					{
 | 
			
		||||
						Name: "proc-net",
 | 
			
		||||
						VolumeSource: v1.VolumeSource{
 | 
			
		||||
							HostPath: &v1.HostPathVolumeSource{
 | 
			
		||||
								Path: "/proc/net",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
		fr.PodClient().CreateSync(hostExecPod)
 | 
			
		||||
		defer fr.PodClient().DeleteSync(hostExecPod.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
 | 
			
		||||
 | 
			
		||||
		// Some distributions (Ubuntu 16.04 etc.) don't support the proc file.
 | 
			
		||||
		_, err = framework.RunHostCmd(fr.Namespace.Name, "e2e-net-exec",
 | 
			
		||||
			"ls /rootfs/proc/net/nf_conntrack")
 | 
			
		||||
		if err != nil && strings.Contains(err.Error(), "No such file or directory") {
 | 
			
		||||
			e2eskipper.Skipf("The node %s does not support /proc/net/nf_conntrack", clientNodeInfo.name)
 | 
			
		||||
		}
 | 
			
		||||
		framework.ExpectNoError(err)
 | 
			
		||||
 | 
			
		||||
		// Create the client and server pods
 | 
			
		||||
		clientPodSpec := &v1.Pod{
 | 
			
		||||
@@ -202,7 +174,6 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
			serverNodeInfo.nodeIP,
 | 
			
		||||
			kubeProxyE2eImage))
 | 
			
		||||
		fr.PodClient().CreateSync(serverPodSpec)
 | 
			
		||||
		defer fr.PodClient().DeleteSync(serverPodSpec.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
 | 
			
		||||
 | 
			
		||||
		// The server should be listening before spawning the client pod
 | 
			
		||||
		if readyErr := e2epod.WaitForPodsReady(fr.ClientSet, fr.Namespace.Name, serverPodSpec.Name, 0); readyErr != nil {
 | 
			
		||||
@@ -215,22 +186,25 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
			clientNodeInfo.nodeIP,
 | 
			
		||||
			kubeProxyE2eImage))
 | 
			
		||||
		fr.PodClient().CreateSync(clientPodSpec)
 | 
			
		||||
		defer fr.PodClient().DeleteSync(clientPodSpec.Name, metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
 | 
			
		||||
 | 
			
		||||
		ginkgo.By("Checking /proc/net/nf_conntrack for the timeout")
 | 
			
		||||
		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
 | 
			
		||||
		ip := fullIPv6(net.ParseIP(serverNodeInfo.nodeIP))
 | 
			
		||||
		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("cat /rootfs/proc/net/nf_conntrack "+
 | 
			
		||||
			"| grep -m 1 'CLOSE_WAIT.*dst=%v.*dport=%v' ",
 | 
			
		||||
			ip, testDaemonTCPPort)
 | 
			
		||||
		cmd := fmt.Sprintf("conntrack -L -f %s -d %v"+
 | 
			
		||||
			"| grep -m 1 'CLOSE_WAIT.*dport=%v' ",
 | 
			
		||||
			ipFamily, ip, testDaemonTCPPort)
 | 
			
		||||
		if err := wait.PollImmediate(1*time.Second, postFinTimeoutSeconds, func() (bool, error) {
 | 
			
		||||
			result, err := framework.RunHostCmd(fr.Namespace.Name, "e2e-net-exec", cmd)
 | 
			
		||||
			// retry if we can't obtain the conntrack entry
 | 
			
		||||
@@ -239,15 +213,14 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
				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 fifth column of
 | 
			
		||||
			// the matched entry in /proc/net/nf_conntrack.
 | 
			
		||||
			// Timeout in seconds is available as the third column of the matched entry
 | 
			
		||||
			line := strings.Fields(result)
 | 
			
		||||
			if len(line) < 5 {
 | 
			
		||||
			if len(line) < 3 {
 | 
			
		||||
				return false, fmt.Errorf("conntrack entry does not have a timeout field: %v", line)
 | 
			
		||||
			}
 | 
			
		||||
			timeoutSeconds, err := strconv.Atoi(line[4])
 | 
			
		||||
			timeoutSeconds, err := strconv.Atoi(line[2])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, fmt.Errorf("failed to convert matched timeout %s to integer: %v", line[4], err)
 | 
			
		||||
				return false, fmt.Errorf("failed to convert matched timeout %s to integer: %v", line[2], err)
 | 
			
		||||
			}
 | 
			
		||||
			if math.Abs(float64(timeoutSeconds-expectedTimeoutSeconds)) < epsilonSeconds {
 | 
			
		||||
				return true, nil
 | 
			
		||||
@@ -372,22 +345,3 @@ var _ = SIGDescribe("Network", func() {
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// fullIPv6 returns a string with the IP representation
 | 
			
		||||
// if IPv6 it returns the expanded address format
 | 
			
		||||
// credit https://stackoverflow.com/a/52003106/4532704
 | 
			
		||||
func fullIPv6(ip net.IP) string {
 | 
			
		||||
	if ip.To4() == nil {
 | 
			
		||||
		dst := make([]byte, hex.EncodedLen(len(ip)))
 | 
			
		||||
		_ = hex.Encode(dst, ip)
 | 
			
		||||
		return string(dst[0:4]) + ":" +
 | 
			
		||||
			string(dst[4:8]) + ":" +
 | 
			
		||||
			string(dst[8:12]) + ":" +
 | 
			
		||||
			string(dst[12:16]) + ":" +
 | 
			
		||||
			string(dst[16:20]) + ":" +
 | 
			
		||||
			string(dst[20:24]) + ":" +
 | 
			
		||||
			string(dst[24:28]) + ":" +
 | 
			
		||||
			string(dst[28:])
 | 
			
		||||
	}
 | 
			
		||||
	return ip.String()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ type RegistryList struct {
 | 
			
		||||
	DockerGluster           string `yaml:"dockerGluster"`
 | 
			
		||||
	E2eRegistry             string `yaml:"e2eRegistry"`
 | 
			
		||||
	PromoterE2eRegistry     string `yaml:"promoterE2eRegistry"`
 | 
			
		||||
	BuildImageRegistry      string `yaml:"buildImageRegistry"`
 | 
			
		||||
	InvalidRegistry         string `yaml:"invalidRegistry"`
 | 
			
		||||
	GcRegistry              string `yaml:"gcRegistry"`
 | 
			
		||||
	GcrReleaseRegistry      string `yaml:"gcrReleaseRegistry"`
 | 
			
		||||
@@ -73,6 +74,7 @@ func initReg() RegistryList {
 | 
			
		||||
		E2eRegistry:             "gcr.io/kubernetes-e2e-test-images",
 | 
			
		||||
		// TODO: After the domain flip, this should instead be k8s.gcr.io/k8s-artifacts-prod/e2e-test-images
 | 
			
		||||
		PromoterE2eRegistry: "us.gcr.io/k8s-artifacts-prod/e2e-test-images",
 | 
			
		||||
		BuildImageRegistry:  "us.gcr.io/k8s-artifacts-prod/build-image",
 | 
			
		||||
		InvalidRegistry:     "invalid.com/invalid",
 | 
			
		||||
		GcRegistry:          "k8s.gcr.io",
 | 
			
		||||
		GcrReleaseRegistry:  "gcr.io/gke-release",
 | 
			
		||||
@@ -106,6 +108,7 @@ var (
 | 
			
		||||
	dockerGluster           = registry.DockerGluster
 | 
			
		||||
	e2eRegistry             = registry.E2eRegistry
 | 
			
		||||
	promoterE2eRegistry     = registry.PromoterE2eRegistry
 | 
			
		||||
	buildImageRegistry      = registry.BuildImageRegistry
 | 
			
		||||
	gcAuthenticatedRegistry = registry.GcAuthenticatedRegistry
 | 
			
		||||
	gcRegistry              = registry.GcRegistry
 | 
			
		||||
	gcrReleaseRegistry      = registry.GcrReleaseRegistry
 | 
			
		||||
@@ -141,6 +144,8 @@ const (
 | 
			
		||||
	CudaVectorAdd
 | 
			
		||||
	// CudaVectorAdd2 image
 | 
			
		||||
	CudaVectorAdd2
 | 
			
		||||
	// DebianIptables Image
 | 
			
		||||
	DebianIptables
 | 
			
		||||
	// EchoServer image
 | 
			
		||||
	EchoServer
 | 
			
		||||
	// Etcd image
 | 
			
		||||
@@ -210,6 +215,7 @@ func initImageConfigs() map[int]Config {
 | 
			
		||||
	configs[CheckMetadataConcealment] = Config{e2eRegistry, "metadata-concealment", "1.2"}
 | 
			
		||||
	configs[CudaVectorAdd] = Config{e2eRegistry, "cuda-vector-add", "1.0"}
 | 
			
		||||
	configs[CudaVectorAdd2] = Config{e2eRegistry, "cuda-vector-add", "2.0"}
 | 
			
		||||
	configs[DebianIptables] = Config{buildImageRegistry, "debian-iptables", "v12.1.0"}
 | 
			
		||||
	configs[EchoServer] = Config{e2eRegistry, "echoserver", "2.2"}
 | 
			
		||||
	configs[Etcd] = Config{gcRegistry, "etcd", "3.4.7"}
 | 
			
		||||
	configs[GlusterDynamicProvisioner] = Config{dockerGluster, "glusterdynamic-provisioner", "v1.0"}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user