Skip to content

Commit

Permalink
e2e: UDN-east-west
Browse files Browse the repository at this point in the history
This commit adds a basic e2e test
over UDN still using NSE.
We will do a new PR to remove the
need for NSE.

Co-Authored-by: Enrique Llorente <ellorent@redhat.com>
Co-Authored-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
  • Loading branch information
3 people committed Jun 22, 2024
1 parent 950c76c commit 14b0f84
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 4 deletions.
2 changes: 1 addition & 1 deletion test/e2e/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ func getPodGWRoute(f *framework.Framework, nodeName string, podName string) net.
if err != nil {
framework.Failf("Error trying to get the pod object")
}
annotation, err := unmarshalPodAnnotation(podGet.Annotations)
annotation, err := unmarshalPodAnnotation(podGet.Annotations, "default")
if err != nil {
framework.Failf("Error trying to unmarshal pod annotations")
}
Expand Down
5 changes: 4 additions & 1 deletion test/e2e/multihoming_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type networkAttachmentConfigParams struct {
networkName string
vlanID int
allowPersistentIPs bool
primaryNetwork bool
}

type networkAttachmentConfig struct {
Expand Down Expand Up @@ -78,7 +79,8 @@ func generateNAD(config networkAttachmentConfig) *nadapi.NetworkAttachmentDefini
"mtu": 1300,
"netAttachDefName": %q,
"vlanID": %d,
"allowPersistentIPs": %t
"allowPersistentIPs": %t,
"primaryNetwork": %t
}
`,
config.networkName,
Expand All @@ -88,6 +90,7 @@ func generateNAD(config networkAttachmentConfig) *nadapi.NetworkAttachmentDefini
namespacedName(config.namespace, config.name),
config.vlanID,
config.allowPersistentIPs,
config.primaryNetwork,
)
return generateNetAttachDef(config.namespace, config.name, nadSpec)
}
Expand Down
205 changes: 205 additions & 0 deletions test/e2e/network_segmentation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package e2e

import (
"context"
"fmt"
"strings"
"time"

nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
nadclient "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/typed/k8s.cni.cncf.io/v1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientset "k8s.io/client-go/kubernetes"
)

var _ = Describe("Network Segmentation", func() {
f := wrappedTestFramework("network-segmentation")
Context("a user defined primary network", func() {
const (
externalServiceIPv4IP = "10.128.0.1"
nodeHostnameKey = "kubernetes.io/hostname"
port = 9000
userDefinedNetworkIPv4Subnet = "10.128.0.0/16"
userDefinedNetworkIPv6Subnet = "2014:100:200::0/60"
userDefinedNetworkName = "hogwarts"
nadName = "gryffindor"
workerOneNodeName = "ovn-worker"
workerTwoNodeName = "ovn-worker2"
)

var (
cs clientset.Interface
nadClient nadclient.K8sCniCncfIoV1Interface
)

BeforeEach(func() {
cs = f.ClientSet

var err error
nadClient, err = nadclient.NewForConfig(f.ClientConfig())
Expect(err).NotTo(HaveOccurred())
})

DescribeTable(
"can perform east/west traffic between nodes",
func(
netConfigParams networkAttachmentConfigParams,
clientPodConfig podConfiguration,
serverPodConfig podConfiguration,
) {
netConfig := newNetworkAttachmentConfig(netConfigParams)

netConfig.namespace = f.Namespace.Name
clientPodConfig.namespace = f.Namespace.Name
serverPodConfig.namespace = f.Namespace.Name

By("creating the attachment configuration")
_, err := nadClient.NetworkAttachmentDefinitions(f.Namespace.Name).Create(
context.Background(),
generateNAD(netConfig),
metav1.CreateOptions{},
)
Expect(err).NotTo(HaveOccurred())

By("instantiating the server pod")
serverPod, err := cs.CoreV1().Pods(serverPodConfig.namespace).Create(
context.Background(),
generatePodSpec(serverPodConfig),
metav1.CreateOptions{},
)
Expect(err).NotTo(HaveOccurred())
Expect(serverPod).NotTo(BeNil())

By("asserting the server pod reaches the `Ready` state")
Eventually(func() v1.PodPhase {
updatedPod, err := cs.CoreV1().Pods(f.Namespace.Name).Get(context.Background(), serverPod.GetName(), metav1.GetOptions{})
if err != nil {
return v1.PodFailed
}
return updatedPod.Status.Phase
}, 2*time.Minute, 6*time.Second).Should(Equal(v1.PodRunning))

By("instantiating the *client* pod")
clientPod, err := cs.CoreV1().Pods(clientPodConfig.namespace).Create(
context.Background(),
generatePodSpec(clientPodConfig),
metav1.CreateOptions{},
)
Expect(err).NotTo(HaveOccurred())

By("asserting the client pod reaches the `Ready` state")
Eventually(func() v1.PodPhase {
updatedPod, err := cs.CoreV1().Pods(f.Namespace.Name).Get(context.Background(), clientPod.GetName(), metav1.GetOptions{})
if err != nil {
return v1.PodFailed
}
return updatedPod.Status.Phase
}, 2*time.Minute, 6*time.Second).Should(Equal(v1.PodRunning))

serverIP := ""
for _, cidr := range strings.Split(netConfig.cidr, ",") {
if cidr != "" {
By("asserting the server pod has an IP from the configured range")
serverIP, err = podIPForUserDefinedPrimaryNetwork(
cs,
f.Namespace.Name,
serverPod.GetName(),
namespacedName(serverPod.Namespace, netConfig.name),
)
Expect(err).NotTo(HaveOccurred())
const netPrefixLengthPerNode = 24
By(fmt.Sprintf("asserting the server pod IP %v is from the configured range %v/%v", serverIP, cidr, netPrefixLengthPerNode))
subnet, err := getNetCIDRSubnet(cidr)
Expect(err).NotTo(HaveOccurred())
Expect(inRange(subnet, serverIP)).To(Succeed())
}

By("asserting the *client* pod can contact the server pod exposed endpoint")
Eventually(func() error {
return reachToServerPodFromClient(cs, serverPodConfig, clientPodConfig, serverIP, port)
}, 2*time.Minute, 6*time.Second).Should(Succeed())
}
},
FEntry(
"two pods connected over an IPv4 network",
networkAttachmentConfigParams{
name: nadName,
networkName: userDefinedNetworkName,
topology: "layer2",
cidr: userDefinedNetworkIPv4Subnet,
excludeCIDRs: []string{externalServiceIPv4IP + "/32"},
primaryNetwork: true,
},
*podConfig(
"client-pod",
nadName,
withNodeSelector(map[string]string{nodeHostnameKey: workerOneNodeName}),
),
*podConfig(
"server-pod",
nadName,
withCommand(func() []string {
return httpServerContainerCmd(port)
}),
withNodeSelector(map[string]string{nodeHostnameKey: workerTwoNodeName}),
),
),
)
})
})

type podOption func(*podConfiguration)

func podConfig(podName, nadName string, opts ...podOption) *podConfiguration {
pod := &podConfiguration{
attachments: []nadapi.NetworkSelectionElement{{Name: nadName}},
name: podName,
}
for _, opt := range opts {
opt(pod)
}
return pod
}

func withCommand(cmdGenerationFn func() []string) podOption {
return func(pod *podConfiguration) {
pod.containerCmd = cmdGenerationFn()
}
}

func withNodeSelector(nodeSelector map[string]string) podOption {
return func(pod *podConfiguration) {
pod.nodeSelector = nodeSelector
}
}

func podIPForUserDefinedPrimaryNetwork(k8sClient clientset.Interface, podNamespace string, podName string, attachmentName string) (string, error) {
pod, err := k8sClient.CoreV1().Pods(podNamespace).Get(context.Background(), podName, metav1.GetOptions{})
if err != nil {
return "", err
}
netStatus, err := userDefinedNetworkStatus(pod, attachmentName)
if err != nil {
return "", err
}

if len(netStatus.IPs) == 0 {
return "", fmt.Errorf("attachment for network %q without IPs", attachmentName)
}
if len(netStatus.IPs) > 1 {
return "", fmt.Errorf("attachment for network %q with more than one IP", attachmentName)
}
return netStatus.IPs[0].IP.String(), nil
}

func userDefinedNetworkStatus(pod *v1.Pod, networkName string) (PodAnnotation, error) {
netStatus, err := unmarshalPodAnnotation(pod.Annotations, networkName)
if err != nil {
return PodAnnotation{}, fmt.Errorf("failed to unmarshall annotations for pod %q: %v", pod.Name, err)
}

return *netStatus, nil
}
4 changes: 2 additions & 2 deletions test/e2e/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func newAnnotationNotSetError(format string, args ...interface{}) error {
}

// UnmarshalPodAnnotation returns the default network info from pod.Annotations
func unmarshalPodAnnotation(annotations map[string]string) (*PodAnnotation, error) {
func unmarshalPodAnnotation(annotations map[string]string, networkName string) (*PodAnnotation, error) {
ovnAnnotation, ok := annotations[podNetworkAnnotation]
if !ok {
return nil, newAnnotationNotSetError("could not find OVN pod annotation in %v", annotations)
Expand All @@ -177,7 +177,7 @@ func unmarshalPodAnnotation(annotations map[string]string) (*PodAnnotation, erro
return nil, fmt.Errorf("failed to unmarshal ovn pod annotation %q: %v",
ovnAnnotation, err)
}
tempA := podNetworks["default"]
tempA := podNetworks[networkName]
a := &tempA

podAnnotation := &PodAnnotation{}
Expand Down

0 comments on commit 14b0f84

Please sign in to comment.