diff --git a/Makefile b/Makefile index 7c1ecb4e85b..0138822d2ec 100644 --- a/Makefile +++ b/Makefile @@ -218,13 +218,13 @@ e2e: done; \ fi - printf "package underlay\n\nvar nodeNetworks = map[string]string{\n" > test/e2e/underlay/network.go + printf "package e2e\n\nvar nodeNetworks = map[string]string{\n" > test/e2e/network.go kind get nodes --name kube-ovn | while read node; do \ - printf "\`$$node\`: \`" >> test/e2e/underlay/network.go; \ - docker inspect -f '{{json .NetworkSettings.Networks.bridge}}' $$node >> test/e2e/underlay/network.go; \ - printf "\`,\n" >> test/e2e/underlay/network.go; \ + printf " \`$$node\`: \`" >> test/e2e/network.go; \ + docker inspect -f '{{json .NetworkSettings.Networks.bridge}}' $$node >> test/e2e/network.go; \ + printf "\`,\n" >> test/e2e/network.go; \ done - echo "}" >> test/e2e/underlay/network.go + echo "}" >> test/e2e/network.go docker pull kubeovn/pause:3.2 kind load docker-image --name kube-ovn kubeovn/pause:3.2 diff --git a/test/e2e-vlan-single-nic/e2e_suite_test.go b/test/e2e-vlan-single-nic/e2e_suite_test.go index b77129d4eb6..5307da47415 100644 --- a/test/e2e-vlan-single-nic/e2e_suite_test.go +++ b/test/e2e-vlan-single-nic/e2e_suite_test.go @@ -11,6 +11,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + _ "github.com/kubeovn/kube-ovn/test/e2e-vlan-single-nic/kubectl-ko" _ "github.com/kubeovn/kube-ovn/test/e2e-vlan-single-nic/node" "github.com/kubeovn/kube-ovn/test/e2e/framework" ) diff --git a/test/e2e-vlan-single-nic/kubectl-ko/ko.go b/test/e2e-vlan-single-nic/kubectl-ko/ko.go new file mode 100644 index 00000000000..2d1b621297b --- /dev/null +++ b/test/e2e-vlan-single-nic/kubectl-ko/ko.go @@ -0,0 +1,39 @@ +package kubectl_ko + +import ( + "context" + "fmt" + "os" + "os/exec" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + kubeovn "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "github.com/kubeovn/kube-ovn/pkg/util" + "github.com/kubeovn/kube-ovn/test/e2e/framework" +) + +var _ = Describe("[kubectl-ko]", func() { + f := framework.NewFramework("kubectl-ko", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME"))) + + It("trace", func() { + pods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=kube-ovn-pinger"}) + Expect(err).NotTo(HaveOccurred()) + pod := pods.Items[0] + if util.CheckProtocol(pod.Status.PodIP) == kubeovn.ProtocolIPv6 { + return + } + + output, err := exec.Command("kubectl", "ko", "trace", fmt.Sprintf("kube-system/%s", pod.Name), "114.114.114.114", "icmp").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + + output, err = exec.Command("kubectl", "ko", "trace", fmt.Sprintf("kube-system/%s", pod.Name), "114.114.114.114", "tcp", "80").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + + output, err = exec.Command("kubectl", "ko", "trace", fmt.Sprintf("kube-system/%s", pod.Name), "114.114.114.114", "udp", "53").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + }) +}) diff --git a/test/e2e-vlan-single-nic/node/node.go b/test/e2e-vlan-single-nic/node/node.go index e016f24949b..ab09bb87a9a 100644 --- a/test/e2e-vlan-single-nic/node/node.go +++ b/test/e2e-vlan-single-nic/node/node.go @@ -36,10 +36,12 @@ var _ = Describe("[Vlan Node]", func() { f := framework.NewFramework("node", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME"))) var network *nodeNetwork - if len(networkJSON) != 0 { - network = new(nodeNetwork) - Expect(json.Unmarshal(networkJSON, network)).NotTo(HaveOccurred()) - } + BeforeEach(func() { + if len(networkJSON) != 0 { + network = new(nodeNetwork) + Expect(json.Unmarshal(networkJSON, network)).NotTo(HaveOccurred()) + } + }) It("Single NIC", func() { nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 927e7832e53..ded1b4ddbaa 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -1,7 +1,8 @@ -package e2e_test +package e2e import ( "context" + "encoding/json" "fmt" "os" "testing" @@ -20,9 +21,19 @@ import ( _ "github.com/kubeovn/kube-ovn/test/e2e/node" _ "github.com/kubeovn/kube-ovn/test/e2e/service" _ "github.com/kubeovn/kube-ovn/test/e2e/subnet" - _ "github.com/kubeovn/kube-ovn/test/e2e/underlay" + "github.com/kubeovn/kube-ovn/test/e2e/underlay" ) +type nodeNetwork struct { + Gateway string + IPAddress string + IPPrefixLen int + IPv6Gateway string + GlobalIPv6Address string + GlobalIPv6PrefixLen int + MacAddress string +} + func TestE2e(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Kube-OVN E2E Suite") @@ -47,6 +58,16 @@ var _ = SynchronizedAfterSuite(func() {}, func() { if err != nil { Fail(err.Error()) } + + err = f.OvnClientSet.KubeovnV1().Vlans().DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: "e2e=true"}) + if err != nil { + Fail(err.Error()) + } + + err = f.OvnClientSet.KubeovnV1().ProviderNetworks().DeleteCollection(context.Background(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: "e2e=true"}) + if err != nil { + Fail(err.Error()) + } }) var _ = SynchronizedBeforeSuite(func() []byte { @@ -80,5 +101,147 @@ var _ = SynchronizedBeforeSuite(func() []byte { if err != nil { Fail(err.Error()) } + + // underlay + var underlayNodeIPs []string + var underlayCIDR, underlayGateway string + for node, network := range nodeNetworks { + var info nodeNetwork + if err = json.Unmarshal([]byte(network), &info); err != nil { + Fail("invalid node network information: " + err.Error()) + } + + underlay.SetNodeMac(node, info.MacAddress) + if info.IPAddress != "" { + underlay.AddNodeIP(info.IPAddress) + underlayNodeIPs = append(underlayNodeIPs, info.IPAddress) + underlay.AddNodeAddrs(node, fmt.Sprintf("%s/%d", info.IPAddress, info.IPPrefixLen)) + if underlayCIDR == "" { + underlayCIDR = fmt.Sprintf("%s/%d", info.IPAddress, info.IPPrefixLen) + } + } + if info.GlobalIPv6Address != "" { + underlay.AddNodeAddrs(node, fmt.Sprintf("%s/%d", info.GlobalIPv6Address, info.GlobalIPv6PrefixLen)) + } + if info.Gateway != "" { + underlay.AddNodeRoutes(node, fmt.Sprintf("default via %s ", info.Gateway)) + if underlayGateway == "" { + underlayGateway = info.Gateway + } + } + if info.IPv6Gateway != "" { + underlay.AddNodeRoutes(node, fmt.Sprintf("default via %s ", info.IPv6Gateway)) + } + } + underlay.SetCIDR(underlayCIDR) + + nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) + if err != nil { + Fail(err.Error()) + } + cniPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=kube-ovn-cni"}) + if err != nil { + Fail(err.Error()) + } + + for i := range nodes.Items { + var nodeIP string + for _, addr := range nodes.Items[i].Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + nodeIP = addr.Address + break + } + } + if nodeIP == "" { + Fail("failed to get IP of node " + nodes.Items[i].Name) + } + + var cniPod *corev1.Pod + for _, pod := range cniPods.Items { + if pod.Status.HostIP == nodeIP { + cniPod = &pod + break + } + } + if cniPod == nil { + Fail("failed to get CNI pod on node " + nodes.Items[i].Name) + } + + // change MTU + mtu := 1500 - (i+1)*5 + cmd := fmt.Sprintf("ip link set %s mtu %d", underlay.ProviderInterface, mtu) + if _, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil); err != nil { + Fail(fmt.Sprintf("failed to set MTU of %s on node %s: %v", underlay.ProviderInterface, nodes.Items[i].Name, err)) + } + underlay.SetNodeMTU(nodes.Items[i].Name, mtu) + } + + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: underlay.Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + } + if _, err = f.KubeClientSet.CoreV1().Namespaces().Create(context.Background(), &ns, metav1.CreateOptions{}); err != nil { + Fail(err.Error()) + } + + // create provider network + pn := kubeovn.ProviderNetwork{ + ObjectMeta: metav1.ObjectMeta{ + Name: underlay.ProviderNetwork, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: kubeovn.ProviderNetworkSpec{ + DefaultInterface: underlay.ProviderInterface, + }, + } + if _, err = f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}); err != nil { + Fail("failed to create provider network: " + err.Error()) + } + if err = f.WaitProviderNetworkReady(pn.Name); err != nil { + Fail("provider network failed: " + err.Error()) + } + + // create vlan + vlan := kubeovn.Vlan{ + ObjectMeta: metav1.ObjectMeta{ + Name: underlay.Vlan, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: kubeovn.VlanSpec{ + ID: 0, + Provider: pn.Name, + }, + } + if _, err = f.OvnClientSet.KubeovnV1().Vlans().Create(context.Background(), &vlan, metav1.CreateOptions{}); err != nil { + Fail("failed to create vlan: " + err.Error()) + } + if err = f.WaitProviderNetworkReady(pn.Name); err != nil { + Fail("provider network failed: " + err.Error()) + } + + // create subnet + subnet := kubeovn.Subnet{ + ObjectMeta: metav1.ObjectMeta{ + Name: underlay.Subnet, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: kubeovn.SubnetSpec{ + CIDRBlock: underlayCIDR, + Gateway: underlayGateway, + ExcludeIps: underlayNodeIPs, + Vlan: vlan.Name, + UnderlayGateway: true, + Namespaces: []string{underlay.Namespace}, + }, + } + if _, err = f.OvnClientSet.KubeovnV1().Subnets().Create(context.Background(), &subnet, metav1.CreateOptions{}); err != nil { + Fail("failed to create subnet: " + err.Error()) + } + if err = f.WaitSubnetReady(subnet.Name); err != nil { + Fail("subnet failed: " + err.Error()) + } + return nil }, func(data []byte) {}) diff --git a/test/e2e/underlay/network.go b/test/e2e/network.go similarity index 70% rename from test/e2e/underlay/network.go rename to test/e2e/network.go index f7def9b44f1..370e45a9e81 100644 --- a/test/e2e/underlay/network.go +++ b/test/e2e/network.go @@ -1,3 +1,3 @@ -package underlay +package e2e var nodeNetworks = map[string]string{} diff --git a/test/e2e/underlay/const.go b/test/e2e/underlay/const.go deleted file mode 100644 index 43337ddeeae..00000000000 --- a/test/e2e/underlay/const.go +++ /dev/null @@ -1,3 +0,0 @@ -package underlay - -const providerInterface = "eth1" diff --git a/test/e2e/underlay/normal.go b/test/e2e/underlay/normal.go deleted file mode 100644 index 6d993f1864e..00000000000 --- a/test/e2e/underlay/normal.go +++ /dev/null @@ -1,504 +0,0 @@ -package underlay - -import ( - "context" - "encoding/json" - "fmt" - "os" - "strconv" - "strings" - "time" - - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - kubeovn "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" - "github.com/kubeovn/kube-ovn/pkg/util" - "github.com/kubeovn/kube-ovn/test/e2e/framework" -) - -type nodeNetwork struct { - Gateway string - IPAddress string - IPPrefixLen int - IPv6Gateway string - GlobalIPv6Address string - GlobalIPv6PrefixLen int - MacAddress string -} - -var _ = Describe("[Provider Network]", func() { - f := framework.NewFramework("provider-network", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME"))) - - nodeMac := make(map[string]string, len(nodeNetworks)) - nodeAddrs := make(map[string][]string, len(nodeNetworks)) - nodeRoutes := make(map[string][]string, len(nodeNetworks)) - for node, network := range nodeNetworks { - var info nodeNetwork - Expect(json.Unmarshal([]byte(network), &info)).NotTo(HaveOccurred()) - nodeMac[node] = info.MacAddress - if info.IPAddress != "" { - nodeAddrs[node] = append(nodeAddrs[node], fmt.Sprintf("%s/%d", info.IPAddress, info.IPPrefixLen)) - } - if info.GlobalIPv6Address != "" { - nodeAddrs[node] = append(nodeAddrs[node], fmt.Sprintf("%s/%d", info.GlobalIPv6Address, info.GlobalIPv6PrefixLen)) - } - if info.Gateway != "" { - nodeRoutes[node] = append(nodeRoutes[node], fmt.Sprintf("default via %s ", info.Gateway)) - } - if info.IPv6Gateway != "" { - nodeRoutes[node] = append(nodeRoutes[node], fmt.Sprintf("default via %s ", info.IPv6Gateway)) - } - Expect(nodeAddrs[node]).NotTo(BeEmpty()) - Expect(nodeRoutes[node]).NotTo(BeEmpty()) - } - - BeforeEach(func() { - if err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete provider network %s: %v", f.GetName(), err) - } - } - time.Sleep(3 * time.Second) - }) - AfterEach(func() { - if err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete provider network %s: %v", f.GetName(), err) - } - } - time.Sleep(3 * time.Second) - }) - - Describe("Create", func() { - It("normal", func() { - name := f.GetName() - - By("create provider network") - pn := kubeovn.ProviderNetwork{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.ProviderNetworkSpec{ - DefaultInterface: providerInterface, - }, - } - _, err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitProviderNetworkReady(name) - Expect(err).NotTo(HaveOccurred()) - - By("validate node labels") - nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - - for _, node := range nodes.Items { - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkExcludeTemplate, name)]).To(BeEmpty()) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, name)]).To(Equal(providerInterface)) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, name)]).To(Equal("true")) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, name)]).NotTo(BeEmpty()) - } - - By("validate provider interface and OVS bridge") - ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) - Expect(err).NotTo(HaveOccurred()) - Expect(ovsPods).NotTo(BeNil()) - for _, node := range nodes.Items { - var hostIP string - for _, addr := range node.Status.Addresses { - if addr.Type == corev1.NodeInternalIP { - hostIP = addr.Address - break - } - } - Expect(hostIP).NotTo(BeEmpty()) - - var ovsPod *corev1.Pod - for _, pod := range ovsPods.Items { - if pod.Status.HostIP == hostIP { - ovsPod = &pod - break - } - } - Expect(ovsPod).NotTo(BeNil()) - - stdout, _, err := f.ExecToPodThroughAPI("ip addr show "+providerInterface, "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - addrNotFound := make([]bool, len(nodeAddrs[node.Name])) - for _, s := range strings.Split(stdout, "\n") { - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if !strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) && !strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrNotFound[i] = true - break - } - } - } - for _, found := range addrNotFound { - Expect(found).To(BeTrue()) - } - - stdout, _, err = f.ExecToPodThroughAPI("ovs-vsctl list-ports "+util.ExternalBridgeName(name), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var portFound bool - for _, port := range strings.Split(stdout, "\n") { - if port == providerInterface { - portFound = true - break - } - } - Expect(portFound).To(BeTrue()) - - stdout, _, err = f.ExecToPodThroughAPI("ip addr show "+util.ExternalBridgeName(name), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var isUp bool - addrFound := make([]bool, len(nodeAddrs[node.Name])) - for i, s := range strings.Split(stdout, "\n") { - if i == 0 { - idx1, idx2 := strings.IndexRune(s, '<'), strings.IndexRune(s, '>') - if idx1 > 0 && idx2 > idx1+1 { - for _, state := range strings.Split(s[idx1+1:idx2], ",") { - if state == "UP" { - isUp = true - break - } - } - } - continue - } - if i == 1 { - if mac := nodeMac[node.Name]; mac != "" { - Expect(strings.TrimSpace(s)).To(HavePrefix("link/ether %s ", mac)) - continue - } - } - - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) || strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrFound[i] = true - break - } - } - } - Expect(isUp).To(BeTrue()) - for _, found := range addrFound { - Expect(found).To(BeTrue()) - } - } - }) - - It("mtu", func() { - ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) - Expect(err).NotTo(HaveOccurred()) - for i, pod := range ovsPods.Items { - _, _, err := f.ExecToPodThroughAPI(fmt.Sprintf("ip link set %s mtu %d", providerInterface, 1600+i*10), "openvswitch", pod.Name, pod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - } - - name := f.GetName() - By("create provider network") - pn := kubeovn.ProviderNetwork{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.ProviderNetworkSpec{ - DefaultInterface: providerInterface, - }, - } - _, err = f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitProviderNetworkReady(name) - Expect(err).NotTo(HaveOccurred()) - - By("validate node labels and OVS bridge MTU") - nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - readyNodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf(util.ProviderNetworkReadyTemplate+"=true", name)}) - Expect(err).NotTo(HaveOccurred()) - Expect(len(nodes.Items)).To(Equal(len(readyNodes.Items))) - - for i, pod := range ovsPods.Items { - mtu := 1600 + i*10 - - var found bool - for _, node := range readyNodes.Items { - for _, addr := range node.Status.Addresses { - if addr.Address == pod.Status.HostIP && addr.Type == corev1.NodeInternalIP { - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, name)]).To(Equal(strconv.Itoa(mtu))) - found = true - break - } - } - if found { - break - } - } - Expect(found).To(BeTrue()) - - output, _, err := f.ExecToPodThroughAPI("ip link show "+util.ExternalBridgeName(name), "openvswitch", pod.Name, pod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(output).To(ContainSubstring(" mtu %d ", mtu)) - } - }) - - It("exclude node", func() { - name := f.GetName() - - By("create provider network") - nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - - excludedNode := nodes.Items[0].Name - pn := kubeovn.ProviderNetwork{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.ProviderNetworkSpec{ - DefaultInterface: providerInterface, - ExcludeNodes: []string{excludedNode}, - }, - } - _, err = f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitProviderNetworkReady(name) - Expect(err).NotTo(HaveOccurred()) - - By("validate node labels") - nodes, err = f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - - for _, node := range nodes.Items { - if node.Name == excludedNode { - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkExcludeTemplate, name)]).To(Equal("true")) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, name)]).To(BeEmpty()) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, name)]).To(BeEmpty()) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, name)]).To(BeEmpty()) - } else { - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkExcludeTemplate, name)]).To(BeEmpty()) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, name)]).To(Equal(providerInterface)) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, name)]).To(Equal("true")) - Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, name)]).NotTo(BeEmpty()) - } - } - - By("validate provider interface and OVS bridge") - ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) - Expect(err).NotTo(HaveOccurred()) - Expect(ovsPods).NotTo(BeNil()) - for _, node := range nodes.Items { - - var hostIP string - for _, addr := range node.Status.Addresses { - if addr.Type == corev1.NodeInternalIP { - hostIP = addr.Address - break - } - } - Expect(hostIP).NotTo(BeEmpty()) - - var ovsPod *corev1.Pod - for _, pod := range ovsPods.Items { - if pod.Status.HostIP == hostIP { - ovsPod = &pod - break - } - } - Expect(ovsPod).NotTo(BeNil()) - - if node.Name == excludedNode { - stdout, _, err := f.ExecToPodThroughAPI("ovs-vsctl list-br", "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var brFound bool - for _, br := range strings.Split(stdout, "\n") { - if br == util.ExternalBridgeName(name) { - brFound = true - break - } - } - Expect(brFound).To(BeFalse()) - - stdout, _, err = f.ExecToPodThroughAPI("ip addr show "+providerInterface, "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - addrFound := make([]bool, len(nodeAddrs[node.Name])) - for _, s := range strings.Split(stdout, "\n") { - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) || strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrFound[i] = true - break - } - } - } - for _, found := range addrFound { - Expect(found).To(BeTrue()) - } - } else { - stdout, _, err := f.ExecToPodThroughAPI("ip addr show "+providerInterface, "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - addrNotFound := make([]bool, len(nodeAddrs[node.Name])) - for _, s := range strings.Split(stdout, "\n") { - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if !strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) && !strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrNotFound[i] = true - break - } - } - } - for _, found := range addrNotFound { - Expect(found).To(BeTrue()) - } - - stdout, _, err = f.ExecToPodThroughAPI("ovs-vsctl list-ports "+util.ExternalBridgeName(name), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var portFound bool - for _, port := range strings.Split(stdout, "\n") { - if port == providerInterface { - portFound = true - break - } - } - Expect(portFound).To(BeTrue()) - - stdout, _, err = f.ExecToPodThroughAPI("ip addr show "+util.ExternalBridgeName(name), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var isUp bool - addrFound := make([]bool, len(nodeAddrs[node.Name])) - for i, s := range strings.Split(stdout, "\n") { - if i == 0 { - idx1, idx2 := strings.IndexRune(s, '<'), strings.IndexRune(s, '>') - if idx1 > 0 && idx2 > idx1+1 { - for _, state := range strings.Split(s[idx1+1:idx2], ",") { - if state == "UP" { - isUp = true - break - } - } - } - continue - } - if i == 1 { - if mac := nodeMac[node.Name]; mac != "" { - Expect(strings.TrimSpace(s)).To(HavePrefix("link/ether %s ", mac)) - continue - } - } - - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) || strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrFound[i] = true - break - } - } - } - Expect(isUp).To(BeTrue()) - for _, found := range addrFound { - Expect(found).To(BeTrue()) - } - } - } - }) - }) - - Describe("Delete", func() { - It("normal", func() { - name := f.GetName() - - By("create provider network") - pn := kubeovn.ProviderNetwork{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.ProviderNetworkSpec{ - DefaultInterface: providerInterface, - }, - } - _, err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitProviderNetworkReady(name) - Expect(err).NotTo(HaveOccurred()) - - By("delete provider network") - err = f.OvnClientSet.KubeovnV1().ProviderNetworks().Delete(context.Background(), pn.Name, metav1.DeleteOptions{}) - Expect(err).NotTo(HaveOccurred()) - time.Sleep(3 * time.Second) - - By("validate node labels") - readyNodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{LabelSelector: fmt.Sprintf(util.ProviderNetworkReadyTemplate+"=true", name)}) - Expect(err).NotTo(HaveOccurred()) - Expect(len(readyNodes.Items)).To(Equal(0)) - - By("validate provider interface and OVS bridge") - ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) - Expect(err).NotTo(HaveOccurred()) - Expect(ovsPods).NotTo(BeNil()) - for _, node := range readyNodes.Items { - var hostIP string - for _, addr := range node.Status.Addresses { - if addr.Type == corev1.NodeInternalIP { - hostIP = addr.Address - break - } - } - Expect(hostIP).NotTo(BeEmpty()) - - var ovsPod *corev1.Pod - for _, pod := range ovsPods.Items { - if pod.Status.HostIP == hostIP { - ovsPod = &pod - break - } - } - Expect(ovsPod).NotTo(BeNil()) - - stdout, _, err := f.ExecToPodThroughAPI("ovs-vsctl list-br", "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - var brFound bool - for _, br := range strings.Split(stdout, "\n") { - if br == util.ExternalBridgeName(name) { - brFound = true - break - } - } - Expect(brFound).To(BeFalse()) - - stdout, _, err = f.ExecToPodThroughAPI("ip addr show "+providerInterface, "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - - addrFound := make([]bool, len(nodeAddrs[node.Name])) - for _, s := range strings.Split(stdout, "\n") { - s = strings.TrimSpace(s) - for i, addr := range nodeAddrs[node.Name] { - if strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) || strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { - addrFound[i] = true - break - } - } - } - for _, found := range addrFound { - Expect(found).To(BeTrue()) - } - } - }) - }) -}) diff --git a/test/e2e/underlay/pod.go b/test/e2e/underlay/pod.go deleted file mode 100644 index bd489ef59b5..00000000000 --- a/test/e2e/underlay/pod.go +++ /dev/null @@ -1,267 +0,0 @@ -package underlay - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/klog" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - kubeovn "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" - "github.com/kubeovn/kube-ovn/pkg/util" - "github.com/kubeovn/kube-ovn/test/e2e/framework" -) - -const testImage = "kubeovn/pause:3.2" - -var _ = Describe("[Underlay Pod]", func() { - f := framework.NewFramework("underlay-pod", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME"))) - - var cidr, gateway string - var nodeIPs []string - for _, network := range nodeNetworks { - var info nodeNetwork - Expect(json.Unmarshal([]byte(network), &info)).NotTo(HaveOccurred()) - - if info.IPAddress != "" { - nodeIPs = append(nodeIPs, info.IPAddress) - if cidr == "" { - cidr = fmt.Sprintf("%s/%d", info.IPAddress, info.IPPrefixLen) - } - } - if gateway == "" && info.Gateway != "" { - gateway = info.Gateway - } - } - Expect(cidr).NotTo(BeEmpty()) - Expect(gateway).NotTo(BeEmpty()) - Expect(nodeIPs).NotTo(BeEmpty()) - if len(nodeIPs) < 2 { - return - } - - namespace := "default" - BeforeEach(func() { - pods, err := f.KubeClientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "e2e=true"}) - if err != nil { - klog.Fatalf("failed to list pods: %v", err) - } - for _, pod := range pods.Items { - if err = f.KubeClientSet.CoreV1().Pods(namespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete pod %s: %v", pod.Name, err) - } - } - } - }) - BeforeEach(func() { - if err := f.OvnClientSet.KubeovnV1().Subnets().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete subnet %s: %v", f.GetName(), err) - } - } - }) - BeforeEach(func() { - if err := f.OvnClientSet.KubeovnV1().Vlans().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete vlan %s: %v", f.GetName(), err) - } - } - }) - BeforeEach(func() { - if err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete provider network %s: %v", f.GetName(), err) - - } - } - time.Sleep(3 * time.Second) - }) - - AfterEach(func() { - if err := f.OvnClientSet.KubeovnV1().ProviderNetworks().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete provider network %s, %v", f.GetName(), err) - - } - } - time.Sleep(3 * time.Second) - }) - AfterEach(func() { - if err := f.OvnClientSet.KubeovnV1().Vlans().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete vlan %s: %v", f.GetName(), err) - - } - } - }) - AfterEach(func() { - if err := f.OvnClientSet.KubeovnV1().Subnets().Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete subnet %s: %v", f.GetName(), err) - - } - } - }) - AfterEach(func() { - pods, err := f.KubeClientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "e2e=true"}) - if err != nil { - klog.Fatalf("failed to list pods: %v", err) - } - for _, pod := range pods.Items { - if err = f.KubeClientSet.CoreV1().Pods(namespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{}); err != nil { - if !k8serrors.IsNotFound(err) { - klog.Fatalf("failed to delete pod %s: %v", pod.Name, err) - } - } - } - }) - - Describe("Connectivity", func() { - It("normal", func() { - name := f.GetName() - - By("validate node count") - nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) - Expect(err).NotTo(HaveOccurred()) - Expect(len(nodes.Items) > 1).To(BeTrue()) - - By("create provider network") - pn := kubeovn.ProviderNetwork{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.ProviderNetworkSpec{ - DefaultInterface: providerInterface, - }, - } - _, err = f.OvnClientSet.KubeovnV1().ProviderNetworks().Create(context.Background(), &pn, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitProviderNetworkReady(name) - Expect(err).NotTo(HaveOccurred()) - - By("create vlan") - vlan := kubeovn.Vlan{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.VlanSpec{ - ID: 0, - Provider: pn.Name, - }, - } - _, err = f.OvnClientSet.KubeovnV1().Vlans().Create(context.Background(), &vlan, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - - By("create subnet") - subnet := kubeovn.Subnet{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{"e2e": "true"}, - }, - Spec: kubeovn.SubnetSpec{ - CIDRBlock: cidr, - Gateway: gateway, - ExcludeIps: append(nodeIPs, gateway), - Vlan: vlan.Name, - UnderlayGateway: true, - }, - } - _, err = f.OvnClientSet.KubeovnV1().Subnets().Create(context.Background(), &subnet, metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - err = f.WaitSubnetReady(subnet.Name) - Expect(err).NotTo(HaveOccurred()) - - pods := make([]*corev1.Pod, 2) - - By("create pods") - var autoMount bool - for i := 0; i < 2; i++ { - pods[i] = &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%d", name, i+1), - Namespace: namespace, - Labels: map[string]string{"e2e": "true"}, - Annotations: map[string]string{util.LogicalSwitchAnnotation: subnet.Name}, - }, - Spec: corev1.PodSpec{ - NodeName: nodes.Items[i].Name, - Containers: []corev1.Container{ - { - Name: name, - Image: testImage, - ImagePullPolicy: corev1.PullIfNotPresent, - }, - }, - AutomountServiceAccountToken: &autoMount, - }, - } - _, err = f.KubeClientSet.CoreV1().Pods(namespace).Create(context.Background(), pods[i], metav1.CreateOptions{}) - Expect(err).NotTo(HaveOccurred()) - pods[i], err = f.WaitPodReady(pods[i].Name, namespace) - Expect(err).NotTo(HaveOccurred()) - } - - By("get ovs & cni pods") - ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) - Expect(err).NotTo(HaveOccurred()) - Expect(ovsPods).NotTo(BeNil()) - Expect(len(ovsPods.Items)).To(Equal(len(nodes.Items))) - - cniPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=kube-ovn-cni"}) - Expect(err).NotTo(HaveOccurred()) - Expect(cniPods).NotTo(BeNil()) - Expect(len(cniPods.Items)).To(Equal(len(nodes.Items))) - - for i := 0; i < 2; i++ { - var hostIP string - for _, addr := range nodes.Items[i].Status.Addresses { - if addr.Type == corev1.NodeInternalIP { - hostIP = addr.Address - break - } - } - Expect(hostIP).NotTo(BeEmpty()) - - var cniPod *corev1.Pod - for _, pod := range cniPods.Items { - if pod.Status.HostIP == hostIP { - cniPod = &pod - break - } - } - Expect(cniPod).NotTo(BeNil()) - - cmd := fmt.Sprintf("ovs-vsctl --no-heading --columns=external_ids find interface external-ids:pod_name=%s external-ids:pod_namespace=%s", pods[i].Name, namespace) - stdout, _, err := f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - var netns string - for _, field := range strings.Fields(stdout) { - if strings.HasPrefix(field, "pod_netns=") { - netns = strings.TrimPrefix(field, "pod_netns=") - netns = netns[:len(netns)-1] - break - } - } - Expect(netns).NotTo(BeEmpty()) - - cmd = fmt.Sprintf("nsenter --net=%s ping -c1 -W1 %s", filepath.Join("/var/run/netns", netns), pods[1-i].Status.PodIP) - stdout, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) - Expect(err).NotTo(HaveOccurred()) - Expect(stdout).To(ContainSubstring(" 0% packet loss")) - } - }) - }) -}) diff --git a/test/e2e/underlay/underlay.go b/test/e2e/underlay/underlay.go new file mode 100644 index 00000000000..eab38d1bf0b --- /dev/null +++ b/test/e2e/underlay/underlay.go @@ -0,0 +1,576 @@ +package underlay + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + kubeovn "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "github.com/kubeovn/kube-ovn/pkg/util" + "github.com/kubeovn/kube-ovn/test/e2e/framework" +) + +const ( + ProviderInterface = "eth1" + + ProviderNetwork = "net1" + Vlan = "net1-flat" + Subnet = "net1-subnet1" + Namespace = "underlay" + + testImage = "kubeovn/pause:3.2" +) + +var ( + cidr string + nodeIPs []string + + nodeMac = make(map[string]string) + nodeAddrs = make(map[string][]string) + nodeRoutes = make(map[string][]string) + nodeMTU = make(map[string]int) +) + +func SetCIDR(s string) { + cidr = s +} +func AddNodeIP(ip string) { + nodeIPs = append(nodeIPs, ip) +} + +func SetNodeMac(node, mac string) { + nodeMac[node] = mac +} +func AddNodeAddrs(node, addr string) { + nodeAddrs[node] = append(nodeAddrs[node], addr) +} +func AddNodeRoutes(node, route string) { + nodeRoutes[node] = append(nodeRoutes[node], route) +} +func SetNodeMTU(node string, mtu int) { + nodeMTU[node] = mtu +} + +var _ = Describe("[Underlay]", func() { + f := framework.NewFramework("underlay", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME"))) + + Context("[Provider Network]", func() { + It("normal", func() { + By("validate node labels") + nodes, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) + Expect(err).NotTo(HaveOccurred()) + + for _, node := range nodes.Items { + Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkExcludeTemplate, ProviderNetwork)]).To(BeEmpty()) + Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkInterfaceTemplate, ProviderNetwork)]).To(Equal(ProviderInterface)) + Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkReadyTemplate, ProviderNetwork)]).To(Equal("true")) + Expect(node.Labels[fmt.Sprintf(util.ProviderNetworkMtuTemplate, ProviderNetwork)]).NotTo(BeEmpty()) + } + + By("validate OVS bridge") + ovsPods, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=ovs"}) + Expect(err).NotTo(HaveOccurred()) + Expect(ovsPods).NotTo(BeNil()) + for _, node := range nodes.Items { + var hostIP string + for _, addr := range node.Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + hostIP = addr.Address + break + } + } + Expect(hostIP).NotTo(BeEmpty()) + + var ovsPod *corev1.Pod + for _, pod := range ovsPods.Items { + if pod.Status.HostIP == hostIP { + ovsPod = &pod + break + } + } + Expect(ovsPod).NotTo(BeNil()) + + stdout, _, err := f.ExecToPodThroughAPI("ip addr show "+ProviderInterface, "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + + addrNotFound := make([]bool, len(nodeAddrs[node.Name])) + for _, s := range strings.Split(stdout, "\n") { + s = strings.TrimSpace(s) + for i, addr := range nodeAddrs[node.Name] { + if !strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) && !strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { + addrNotFound[i] = true + break + } + } + } + for _, found := range addrNotFound { + Expect(found).To(BeTrue()) + } + + stdout, _, err = f.ExecToPodThroughAPI("ovs-vsctl list-ports "+util.ExternalBridgeName(ProviderNetwork), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + + var portFound bool + for _, port := range strings.Split(stdout, "\n") { + if port == ProviderInterface { + portFound = true + break + } + } + Expect(portFound).To(BeTrue()) + + stdout, _, err = f.ExecToPodThroughAPI("ip addr show "+util.ExternalBridgeName(ProviderNetwork), "openvswitch", ovsPod.Name, ovsPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + + var isUp bool + addrFound := make([]bool, len(nodeAddrs[node.Name])) + for i, s := range strings.Split(stdout, "\n") { + if i == 0 { + idx1, idx2 := strings.IndexRune(s, '<'), strings.IndexRune(s, '>') + if idx1 > 0 && idx2 > idx1+1 { + for _, state := range strings.Split(s[idx1+1:idx2], ",") { + if state == "UP" { + isUp = true + break + } + } + } + continue + } + if i == 1 { + if mac := nodeMac[node.Name]; mac != "" { + Expect(strings.TrimSpace(s)).To(HavePrefix("link/ether %s ", mac)) + continue + } + } + + s = strings.TrimSpace(s) + for i, addr := range nodeAddrs[node.Name] { + if strings.HasPrefix(s, fmt.Sprintf("inet %s ", addr)) || strings.HasPrefix(s, fmt.Sprintf("inet6 %s ", addr)) { + addrFound[i] = true + break + } + } + } + Expect(isUp).To(BeTrue()) + for _, found := range addrFound { + Expect(found).To(BeTrue()) + } + } + }) + }) + + Context("[Pod]", func() { + var cniPods map[string]corev1.Pod + BeforeEach(func() { + nodeList, err := f.KubeClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(nodeList).NotTo(BeNil()) + + podList, err := f.KubeClientSet.CoreV1().Pods("kube-system").List(context.Background(), metav1.ListOptions{LabelSelector: "app=kube-ovn-cni"}) + Expect(err).NotTo(HaveOccurred()) + Expect(podList).NotTo(BeNil()) + Expect(len(podList.Items)).To(Equal(len(nodeList.Items))) + + cniPods = make(map[string]corev1.Pod) + for _, node := range nodeList.Items { + var nodeIP string + for _, addr := range node.Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + nodeIP = addr.Address + break + } + } + Expect(nodeIP).NotTo(BeEmpty()) + + var cniPod *corev1.Pod + for _, pod := range podList.Items { + if pod.Status.HostIP == nodeIP { + cniPod = &pod + break + } + } + Expect(cniPod).NotTo(BeNil()) + cniPods[node.Name] = *cniPod + } + }) + + Context("[MTU]", func() { + BeforeEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + AfterEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + + It("normal", func() { + By("create pod") + var autoMount bool + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.GetName(), + Namespace: Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: f.GetName(), + Image: testImage, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + AutomountServiceAccountToken: &autoMount, + }, + } + _, err := f.KubeClientSet.CoreV1().Pods(Namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + pod, err = f.WaitPodReady(pod.Name, Namespace) + Expect(err).NotTo(HaveOccurred()) + Expect(pod.Spec.NodeName).NotTo(BeEmpty()) + + By("get cni pod") + cniPod, ok := cniPods[pod.Spec.NodeName] + Expect(ok).To(BeTrue()) + + By("get pod's netns") + cmd := fmt.Sprintf("ovs-vsctl --no-heading --columns=external_ids find interface external-ids:pod_name=%s external-ids:pod_namespace=%s", pod.Name, Namespace) + stdout, _, err := f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + var netns string + for _, field := range strings.Fields(stdout) { + if strings.HasPrefix(field, "pod_netns=") { + netns = strings.TrimPrefix(field, "pod_netns=") + netns = netns[:len(netns)-1] + break + } + } + Expect(netns).NotTo(BeEmpty()) + + By("validate pod's MTU") + cmd = fmt.Sprintf("nsenter --net=%s ip link show eth0", filepath.Join("/var/run/netns", netns)) + stdout, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(stdout).To(ContainSubstring(" mtu %d ", nodeMTU[pod.Spec.NodeName])) + }) + }) + + Context("[Connectivity]", func() { + Context("[Host-Pod]", func() { + BeforeEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + AfterEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + + It("hp", func() { + By("create pod") + var autoMount bool + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.GetName(), + Namespace: Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: f.GetName(), + Image: testImage, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + AutomountServiceAccountToken: &autoMount, + }, + } + _, err := f.KubeClientSet.CoreV1().Pods(Namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + pod, err = f.WaitPodReady(pod.Name, Namespace) + Expect(err).NotTo(HaveOccurred()) + Expect(pod.Spec.NodeName).NotTo(BeEmpty()) + + By("get pod's netns") + cniPod := cniPods[pod.Spec.NodeName] + cmd := fmt.Sprintf("ovs-vsctl --no-heading --columns=external_ids find interface external-ids:pod_name=%s external-ids:pod_namespace=%s", pod.Name, Namespace) + stdout, _, err := f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + var netns string + for _, field := range strings.Fields(stdout) { + if strings.HasPrefix(field, "pod_netns=") { + netns = strings.TrimPrefix(field, "pod_netns=") + netns = netns[:len(netns)-1] + break + } + } + Expect(netns).NotTo(BeEmpty()) + + By("get host IP") + var hostIP string + for _, addr := range nodeAddrs[pod.Spec.NodeName] { + if util.CIDRContainIP(cidr, strings.Split(addr, "/")[0]) { + hostIP = strings.Split(addr, "/")[0] + break + } + } + Expect(hostIP).ToNot(BeEmpty()) + + By("ping host") + cmd = fmt.Sprintf("nsenter --net=%s ping -c1 -W1 %s", filepath.Join("/var/run/netns", netns), hostIP) + stdout, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(stdout).To(ContainSubstring(" 0% packet loss")) + }) + }) + + Context("[Host-Host-Pod]", func() { + BeforeEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + AfterEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + + It("hhp", func() { + if len(cniPods) < 2 { + return + } + + By("select nodes") + nodes := make([]string, 0, 2) + for node := range cniPods { + nodes = append(nodes, node) + if len(nodes) == 2 { + break + } + } + Expect(len(nodes)).To(Equal(2)) + + By("create pod") + var autoMount bool + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.GetName(), + Namespace: Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: corev1.PodSpec{ + NodeName: nodes[0], + Containers: []corev1.Container{ + { + Name: f.GetName(), + Image: testImage, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + AutomountServiceAccountToken: &autoMount, + }, + } + _, err := f.KubeClientSet.CoreV1().Pods(Namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + pod, err = f.WaitPodReady(pod.Name, Namespace) + Expect(err).NotTo(HaveOccurred()) + Expect(pod.Spec.NodeName).NotTo(BeEmpty()) + + By("get pod's netns") + cniPod := cniPods[pod.Spec.NodeName] + cmd := fmt.Sprintf("ovs-vsctl --no-heading --columns=external_ids find interface external-ids:pod_name=%s external-ids:pod_namespace=%s", pod.Name, Namespace) + stdout, _, err := f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + var netns string + for _, field := range strings.Fields(stdout) { + if strings.HasPrefix(field, "pod_netns=") { + netns = strings.TrimPrefix(field, "pod_netns=") + netns = netns[:len(netns)-1] + break + } + } + Expect(netns).NotTo(BeEmpty()) + + By("get host IP") + var hostIP string + for _, addr := range nodeAddrs[nodes[1]] { + if util.CIDRContainIP(cidr, strings.Split(addr, "/")[0]) { + hostIP = strings.Split(addr, "/")[0] + break + } + } + Expect(hostIP).ToNot(BeEmpty()) + + By("ping host") + cmd = fmt.Sprintf("nsenter --net=%s ping -c1 -W1 %s", filepath.Join("/var/run/netns", netns), hostIP) + stdout, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPod.Name, cniPod.Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(stdout).To(ContainSubstring(" 0% packet loss")) + }) + }) + + Context("Pod-Host-Host-Pod", func() { + BeforeEach(func() { + for i := 0; i < len(cniPods); i++ { + name := fmt.Sprintf("%s-%d", f.GetName(), i+1) + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), name, metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", name, err) + } + } + }) + AfterEach(func() { + for i := 0; i < len(cniPods); i++ { + name := fmt.Sprintf("%s-%d", f.GetName(), i+1) + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), name, metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", name, err) + } + } + }) + + It("phhp", func() { + if len(cniPods) < 2 { + return + } + + By("select nodes") + nodes := make([]string, 0, len(cniPods)) + for node := range cniPods { + nodes = append(nodes, node) + } + + By("create pods") + name := f.GetName() + pods := make([]*corev1.Pod, 2) + var autoMount bool + for i := range nodes { + pods[i] = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%d", name, i+1), + Namespace: Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: corev1.PodSpec{ + NodeName: nodes[i], + Containers: []corev1.Container{ + { + Name: name, + Image: testImage, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + AutomountServiceAccountToken: &autoMount, + }, + } + _, err := f.KubeClientSet.CoreV1().Pods(Namespace).Create(context.Background(), pods[i], metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + pods[i], err = f.WaitPodReady(pods[i].Name, Namespace) + Expect(err).NotTo(HaveOccurred()) + } + + for i := range pods { + By("get pod's netns") + cmd := fmt.Sprintf("ovs-vsctl --no-heading --columns=external_ids find interface external-ids:pod_name=%s external-ids:pod_namespace=%s", pods[i].Name, Namespace) + stdout, _, err := f.ExecToPodThroughAPI(cmd, "cni-server", cniPods[nodes[i]].Name, cniPods[nodes[i]].Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + var netns string + for _, field := range strings.Fields(stdout) { + if strings.HasPrefix(field, "pod_netns=") { + netns = strings.TrimPrefix(field, "pod_netns=") + netns = netns[:len(netns)-1] + break + } + } + Expect(netns).NotTo(BeEmpty()) + + By("ping another pod") + cmd = fmt.Sprintf("nsenter --net=%s ping -c1 -W1 %s", filepath.Join("/var/run/netns", netns), pods[(i+len(pods)+1)%len(pods)].Status.PodIP) + stdout, _, err = f.ExecToPodThroughAPI(cmd, "cni-server", cniPods[nodes[i]].Name, cniPods[nodes[i]].Namespace, nil) + Expect(err).NotTo(HaveOccurred()) + Expect(stdout).To(ContainSubstring(" 0% packet loss")) + } + }) + }) + }) + }) + + Context("[kubectl-ko]", func() { + BeforeEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + AfterEach(func() { + err := f.KubeClientSet.CoreV1().Pods(Namespace).Delete(context.Background(), f.GetName(), metav1.DeleteOptions{}) + if err != nil && !k8serrors.IsNotFound(err) { + klog.Fatalf("failed to delete pod %s: %v", f.GetName(), err) + } + }) + + It("trace", func() { + if util.CheckProtocol(cidr) == kubeovn.ProtocolIPv6 { + return + } + + By("create pod") + var autoMount bool + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: f.GetName(), + Namespace: Namespace, + Labels: map[string]string{"e2e": "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: f.GetName(), + Image: testImage, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + AutomountServiceAccountToken: &autoMount, + }, + } + _, err := f.KubeClientSet.CoreV1().Pods(Namespace).Create(context.Background(), pod, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + pod, err = f.WaitPodReady(pod.Name, Namespace) + Expect(err).NotTo(HaveOccurred()) + + output, err := exec.Command("kubectl", "ko", "trace", fmt.Sprintf("%s/%s", Namespace, pod.Name), "114.114.114.114", "icmp").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + + output, err = exec.Command("kubectl", "ko", "trace", fmt.Sprintf("%s/%s", Namespace, pod.Name), "114.114.114.114", "tcp", "80").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + + output, err = exec.Command("kubectl", "ko", "trace", fmt.Sprintf("%s/%s", Namespace, pod.Name), "114.114.114.114", "udp", "53").CombinedOutput() + Expect(err).NotTo(HaveOccurred(), string(output)) + }) + }) +})