Skip to content

Commit

Permalink
tests/e2e, persistentip: Add primary UDN test case
Browse files Browse the repository at this point in the history
This commit adds a new test subtree, checking persistentIPs on workloads
with primary UDN.

Signed-off-by: Ram Lavi <[email protected]>
  • Loading branch information
RamLavi committed Oct 1, 2024
1 parent 1c5e4b0 commit cf5b99e
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 1 deletion.
8 changes: 8 additions & 0 deletions test/e2e/persistentips_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
const networkInterfaceName = "multus"

const (
rolePrimary = "primary"
roleSecondary = "secondary"
)

Expand Down Expand Up @@ -84,6 +85,8 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
nad = testenv.GenerateLayer2WithSubnetNAD(td.Namespace, params.role)
if params.role == roleSecondary {
vmi = testenv.GenerateAlpineWithMultusVMI(td.Namespace, networkInterfaceName, nad.Name)
} else if params.role == rolePrimary {
vmi = testenv.GenerateAlpineWithPrimaryUDNVMI(td.Namespace)
}
vm = testenv.NewVirtualMachine(vmi, testenv.WithRunning())

Expand Down Expand Up @@ -314,6 +317,11 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
role: roleSecondary,
getIPsFunc: getIPsFromVMIStatus,
}),
Entry("primary UDN",
testParams{
role: rolePrimary,
getIPsFunc: testenv.GetIPsFromNetworkStatusAnnotation,
}),
)

func foregroundDeleteOptions() *client.DeleteOptions {
Expand Down
74 changes: 74 additions & 0 deletions test/env/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,80 @@ func GenerateAlpineWithMultusVMI(namespace, interfaceName, networkName string) *
}
}

func GenerateAlpineWithPrimaryUDNVMI(namespace string) *kubevirtv1.VirtualMachineInstance {
const interfaceName = "passtnet"
return &kubevirtv1.VirtualMachineInstance{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: RandomName("alpine", 16),
},
Spec: kubevirtv1.VirtualMachineInstanceSpec{
Domain: kubevirtv1.DomainSpec{
Resources: kubevirtv1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceMemory: resource.MustParse("2048Mi"),
},
},
Devices: kubevirtv1.Devices{
Disks: []kubevirtv1.Disk{
{
DiskDevice: kubevirtv1.DiskDevice{
Disk: &kubevirtv1.DiskTarget{
Bus: kubevirtv1.DiskBusVirtio,
},
},
Name: "containerdisk",
},
},
Interfaces: []kubevirtv1.Interface{
{
Name: interfaceName,
Binding: &kubevirtv1.PluginBinding{
Name: "passt",
},
},
},
},
},
Networks: []kubevirtv1.Network{
{
Name: interfaceName,
NetworkSource: kubevirtv1.NetworkSource{
Pod: &kubevirtv1.PodNetwork{},
},
},
},
TerminationGracePeriodSeconds: pointer.Int64(5),
Volumes: []kubevirtv1.Volume{
{
Name: "containerdisk",
VolumeSource: kubevirtv1.VolumeSource{
ContainerDisk: &kubevirtv1.ContainerDiskSource{
Image: "quay.io/kubevirtci/alpine-container-disk-demo:devel_alt",
},
},
},
{
Name: "cloudinitdisk",
VolumeSource: kubevirtv1.VolumeSource{
CloudInitNoCloud: &kubevirtv1.CloudInitNoCloudSource{
NetworkData: cloudInitNetworkData(),
},
},
},
},
},
}
}

func cloudInitNetworkData() string {
return `
version: 2
ethernets:
eth0:
dhcp4: true`
}

type VMOption func(vm *kubevirtv1.VirtualMachine)

func NewVirtualMachine(vmi *kubevirtv1.VirtualMachineInstance, opts ...VMOption) *kubevirtv1.VirtualMachine {
Expand Down
92 changes: 91 additions & 1 deletion test/env/getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package env

import (
"context"
"encoding/json"
"fmt"

nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
kubevirtv1 "kubevirt.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

kubevirtv1 "kubevirt.io/api/core/v1"
)

// ThisVMI fetches the latest state of the VirtualMachineInstance. If the object does not exist, nil is returned.
Expand Down Expand Up @@ -44,3 +51,86 @@ func GetIPsFromVMIStatus(vmi *kubevirtv1.VirtualMachineInstance, networkInterfac
}
return ifaceStatus.IPs
}

func getPodByVirtualMachineInstance(vmi *kubevirtv1.VirtualMachineInstance) (*corev1.Pod, error) {
pod, err := lookupPodBySelector(vmi.Namespace, vmiLabelSelector(vmi), vmiFieldSelector(vmi))
if err != nil {
return nil, fmt.Errorf("failed to find pod for VMI %s (%s)", vmi.Name, string(vmi.GetUID()))
}
return pod, nil
}

func lookupPodBySelector(namespace string, labelSelector, fieldSelector map[string]string) (*corev1.Pod, error) {
pods := &corev1.PodList{}
err := Client.List(context.Background(), pods,
client.InNamespace(namespace),
client.MatchingLabels(labelSelector),
client.MatchingFields(fieldSelector))
if err != nil {
return nil, err
}

if len(pods.Items) == 0 {
return nil, fmt.Errorf("failed to lookup pod")
}

return &pods.Items[0], nil
}

func vmiLabelSelector(vmi *kubevirtv1.VirtualMachineInstance) map[string]string {
return map[string]string{kubevirtv1.CreatedByLabel: string(vmi.GetUID())}
}

func vmiFieldSelector(vmi *kubevirtv1.VirtualMachineInstance) map[string]string {
fieldSelectors := map[string]string{}
if vmi.Status.Phase == kubevirtv1.Running {
const podPhase = "status.phase"
fieldSelectors[podPhase] = string(corev1.PodRunning)
}
if node := vmi.Status.NodeName; node != "" {
const nodeName = "spec.nodeName"
fieldSelectors[nodeName] = node
}
return fieldSelectors
}

func parsePodNetworkStatusAnnotation(podNetStatus string) ([]nadv1.NetworkStatus, error) {
if len(podNetStatus) == 0 {
return nil, fmt.Errorf("network status annotation not found")
}

var netStatus []nadv1.NetworkStatus
if err := json.Unmarshal([]byte(podNetStatus), &netStatus); err != nil {
return nil, err
}

return netStatus, nil
}

func getDefaultNetworkStatus(vmi *kubevirtv1.VirtualMachineInstance) (*nadv1.NetworkStatus, error) {
virtLauncherPod, err := getPodByVirtualMachineInstance(vmi)
if err != nil {
return nil, err
}

netStatuses, err := parsePodNetworkStatusAnnotation(virtLauncherPod.Annotations[nadv1.NetworkStatusAnnot])
if err != nil {
return nil, err
}

for _, netStatus := range netStatuses {
if netStatus.Default {
return &netStatus, nil
}
}
return nil, fmt.Errorf("primary IPs not found")
}

func GetIPsFromNetworkStatusAnnotation(vmi *kubevirtv1.VirtualMachineInstance) ([]string, error) {
defaultNetworkStatus, err := getDefaultNetworkStatus(vmi)
if err != nil {
return nil, err
}

return defaultNetworkStatus.IPs, nil
}

0 comments on commit cf5b99e

Please sign in to comment.