1
16
17 package network
18
19 import (
20 "context"
21 "fmt"
22 "math/rand"
23 "net"
24 "strconv"
25
26 "github.com/onsi/ginkgo/v2"
27 v1 "k8s.io/api/core/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/util/intstr"
30 clientset "k8s.io/client-go/kubernetes"
31 "k8s.io/kubernetes/test/e2e/framework"
32 e2enode "k8s.io/kubernetes/test/e2e/framework/node"
33 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
34 "k8s.io/kubernetes/test/e2e/network/common"
35 imageutils "k8s.io/kubernetes/test/utils/image"
36 admissionapi "k8s.io/pod-security-admission/api"
37 )
38
39 var _ = common.SIGDescribe("HostPort", func() {
40
41 f := framework.NewDefaultFramework("hostport")
42 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
43
44 var (
45 cs clientset.Interface
46 ns string
47 )
48
49 ginkgo.BeforeEach(func() {
50 cs = f.ClientSet
51 ns = f.Namespace.Name
52
53 })
54
55
62
63 framework.ConformanceIt("validates that there is no conflict between pods with same hostPort but different hostIP and protocol [LinuxOnly]", func(ctx context.Context) {
64
65 localhost := "127.0.0.1"
66 family := v1.IPv4Protocol
67 if framework.TestContext.ClusterIsIPv6() {
68 localhost = "::1"
69 family = v1.IPv6Protocol
70 }
71
72 nodes, err := e2enode.GetBoundedReadySchedulableNodes(ctx, cs, 1)
73 framework.ExpectNoError(err)
74 if len(nodes.Items) == 0 {
75 framework.Failf("No nodes available")
76
77 }
78 randomNode := &nodes.Items[rand.Intn(len(nodes.Items))]
79
80 ips := e2enode.GetAddressesByTypeAndFamily(randomNode, v1.NodeInternalIP, family)
81 if len(ips) == 0 {
82 framework.Failf("Failed to get NodeIP")
83 }
84 hostIP := ips[0]
85 port := int32(54323)
86
87
88 ginkgo.By(fmt.Sprintf("Trying to create a pod(pod1) with hostport %v and hostIP %s and expect scheduled", port, localhost))
89 createHostPortPodOnNode(ctx, f, "pod1", ns, localhost, port, v1.ProtocolTCP, randomNode.Name)
90
91 ginkgo.By(fmt.Sprintf("Trying to create another pod(pod2) with hostport %v but hostIP %s on the node which pod1 resides and expect scheduled", port, hostIP))
92 createHostPortPodOnNode(ctx, f, "pod2", ns, hostIP, port, v1.ProtocolTCP, randomNode.Name)
93
94 ginkgo.By(fmt.Sprintf("Trying to create a third pod(pod3) with hostport %v, hostIP %s but use UDP protocol on the node which pod2 resides", port, hostIP))
95 createHostPortPodOnNode(ctx, f, "pod3", ns, hostIP, port, v1.ProtocolUDP, randomNode.Name)
96
97
98
99 hostExecPod := &v1.Pod{
100 ObjectMeta: metav1.ObjectMeta{
101 Name: "e2e-host-exec",
102 Namespace: f.Namespace.Name,
103 },
104 Spec: v1.PodSpec{
105 HostNetwork: true,
106 NodeName: randomNode.Name,
107 Containers: []v1.Container{
108 {
109 Name: "e2e-host-exec",
110 Image: imageutils.GetE2EImage(imageutils.Agnhost),
111 },
112 },
113 },
114 }
115 e2epod.NewPodClient(f).CreateSync(ctx, hostExecPod)
116
117
118 timeout := 5
119
120 cmdPod1 := []string{"/bin/sh", "-c", fmt.Sprintf("curl -g --connect-timeout %v --interface %s http://%s/hostname", timeout, hostIP, net.JoinHostPort(localhost, strconv.Itoa(int(port))))}
121 cmdPod2 := []string{"/bin/sh", "-c", fmt.Sprintf("curl -g --connect-timeout %v http://%s/hostname", timeout, net.JoinHostPort(hostIP, strconv.Itoa(int(port))))}
122 cmdPod3 := []string{"/bin/sh", "-c", fmt.Sprintf("echo hostname | nc -u -w %v %s %d", timeout, hostIP, port)}
123
124 for i := 0; i < 5; i++ {
125
126 ginkgo.By(fmt.Sprintf("checking connectivity from pod %s to serverIP: %s, port: %d", hostExecPod.Name, localhost, port))
127 hostname1, _, err := e2epod.ExecCommandInContainerWithFullOutput(f, hostExecPod.Name, "e2e-host-exec", cmdPod1...)
128 if err != nil {
129 framework.Logf("Can not connect from %s to pod(pod1) to serverIP: %s, port: %d", hostExecPod.Name, localhost, port)
130 continue
131 }
132
133 ginkgo.By(fmt.Sprintf("checking connectivity from pod %s to serverIP: %s, port: %d", hostExecPod.Name, hostIP, port))
134 hostname2, _, err := e2epod.ExecCommandInContainerWithFullOutput(f, hostExecPod.Name, "e2e-host-exec", cmdPod2...)
135 if err != nil {
136 framework.Logf("Can not connect from %s to pod(pod2) to serverIP: %s, port: %d", hostExecPod.Name, hostIP, port)
137 continue
138 }
139
140 if hostname1 == hostname2 {
141 framework.Logf("pods must have different hostname: pod1 has hostname %s, pod2 has hostname %s", hostname1, hostname2)
142 continue
143 }
144
145 ginkgo.By(fmt.Sprintf("checking connectivity from pod %s to serverIP: %s, port: %d UDP", hostExecPod.Name, hostIP, port))
146 hostname3, _, err := e2epod.ExecCommandInContainerWithFullOutput(f, hostExecPod.Name, "e2e-host-exec", cmdPod3...)
147 if err != nil {
148 framework.Logf("Can not connect from %s to pod(pod2) to serverIP: %s, port: %d", hostExecPod.Name, hostIP, port)
149 continue
150 }
151 if hostname1 == hostname3 {
152 framework.Logf("pods must have different hostname: pod1 has hostname %s, pod3 has hostname %s", hostname1, hostname3)
153 continue
154 }
155 if hostname2 == hostname3 {
156 framework.Logf("pods must have different hostname: pod2 has hostname %s, pod3 has hostname %s", hostname2, hostname3)
157 continue
158 }
159 return
160 }
161 framework.Failf("Failed to connect to exposed host ports")
162 })
163 })
164
165
166
167 func createHostPortPodOnNode(ctx context.Context, f *framework.Framework, podName, ns, hostIP string, port int32, protocol v1.Protocol, nodeName string) {
168
169 var netexecArgs []string
170 var readinessProbePort int32
171
172 if protocol == v1.ProtocolTCP {
173 readinessProbePort = 8080
174 netexecArgs = []string{"--http-port=8080", "--udp-port=-1"}
175 } else {
176 readinessProbePort = 8008
177 netexecArgs = []string{"--http-port=8008", "--udp-port=8080"}
178 }
179
180 hostPortPod := &v1.Pod{
181 ObjectMeta: metav1.ObjectMeta{
182 Name: podName,
183 },
184 Spec: v1.PodSpec{
185 Containers: []v1.Container{
186 {
187 Name: "agnhost",
188 Image: imageutils.GetE2EImage(imageutils.Agnhost),
189 Args: append([]string{"netexec"}, netexecArgs...),
190 Ports: []v1.ContainerPort{
191 {
192 HostPort: port,
193 ContainerPort: 8080,
194 Protocol: protocol,
195 HostIP: hostIP,
196 },
197 },
198 ReadinessProbe: &v1.Probe{
199 ProbeHandler: v1.ProbeHandler{
200 HTTPGet: &v1.HTTPGetAction{
201 Path: "/hostname",
202 Port: intstr.IntOrString{
203 IntVal: readinessProbePort,
204 },
205 Scheme: v1.URISchemeHTTP,
206 },
207 },
208 },
209 },
210 },
211 NodeName: nodeName,
212 },
213 }
214 if _, err := f.ClientSet.CoreV1().Pods(ns).Create(ctx, hostPortPod, metav1.CreateOptions{}); err != nil {
215 framework.Failf("error creating pod %s, err:%v", podName, err)
216 }
217
218 if err := e2epod.WaitTimeoutForPodReadyInNamespace(ctx, f.ClientSet, podName, ns, framework.PodStartTimeout); err != nil {
219 framework.Failf("wait for pod %s timeout, err:%v", podName, err)
220 }
221 }
222
View as plain text