1
16
17 package network
18
19 import (
20 "context"
21 "fmt"
22 "net"
23 "time"
24
25 "github.com/onsi/ginkgo/v2"
26 v1 "k8s.io/api/core/v1"
27 discoveryv1 "k8s.io/api/discovery/v1"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/util/wait"
30 clientset "k8s.io/client-go/kubernetes"
31 "k8s.io/kubernetes/test/e2e/framework"
32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
33 "k8s.io/kubernetes/test/e2e/network/common"
34 admissionapi "k8s.io/pod-security-admission/api"
35 )
36
37 var _ = common.SIGDescribe("EndpointSliceMirroring", func() {
38 f := framework.NewDefaultFramework("endpointslicemirroring")
39 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
40
41 var cs clientset.Interface
42
43 ginkgo.BeforeEach(func() {
44 cs = f.ClientSet
45 })
46
47
55 framework.ConformanceIt("should mirror a custom Endpoints resource through create update and delete", func(ctx context.Context) {
56 svc := createServiceReportErr(ctx, cs, f.Namespace.Name, &v1.Service{
57 ObjectMeta: metav1.ObjectMeta{
58 Name: "example-custom-endpoints",
59 },
60 Spec: v1.ServiceSpec{
61 Ports: []v1.ServicePort{{
62 Name: "example",
63 Port: 80,
64 Protocol: v1.ProtocolTCP,
65 }},
66 },
67 })
68
69 endpoints := &v1.Endpoints{
70 ObjectMeta: metav1.ObjectMeta{
71 Name: svc.Name,
72 },
73 Subsets: []v1.EndpointSubset{{
74 Ports: []v1.EndpointPort{{
75 Port: 80,
76 }},
77 Addresses: []v1.EndpointAddress{{
78 IP: "10.1.2.3",
79 }},
80 }},
81 }
82
83 ginkgo.By("mirroring a new custom Endpoint", func() {
84 _, err := cs.CoreV1().Endpoints(f.Namespace.Name).Create(ctx, endpoints, metav1.CreateOptions{})
85 framework.ExpectNoError(err, "Unexpected error creating Endpoints")
86
87 if err := wait.PollImmediate(2*time.Second, 12*time.Second, func() (bool, error) {
88 esList, err := cs.DiscoveryV1().EndpointSlices(f.Namespace.Name).List(ctx, metav1.ListOptions{
89 LabelSelector: discoveryv1.LabelServiceName + "=" + svc.Name,
90 })
91 if err != nil {
92 framework.Logf("Error listing EndpointSlices: %v", err)
93 return false, nil
94 }
95 if len(esList.Items) == 0 {
96 framework.Logf("Waiting for at least 1 EndpointSlice to exist, got %d", len(esList.Items))
97 return false, nil
98 }
99
100
101
102
103
104 for _, epSlice := range esList.Items {
105 if len(epSlice.Ports) != 1 {
106 return false, fmt.Errorf("Expected EndpointSlice to have 1 Port, got %d", len(epSlice.Ports))
107 }
108 port := epSlice.Ports[0]
109 if *port.Port != int32(80) {
110 return false, fmt.Errorf("Expected port to be 80, got %d", *port.Port)
111 }
112 if len(epSlice.Endpoints) != 1 {
113 return false, fmt.Errorf("Expected EndpointSlice to have 1 endpoints, got %d", len(epSlice.Endpoints))
114 }
115 endpoint := epSlice.Endpoints[0]
116 if len(endpoint.Addresses) != 1 {
117 return false, fmt.Errorf("Expected EndpointSlice endpoint to have 1 address, got %d", len(endpoint.Addresses))
118 }
119 address := endpoint.Addresses[0]
120 if address != "10.1.2.3" {
121 return false, fmt.Errorf("Expected EndpointSlice to have 10.1.2.3 as address, got %s", address)
122 }
123 }
124
125 return true, nil
126 }); err != nil {
127 framework.Failf("Did not find matching EndpointSlice for %s/%s: %s", svc.Namespace, svc.Name, err)
128 }
129 })
130
131 ginkgo.By("mirroring an update to a custom Endpoint", func() {
132 endpoints.Subsets[0].Addresses = []v1.EndpointAddress{{
133 IP: "10.2.3.4",
134 }}
135 _, err := cs.CoreV1().Endpoints(f.Namespace.Name).Update(ctx, endpoints, metav1.UpdateOptions{})
136 framework.ExpectNoError(err, "Unexpected error updating Endpoints")
137
138
139 if err := wait.PollImmediate(2*time.Second, 12*time.Second, func() (bool, error) {
140 esList, err := cs.DiscoveryV1().EndpointSlices(f.Namespace.Name).List(ctx, metav1.ListOptions{
141 LabelSelector: discoveryv1.LabelServiceName + "=" + svc.Name,
142 })
143 if err != nil {
144 return false, err
145 }
146 if len(esList.Items) != 1 {
147 framework.Logf("Waiting for 1 EndpointSlice to exist, got %d", len(esList.Items))
148 return false, nil
149 }
150 epSlice := esList.Items[0]
151 if len(epSlice.Ports) != 1 {
152 framework.Logf("Expected EndpointSlice to have 1 Port, got %d", len(epSlice.Ports))
153 return false, nil
154 }
155 port := epSlice.Ports[0]
156 if *port.Port != int32(80) {
157 framework.Logf("Expected port to be 80, got %d", *port.Port)
158 return false, nil
159 }
160 if len(epSlice.Endpoints) != 1 {
161 framework.Logf("Expected EndpointSlice to have 1 endpoints, got %d", len(epSlice.Endpoints))
162 return false, nil
163 }
164 endpoint := epSlice.Endpoints[0]
165 if len(endpoint.Addresses) != 1 {
166 framework.Logf("Expected EndpointSlice endpoint to have 1 address, got %d", len(endpoint.Addresses))
167 return false, nil
168 }
169 address := endpoint.Addresses[0]
170 if address != "10.2.3.4" {
171 framework.Logf("Expected EndpointSlice to have 10.2.3.4 as address, got %s", address)
172 return false, nil
173 }
174
175 return true, nil
176 }); err != nil {
177 framework.Failf("Did not find matching EndpointSlice for %s/%s: %s", svc.Namespace, svc.Name, err)
178 }
179 })
180
181 ginkgo.By("mirroring deletion of a custom Endpoint", func() {
182 err := cs.CoreV1().Endpoints(f.Namespace.Name).Delete(ctx, endpoints.Name, metav1.DeleteOptions{})
183 framework.ExpectNoError(err, "Unexpected error deleting Endpoints")
184
185
186 if err := wait.PollImmediate(2*time.Second, 12*time.Second, func() (bool, error) {
187 esList, err := cs.DiscoveryV1().EndpointSlices(f.Namespace.Name).List(ctx, metav1.ListOptions{
188 LabelSelector: discoveryv1.LabelServiceName + "=" + svc.Name,
189 })
190 if err != nil {
191 return false, err
192 }
193 if len(esList.Items) != 0 {
194 framework.Logf("Waiting for 0 EndpointSlices to exist, got %d", len(esList.Items))
195 return false, nil
196 }
197
198 return true, nil
199 }); err != nil {
200 framework.Failf("Did not find matching EndpointSlice for %s/%s: %s", svc.Namespace, svc.Name, err)
201 }
202 })
203 })
204
205 ginkgo.It("should mirror a custom Endpoint with multiple subsets and same IP address", func(ctx context.Context) {
206 ns := f.Namespace.Name
207 svc := createServiceReportErr(ctx, cs, f.Namespace.Name, &v1.Service{
208 ObjectMeta: metav1.ObjectMeta{
209 Name: "example-custom-endpoints",
210 },
211 Spec: v1.ServiceSpec{
212 Ports: []v1.ServicePort{
213 {
214 Name: "port80",
215 Port: 80,
216 Protocol: v1.ProtocolTCP,
217 },
218 {
219 Name: "port81",
220 Port: 81,
221 Protocol: v1.ProtocolTCP,
222 },
223 },
224 },
225 })
226
227
228 port8080 := []v1.ContainerPort{
229 {
230 ContainerPort: 8090,
231 Protocol: v1.ProtocolTCP,
232 },
233 }
234 port9090 := []v1.ContainerPort{
235 {
236 ContainerPort: 9090,
237 Protocol: v1.ProtocolTCP,
238 },
239 }
240
241 serverPod := e2epod.NewAgnhostPodFromContainers(
242 "", "pod-handle-http-request", nil,
243 e2epod.NewAgnhostContainer("container-handle-8090-request", nil, port8080, "netexec", "--http-port", "8090", "--udp-port", "-1"),
244 e2epod.NewAgnhostContainer("container-handle-9090-request", nil, port9090, "netexec", "--http-port", "9090", "--udp-port", "-1"),
245 )
246
247 pod := e2epod.NewPodClient(f).CreateSync(ctx, serverPod)
248
249 if pod.Status.PodIP == "" {
250 framework.Failf("PodIP not assigned for pod %s", pod.Name)
251 }
252
253
254 endpoints := &v1.Endpoints{
255 ObjectMeta: metav1.ObjectMeta{
256 Name: svc.Name,
257 },
258 Subsets: []v1.EndpointSubset{
259 {
260 Ports: []v1.EndpointPort{{
261 Name: "port80",
262 Port: 8090,
263 }},
264 Addresses: []v1.EndpointAddress{{
265 IP: pod.Status.PodIP,
266 }},
267 },
268 {
269 Ports: []v1.EndpointPort{{
270 Name: "port81",
271 Port: 9090,
272 }},
273 Addresses: []v1.EndpointAddress{{
274 IP: pod.Status.PodIP,
275 }},
276 },
277 },
278 }
279
280 ginkgo.By("mirroring a new custom Endpoint", func() {
281 _, err := cs.CoreV1().Endpoints(f.Namespace.Name).Create(context.TODO(), endpoints, metav1.CreateOptions{})
282 framework.ExpectNoError(err, "Unexpected error creating Endpoints")
283
284 if err := wait.PollImmediate(2*time.Second, 12*time.Second, func() (bool, error) {
285 esList, err := cs.DiscoveryV1().EndpointSlices(f.Namespace.Name).List(context.TODO(), metav1.ListOptions{
286 LabelSelector: discoveryv1.LabelServiceName + "=" + svc.Name,
287 })
288 if err != nil {
289 framework.Logf("Error listing EndpointSlices: %v", err)
290 return false, nil
291 }
292 if len(esList.Items) == 0 {
293 framework.Logf("Waiting for at least 1 EndpointSlice to exist, got %d", len(esList.Items))
294 return false, nil
295 }
296 return true, nil
297 }); err != nil {
298 framework.Failf("Did not find matching EndpointSlice for %s/%s: %s", svc.Namespace, svc.Name, err)
299 }
300 })
301
302
303 ginkgo.By("Creating a pause pods that will try to connect to the webservers")
304 pausePod := e2epod.NewAgnhostPod(ns, "pause-pod-0", nil, nil, nil)
305 e2epod.NewPodClient(f).CreateSync(ctx, pausePod)
306 dest1 := net.JoinHostPort(svc.Spec.ClusterIP, "80")
307 dest2 := net.JoinHostPort(svc.Spec.ClusterIP, "81")
308 execHostnameTest(*pausePod, dest1, pod.Name)
309 execHostnameTest(*pausePod, dest2, pod.Name)
310
311
312 ginkgo.By("mirroring deletion of a custom Endpoint", func() {
313 err := cs.CoreV1().Endpoints(f.Namespace.Name).Delete(context.TODO(), endpoints.Name, metav1.DeleteOptions{})
314 framework.ExpectNoError(err, "Unexpected error deleting Endpoints")
315
316
317 if err := wait.PollImmediate(2*time.Second, 12*time.Second, func() (bool, error) {
318 esList, err := cs.DiscoveryV1().EndpointSlices(f.Namespace.Name).List(context.TODO(), metav1.ListOptions{
319 LabelSelector: discoveryv1.LabelServiceName + "=" + svc.Name,
320 })
321 if err != nil {
322 return false, err
323 }
324 if len(esList.Items) != 0 {
325 framework.Logf("Waiting for 0 EndpointSlices to exist, got %d", len(esList.Items))
326 return false, nil
327 }
328
329 return true, nil
330 }); err != nil {
331 framework.Failf("Did not find matching EndpointSlice for %s/%s: %s", svc.Namespace, svc.Name, err)
332 }
333 })
334 })
335 })
336
View as plain text