From 1e550f4bf9f9567d2f136d72e1f7be63ce270f61 Mon Sep 17 00:00:00 2001 From: Martin Kennelly Date: Tue, 19 Nov 2024 13:43:16 +0000 Subject: [PATCH] EIP e2e: check EIP when pods from different ns connect to same net Signed-off-by: Martin Kennelly --- test/e2e/egressip.go | 116 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/test/e2e/egressip.go b/test/e2e/egressip.go index b778c28a66..ae061bd30c 100644 --- a/test/e2e/egressip.go +++ b/test/e2e/egressip.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/test/e2e/framework" e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl" e2enode "k8s.io/kubernetes/test/e2e/framework/node" + "k8s.io/kubernetes/test/e2e/framework/pod" e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" utilnet "k8s.io/utils/net" ) @@ -2712,6 +2713,121 @@ spec: "and verify the expected IP, failed for EgressIP %s: %v", egressIPName, err) }) + // two pods attached to different namespaces but the same role primary user defined network + // One pod is deleted and ensure connectivity for the other pod is ok + // The previous pod namespace is deleted and again, ensure connectivity for the other pod is ok + ginkgo.It("[OVN network] multiple namespaces sharing a primary networks", func() { + if !isNetworkSegmentationEnabled() || netConfigParams.role != "primary" { + ginkgo.Skip("Network segmentation is disabled or network isn't a role primary UDN") + } + ginkgo.By(fmt.Sprintf("Building another namespace api object, basename %s", f.BaseName)) + otherNetworkNamespace, err := f.CreateNamespace(context.Background(), f.BaseName, map[string]string{ + "e2e-framework": f.BaseName, + }) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + + ginkgo.By("namespace is connected to UDN, create a namespace attached to this primary as a layer3 UDN") + nadClient, err := nadclient.NewForConfig(f.ClientConfig()) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + netConfig := newNetworkAttachmentConfig(netConfigParams) + netConfig.namespace = otherNetworkNamespace.Name + _, err = nadClient.NetworkAttachmentDefinitions(otherNetworkNamespace.Name).Create( + context.Background(), + generateNAD(netConfig), + metav1.CreateOptions{}, + ) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + egressNodeAvailabilityHandler := egressNodeAvailabilityHandlerViaLabel{f} + ginkgo.By("1. Set one node as available for egress") + egressNodeAvailabilityHandler.Enable(egress1Node.name) + defer egressNodeAvailabilityHandler.Restore(egress1Node.name) + + selectedByEIPLabels := map[string]string{ + "wants": "egress", + } + pod1Namespace := f.Namespace + pod1Namespace.Labels = selectedByEIPLabels + updateNamespace(f, pod1Namespace) + pod2OtherNetworkNamespace := otherNetworkNamespace.Name + otherNetworkNamespace.Labels = selectedByEIPLabels + updateNamespace(f, otherNetworkNamespace) + + ginkgo.By("3. Create an EgressIP object with one egress IP defined") + // Assign the egress IP without conflicting with any node IP, + // the kind subnet is /16 or /64 so the following should be fine. + egressNodeIP := net.ParseIP(egress1Node.nodeIP) + egressIP1 := dupIP(egressNodeIP) + egressIP1[len(egressIP1)-2]++ + + var egressIPConfig = fmt.Sprintf(`apiVersion: k8s.ovn.org/v1 +kind: EgressIP +metadata: + name: ` + egressIPName + ` +spec: + egressIPs: + - ` + egressIP1.String() + ` + podSelector: + matchLabels: + wants: egress + namespaceSelector: + matchLabels: + wants: egress +`) + if err := os.WriteFile(egressIPYaml, []byte(egressIPConfig), 0644); err != nil { + framework.Failf("Unable to write CRD config to disk: %v", err) + } + defer func() { + if err := os.Remove(egressIPYaml); err != nil { + framework.Logf("Unable to remove the CRD config from disk: %v", err) + } + }() + + framework.Logf("Create the EgressIP configuration") + e2ekubectl.RunKubectlOrDie("default", "create", "-f", egressIPYaml) + + ginkgo.By("4. Check that the status is of length one and that it is assigned to egress1Node") + statuses := verifyEgressIPStatusLengthEquals(1, nil) + if statuses[0].Node != egress1Node.name { + framework.Failf("Step 4. Check that the status is of length one and that it is assigned to egress1Node, failed") + } + + ginkgo.By("5. Create two pods matching the EgressIP with each connected to the same network") + pod1, err := createGenericPodWithLabel(f, pod1Name, pod1Node.name, f.Namespace.Name, command, podEgressLabel) + framework.ExpectNoError(err, "5. Create one pod matching the EgressIP: running on egress1Node, failed: %v", err) + pod2, err := createGenericPodWithLabel(f, pod2Name, pod2Node.name, otherNetworkNamespace.Name, command, podEgressLabel) + framework.ExpectNoError(err, "5. Create one pod matching the EgressIP: running on egress2Node, failed: %v", err) + + gomega.Expect(pod.WaitForPodRunningInNamespace(context.TODO(), f.ClientSet, pod1)).Should(gomega.Succeed()) + gomega.Expect(pod.WaitForPodRunningInNamespace(context.TODO(), f.ClientSet, pod2)).Should(gomega.Succeed()) + + framework.ExpectNoError(err, "Step 5. Create one pod matching the EgressIP: running on egress1Node, failed, err: %v", err) + framework.Logf("Created pod %s on node %s", pod1Name, pod1Node.name) + framework.ExpectNoError(err, "Step 5. Create one pod matching the EgressIP: running on egress2Node, failed, err: %v", err) + framework.Logf("Created pod %s on node %s", pod2Name, pod2Node.name) + + ginkgo.By("6. Check connectivity from pod to an external node and verify that the srcIP is the expected egressIP") + err = wait.PollImmediate(retryInterval, retryTimeout, targetExternalContainerAndTest(targetNode, pod1Name, pod1Namespace.Name, true, []string{egressIP1.String()})) + framework.ExpectNoError(err, "Step 6. Check connectivity from pod to an external node and verify that the srcIP is the expected egressIP, failed: %v", err) + + ginkgo.By("7. Check connectivity from pod connected to the same network and verify that the srcIP is the expected egressIP") + err = wait.PollImmediate(retryInterval, retryTimeout, targetExternalContainerAndTest(targetNode, pod2Name, pod2OtherNetworkNamespace, true, []string{egressIP1.String()})) + framework.ExpectNoError(err, "Step 7. Check connectivity from pod connected to the same network and verify that the srcIP is the expected nodeIP, failed: %v", err) + + ginkgo.By("8. Delete pod in one namespace") + framework.ExpectNoError(pod.DeletePodWithWait(context.TODO(), f.ClientSet, pod1), "pod %s/%s deletion failed", pod1.Namespace, pod1.Name) + + ginkgo.By("9. Check connectivity from other pod and verify that the srcIP is the expected egressIP") + err = wait.PollImmediate(retryInterval, retryTimeout, targetExternalContainerAndTest(targetNode, pod2Name, pod2OtherNetworkNamespace, true, []string{egressIP1.String()})) + framework.ExpectNoError(err, "Step 9. Check connectivity from other pod and verify that the srcIP is the expected egressIP, failed: %v", err) + + ginkgo.By("10. Delete namespace with zero pods") + gomega.Expect(f.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), pod1.Namespace, metav1.DeleteOptions{})).To(gomega.Succeed()) + + ginkgo.By("11. Check connectivity from other pod and verify that the srcIP is the expected egressIP") + err = wait.PollImmediate(retryInterval, retryTimeout, targetExternalContainerAndTest(targetNode, pod2Name, pod2OtherNetworkNamespace, true, []string{egressIP1.String()})) + framework.ExpectNoError(err, "Step 11. Check connectivity from other pod and verify that the srcIP is the expected egressIP and verify that the srcIP is the expected nodeIP, failed: %v", err) + }) + ginkgo.DescribeTable("[OVN network] multiple namespaces with different primary networks", func(otherNetworkAttachParms networkAttachmentConfigParams) { if !isNetworkSegmentationEnabled() { ginkgo.Skip("Network segmentation is disabled")