Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

e2e, persistent-ip: Unify primary,secondary tests (primary UDNs use managedTap) #75

Merged
merged 2 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 22 additions & 37 deletions test/e2e/persistentips_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ import (

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

testenv "github.com/kubevirt/ipam-extensions/test/env"
"sigs.k8s.io/controller-runtime/pkg/client"

testenv "github.com/kubevirt/ipam-extensions/test/env"
)

const (
secondaryLogicalNetworkInterfaceName = "multus"
primaryLogicalNetworkInterfaceName = "pod"
nadName = "l2-net-attach-def"
)

Expand All @@ -51,9 +53,9 @@ const (
)

type testParams struct {
role string
ipsFrom func(vmi *kubevirtv1.VirtualMachineInstance) ([]string, error)
vmi func(namespace string) *kubevirtv1.VirtualMachineInstance
role string
networkInterfaceName string
vmi func(namespace string) *kubevirtv1.VirtualMachineInstance
}

var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
Expand Down Expand Up @@ -110,13 +112,12 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
WithPolling(time.Second).
ShouldNot(BeEmpty())

Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPs(params.ipsFrom, Not(BeEmpty())))
Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPsAtInterfaceByName(params.networkInterfaceName, Not(BeEmpty())))
})

It("should keep ips after live migration", func() {
Expect(testenv.Client.Get(context.Background(), client.ObjectKeyFromObject(vmi), vmi)).To(Succeed())
vmiIPsBeforeMigration, err := params.ipsFrom(vmi)
Expect(err).NotTo(HaveOccurred())
vmiIPsBeforeMigration := testenv.GetIPsFromVMIStatus(vmi, params.networkInterfaceName)
Expect(vmiIPsBeforeMigration).NotTo(BeEmpty())

testenv.LiveMigrateVirtualMachine(td.Namespace, vm.Name)
Expand All @@ -128,7 +129,7 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
WithTimeout(5 * time.Minute).
Should(testenv.ContainConditionVMIReady())

Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPs(params.ipsFrom, ConsistOf(vmiIPsBeforeMigration)))
Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPsAtInterfaceByName(params.networkInterfaceName, ConsistOf(vmiIPsBeforeMigration)))
})

It("should garbage collect IPAMClaims after VM deletion", func() {
Expand Down Expand Up @@ -187,8 +188,7 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {

It("should keep ips after restart", func() {
Expect(testenv.Client.Get(context.Background(), client.ObjectKeyFromObject(vmi), vmi)).To(Succeed())
vmiIPsBeforeRestart, err := params.ipsFrom(vmi)
Expect(err).NotTo(HaveOccurred())
vmiIPsBeforeRestart := testenv.GetIPsFromVMIStatus(vmi, params.networkInterfaceName)
Expect(vmiIPsBeforeRestart).NotTo(BeEmpty())
vmiUUIDBeforeRestart := vmi.UID

Expand All @@ -208,7 +208,7 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
WithTimeout(5 * time.Minute).
Should(testenv.ContainConditionVMIReady())

Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPs(params.ipsFrom, ConsistOf(vmiIPsBeforeRestart)))
Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPsAtInterfaceByName(params.networkInterfaceName, ConsistOf(vmiIPsBeforeRestart)))
})
})

Expand All @@ -235,8 +235,7 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
ShouldNot(BeEmpty())

Expect(testenv.Client.Get(context.Background(), client.ObjectKeyFromObject(vmi), vmi)).To(Succeed())
ips, err := params.ipsFrom(vmi)
Expect(err).NotTo(HaveOccurred())
ips := testenv.GetIPsFromVMIStatus(vmi, params.networkInterfaceName)
Expect(ips).NotTo(BeEmpty())
})

Expand Down Expand Up @@ -278,19 +277,18 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
WithPolling(time.Second).
ShouldNot(BeEmpty())

Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPs(params.ipsFrom, Not(BeEmpty())))
Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPsAtInterfaceByName(params.networkInterfaceName, Not(BeEmpty())))
})

It("should keep ips after live migration", func() {
Expect(testenv.Client.Get(context.Background(), client.ObjectKeyFromObject(vmi), vmi)).To(Succeed())
vmiIPsBeforeMigration, err := params.ipsFrom(vmi)
Expect(err).NotTo(HaveOccurred())
vmiIPsBeforeMigration := testenv.GetIPsFromVMIStatus(vmi, params.networkInterfaceName)
Expect(vmiIPsBeforeMigration).NotTo(BeEmpty())

testenv.LiveMigrateVirtualMachine(td.Namespace, vmi.Name)
testenv.CheckLiveMigrationSucceeded(td.Namespace, vmi.Name)

Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPs(params.ipsFrom, ConsistOf(vmiIPsBeforeMigration)))
Expect(testenv.ThisVMI(vmi)()).Should(testenv.MatchIPsAtInterfaceByName(params.networkInterfaceName, ConsistOf(vmiIPsBeforeMigration)))
})

It("should garbage collect IPAMClaims after VMI deletion", func() {
Expand All @@ -314,15 +312,15 @@ var _ = DescribeTableSubtree("Persistent IPs", func(params testParams) {
},
Entry("secondary interfaces",
testParams{
role: roleSecondary,
ipsFrom: secondaryNetworkVMIStatusIPs,
vmi: vmiWithMultus,
role: roleSecondary,
networkInterfaceName: secondaryLogicalNetworkInterfaceName,
vmi: vmiWithMultus,
}),
Entry("primary UDN",
testParams{
role: rolePrimary,
ipsFrom: defaultNetworkStatusAnnotationIPs,
vmi: vmiWithManagedTap,
role: rolePrimary,
networkInterfaceName: primaryLogicalNetworkInterfaceName,
vmi: vmiWithManagedTap,
}),
)

Expand All @@ -342,19 +340,6 @@ func removeFinalizersPatch() ([]byte, error) {
return json.Marshal(patch)
}

func secondaryNetworkVMIStatusIPs(vmi *kubevirtv1.VirtualMachineInstance) ([]string, error) {
return testenv.GetIPsFromVMIStatus(vmi, secondaryLogicalNetworkInterfaceName), nil
}

func defaultNetworkStatusAnnotationIPs(vmi *kubevirtv1.VirtualMachineInstance) ([]string, error) {
defNetworkStatus, err := testenv.DefaultNetworkStatus(vmi)
if err != nil {
return nil, err
}

return defNetworkStatus.IPs, nil
}

func vmiWithMultus(namespace string) *kubevirtv1.VirtualMachineInstance {
interfaceName := secondaryLogicalNetworkInterfaceName
return testenv.NewVirtualMachineInstance(
Expand All @@ -380,7 +365,7 @@ func vmiWithMultus(namespace string) *kubevirtv1.VirtualMachineInstance {

func vmiWithManagedTap(namespace string) *kubevirtv1.VirtualMachineInstance {
const (
interfaceName = "pod"
interfaceName = primaryLogicalNetworkInterfaceName
cloudInitNetworkData = `
version: 2
ethernets:
Expand Down
77 changes: 0 additions & 77 deletions test/env/getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ 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"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -51,75 +46,3 @@ func GetIPsFromVMIStatus(vmi *kubevirtv1.VirtualMachineInstance, networkInterfac
}
return ifaceStatus.IPs
}

func virtualMachineInstancePod(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 with labels %v, fields %v in namespace %s", labelSelector, fieldSelector, namespace)
}

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)
}
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 DefaultNetworkStatus(vmi *kubevirtv1.VirtualMachineInstance) (*nadv1.NetworkStatus, error) {
virtLauncherPod, err := virtualMachineInstancePod(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")
}
24 changes: 13 additions & 11 deletions test/env/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ func vmiStatusConditions(vmi *kubevirtv1.VirtualMachineInstance) []kubevirtv1.Vi
return vmi.Status.Conditions
}

type IPResult struct {
IPs []string
Err error
func interfaceIPs(networkInterface *kubevirtv1.VirtualMachineInstanceNetworkInterface) []string {
if networkInterface == nil {
return nil
}
return networkInterface.IPs
}

func MatchIPs(getIPsFunc func(vmi *kubevirtv1.VirtualMachineInstance) ([]string, error), ipsMatcher gomegatypes.GomegaMatcher) gomegatypes.GomegaMatcher {
return WithTransform(func(vmi *kubevirtv1.VirtualMachineInstance) IPResult {
ips, err := getIPsFunc(vmi)
return IPResult{IPs: ips, Err: err}
}, SatisfyAll(
WithTransform(func(result IPResult) error { return result.Err }, Succeed()),
WithTransform(func(result IPResult) []string { return result.IPs }, ipsMatcher),
))
func MatchIPsAtInterfaceByName(interfaceName string, ipsMatcher gomegatypes.GomegaMatcher) gomegatypes.GomegaMatcher {
return WithTransform(
func(vmi *kubevirtv1.VirtualMachineInstance) *kubevirtv1.VirtualMachineInstanceNetworkInterface {
return lookupInterfaceStatusByName(vmi.Status.Interfaces, interfaceName)
},
SatisfyAll(
Not(BeNil()),
WithTransform(interfaceIPs, ipsMatcher)))
}

func BeRestarted(oldUID types.UID) gomegatypes.GomegaMatcher {
Expand Down
Loading