From 8bd94708ceaebfce74927a7955cde7e93f7c9b02 Mon Sep 17 00:00:00 2001 From: Lukas Koszegy Date: Wed, 6 Mar 2024 08:55:38 +0000 Subject: [PATCH] check boot of vm with ssh connection --- provider/server/exec_test.go | 3 +- .../server/machine_annotations_update_test.go | 2 +- provider/server/machine_create_test.go | 128 ++++++------- provider/server/machine_delete_test.go | 46 ++--- provider/server/machine_list_test.go | 12 +- .../machine_networkinterface_attach_test.go | 13 +- .../machine_networkinterface_detach_test.go | 22 +-- .../server/machine_powerstate_update_test.go | 2 +- provider/server/machine_volume_attach_test.go | 23 ++- provider/server/machine_volume_detach_test.go | 24 +-- provider/server/server_suite_test.go | 14 +- provider/server/status_test.go | 2 +- provider/server/testutils_test.go | 175 ++++++++++++++++++ 13 files changed, 307 insertions(+), 159 deletions(-) create mode 100644 provider/server/testutils_test.go diff --git a/provider/server/exec_test.go b/provider/server/exec_test.go index d5f1163f..53099dc6 100644 --- a/provider/server/exec_test.go +++ b/provider/server/exec_test.go @@ -22,8 +22,7 @@ import ( "k8s.io/kubectl/pkg/util/term" ) -var _ = Describe("Exec", func() { - +var _ = Describe("Exec", Ordered, func() { It("should verify an exec-url with a token", func(ctx SpecContext) { By("creating the test machine") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ diff --git a/provider/server/machine_annotations_update_test.go b/provider/server/machine_annotations_update_test.go index f3e8681d..37b5329c 100644 --- a/provider/server/machine_annotations_update_test.go +++ b/provider/server/machine_annotations_update_test.go @@ -12,7 +12,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("UpdateMachineAnnotations", func() { +var _ = Describe("UpdateMachineAnnotations", Ordered, func() { It("should update machine annotations", func(ctx SpecContext) { ignitionData := []byte("urjhikmnbdjfkknhhdddeee") By("creating a machine") diff --git a/provider/server/machine_create_test.go b/provider/server/machine_create_test.go index af9701ba..ce87f64c 100644 --- a/provider/server/machine_create_test.go +++ b/provider/server/machine_create_test.go @@ -14,11 +14,7 @@ import ( . "github.com/onsi/gomega" ) -const ( - osImage = "ghcr.io/ironcore-dev/ironcore-image/gardenlinux:rootfs-dev-20231206-v1" -) - -var _ = Describe("CreateMachine", func() { +var _ = Describe("CreateMachine", Ordered, func() { It("should create a machine without boot image, volume and network interface", func(ctx SpecContext) { By("creating a machine without boot image, volume and network interface") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ @@ -37,6 +33,18 @@ var _ = Describe("CreateMachine", func() { Expect(err).NotTo(HaveOccurred()) Expect(createResp).NotTo(BeNil()) + DeferCleanup(func(ctx SpecContext) { + Eventually(func(g Gomega) bool { + _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) + g.Expect(err).To(SatisfyAny( + BeNil(), + MatchError(ContainSubstring("NotFound")), + )) + _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) + return libvirt.IsNotFound(err) + }).Should(BeTrue()) + }) + By("ensuring the correct creation response") Expect(createResp).Should(SatisfyAll( HaveField("Machine.Metadata.Id", Not(BeEmpty())), @@ -53,18 +61,6 @@ var _ = Describe("CreateMachine", func() { HaveField("Machine.Status.NetworkInterfaces", BeNil()), )) - DeferCleanup(func(ctx SpecContext) { - Eventually(func(g Gomega) bool { - _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) - g.Expect(err).To(SatisfyAny( - BeNil(), - MatchError(ContainSubstring("NotFound")), - )) - _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) - return libvirt.IsNotFound(err) - }).Should(BeTrue()) - }) - By("ensuring domain and domain XML is created for machine") var domain libvirt.Domain Eventually(func() error { @@ -125,7 +121,8 @@ var _ = Describe("CreateMachine", func() { }, NetworkInterfaces: []*iri.NetworkInterface{ { - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, }, }, }, @@ -134,6 +131,18 @@ var _ = Describe("CreateMachine", func() { Expect(err).NotTo(HaveOccurred()) Expect(createResp).NotTo(BeNil()) + DeferCleanup(func(ctx SpecContext) { + Eventually(func(g Gomega) bool { + _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) + g.Expect(err).To(SatisfyAny( + BeNil(), + MatchError(ContainSubstring("NotFound")), + )) + _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) + return libvirt.IsNotFound(err) + }).Should(BeTrue()) + }) + By("ensuring the correct creation response") Expect(createResp).Should(SatisfyAll( HaveField("Machine.Metadata.Id", Not(BeEmpty())), @@ -149,7 +158,8 @@ var _ = Describe("CreateMachine", func() { Device: "oda", })), HaveField("Machine.Spec.NetworkInterfaces", ContainElement(&iri.NetworkInterface{ - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, })), HaveField("Machine.Status.ObservedGeneration", BeZero()), HaveField("Machine.Status.State", Equal(iri.MachineState_MACHINE_PENDING)), @@ -158,18 +168,6 @@ var _ = Describe("CreateMachine", func() { HaveField("Machine.Status.NetworkInterfaces", BeNil()), )) - DeferCleanup(func(ctx SpecContext) { - Eventually(func(g Gomega) bool { - _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) - g.Expect(err).To(SatisfyAny( - BeNil(), - MatchError(ContainSubstring("NotFound")), - )) - _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) - return libvirt.IsNotFound(err) - }).Should(BeTrue()) - }) - By("ensuring domain and domain XML is created for machine") var domain libvirt.Domain Eventually(func() error { @@ -227,7 +225,7 @@ var _ = Describe("CreateMachine", func() { Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Image: &iri.ImageSpec{ - Image: osImage, + Image: squashfsOSImage, }, Class: machineClassx3xlarge, IgnitionData: ignitionData, @@ -242,7 +240,8 @@ var _ = Describe("CreateMachine", func() { }, NetworkInterfaces: []*iri.NetworkInterface{ { - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, }, }, }, @@ -251,11 +250,23 @@ var _ = Describe("CreateMachine", func() { Expect(err).NotTo(HaveOccurred()) Expect(createResp).NotTo(BeNil()) + DeferCleanup(func(ctx SpecContext) { + Eventually(func(g Gomega) bool { + _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) + g.Expect(err).To(SatisfyAny( + BeNil(), + MatchError(ContainSubstring("NotFound")), + )) + _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) + return libvirt.IsNotFound(err) + }).Should(BeTrue()) + }) + By("ensuring the correct creation response") Expect(createResp).Should(SatisfyAll( HaveField("Machine.Metadata.Id", Not(BeEmpty())), HaveField("Machine.Spec.Power", iri.Power_POWER_ON), - HaveField("Machine.Spec.Image.Image", Equal(osImage)), + HaveField("Machine.Spec.Image.Image", Equal(squashfsOSImage)), HaveField("Machine.Spec.Class", machineClassx3xlarge), HaveField("Machine.Spec.IgnitionData", Equal(ignitionData)), HaveField("Machine.Spec.Volumes", ContainElement(&iri.Volume{ @@ -266,7 +277,8 @@ var _ = Describe("CreateMachine", func() { }, })), HaveField("Machine.Spec.NetworkInterfaces", ContainElement(&iri.NetworkInterface{ - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, })), HaveField("Machine.Status.ObservedGeneration", BeZero()), HaveField("Machine.Status.State", Equal(iri.MachineState_MACHINE_PENDING)), @@ -275,18 +287,6 @@ var _ = Describe("CreateMachine", func() { HaveField("Machine.Status.NetworkInterfaces", BeNil()), )) - DeferCleanup(func(ctx SpecContext) { - Eventually(func(g Gomega) bool { - _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) - g.Expect(err).To(SatisfyAny( - BeNil(), - MatchError(ContainSubstring("NotFound")), - )) - _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) - return libvirt.IsNotFound(err) - }).Should(BeTrue()) - }) - By("ensuring domain and domain XML is created for machine") var domain libvirt.Domain Eventually(func() error { @@ -343,7 +343,7 @@ var _ = Describe("CreateMachine", func() { Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Image: &iri.ImageSpec{ - Image: osImage, + Image: squashfsOSImage, }, Class: machineClassx3xlarge, IgnitionData: ignitionData, @@ -365,7 +365,8 @@ var _ = Describe("CreateMachine", func() { }, NetworkInterfaces: []*iri.NetworkInterface{ { - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, }, }, }, @@ -374,11 +375,23 @@ var _ = Describe("CreateMachine", func() { Expect(err).NotTo(HaveOccurred()) Expect(createResp).NotTo(BeNil()) + DeferCleanup(func(ctx SpecContext) { + Eventually(func(g Gomega) bool { + _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) + g.Expect(err).To(SatisfyAny( + BeNil(), + MatchError(ContainSubstring("NotFound")), + )) + _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) + return libvirt.IsNotFound(err) + }).Should(BeTrue()) + }) + By("ensuring the correct creation response") Expect(createResp).Should(SatisfyAll( HaveField("Machine.Metadata.Id", Not(BeEmpty())), HaveField("Machine.Spec.Power", iri.Power_POWER_ON), - HaveField("Machine.Spec.Image.Image", Equal(osImage)), + HaveField("Machine.Spec.Image.Image", Equal(squashfsOSImage)), HaveField("Machine.Spec.Class", machineClassx3xlarge), HaveField("Machine.Spec.IgnitionData", Equal(ignitionData)), HaveField("Machine.Spec.Volumes", ContainElements( @@ -397,7 +410,8 @@ var _ = Describe("CreateMachine", func() { }, })), HaveField("Machine.Spec.NetworkInterfaces", ContainElement(&iri.NetworkInterface{ - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, })), HaveField("Machine.Status.ObservedGeneration", BeZero()), HaveField("Machine.Status.State", Equal(iri.MachineState_MACHINE_PENDING)), @@ -406,18 +420,6 @@ var _ = Describe("CreateMachine", func() { HaveField("Machine.Status.NetworkInterfaces", BeNil()), )) - DeferCleanup(func(ctx SpecContext) { - Eventually(func(g Gomega) bool { - _, err := machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{MachineId: createResp.Machine.Metadata.Id}) - g.Expect(err).To(SatisfyAny( - BeNil(), - MatchError(ContainSubstring("NotFound")), - )) - _, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) - return libvirt.IsNotFound(err) - }).Should(BeTrue()) - }) - By("ensuring domain and domain XML is created for machine") var domain libvirt.Domain Eventually(func() error { diff --git a/provider/server/machine_delete_test.go b/provider/server/machine_delete_test.go index 398913f1..a843019e 100644 --- a/provider/server/machine_delete_test.go +++ b/provider/server/machine_delete_test.go @@ -15,16 +15,13 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("DeleteMachine", func() { - +var _ = Describe("DeleteMachine", Ordered, func() { It("should delete a machine with graceful shutdown", func(ctx SpecContext) { By("creating a machine using squashfs os image") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, + Labels: map[string]string{}, }, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, @@ -32,13 +29,10 @@ var _ = Describe("DeleteMachine", func() { Image: squashfsOSImage, }, Class: machineClassx3xlarge, - Volumes: []*iri.Volume{ + NetworkInterfaces: []*iri.NetworkInterface{ { - Name: "disk-1", - EmptyDisk: &iri.EmptyDisk{ - SizeBytes: emptyDiskSize, - }, - Device: "oda", + Name: "eth0", + NetworkId: networkID.Name, }, }, }, @@ -76,8 +70,9 @@ var _ = Describe("DeleteMachine", func() { return listResp.Machines[0].Status.State }).Should(Equal(iri.MachineState_MACHINE_RUNNING)) - //allow some time for the vm to boot properly - time.Sleep(30 * time.Second) + Eventually(func(g Gomega) { + isDomainVMUpAndRunning(g, libvirtConn, &domain, &networkID) + }).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) By("deleting the machine") _, err = machineClient.DeleteMachine(ctx, &iri.DeleteMachineRequest{ @@ -106,7 +101,7 @@ var _ = Describe("DeleteMachine", func() { }) g.Expect(err).NotTo(HaveOccurred()) g.Expect(listResp.Machines).To(BeEmpty()) - }).Within(gracefulShutdownTimeout).ProbeEvery(probeEveryInterval).Should(Succeed()) // ProbeEvery has to be ideally less than or equal to half of ResyncIntervalGarbageCollector + }).WithTimeout(gracefulShutdownTimeout).WithPolling(pollingIntervalDeletion).Should(Succeed()) By("ensuring domain and domain XML is deleted for machine") domain, err = libvirtConn.DomainLookupByUUID(libvirtutils.UUIDStringToBytes(createResp.Machine.Metadata.Id)) @@ -123,23 +118,11 @@ var _ = Describe("DeleteMachine", func() { By("creating a machine which may not boot properly") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ - Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, - }, + Metadata: &irimeta.ObjectMetadata{}, Spec: &iri.MachineSpec{ - Power: iri.Power_POWER_ON, - Class: machineClassx3xlarge, - Volumes: []*iri.Volume{ - { - Name: "disk-1", - EmptyDisk: &iri.EmptyDisk{ - SizeBytes: emptyDiskSize, - }, - Device: "oda", - }, - }, + Power: iri.Power_POWER_ON, + Class: machineClassx3xlarge, + Volumes: nil, NetworkInterfaces: nil, }, }, @@ -203,7 +186,8 @@ var _ = Describe("DeleteMachine", func() { }) g.Expect(err).NotTo(HaveOccurred()) g.Expect(listResp.Machines).Should(HaveLen(1)) - }).WithTimeout(gracefulShutdownTimeout).WithPolling(probeEveryInterval).Should(Succeed()) + g.Expect(listResp.Machines[0].Status.State).Should(Equal(iri.MachineState_MACHINE_TERMINATING)) + }).WithTimeout(gracefulShutdownTimeout).WithPolling(pollingIntervalDeletion).Should(Succeed()) By("ensuring machine is deleted after gracefulShutdownTimeout") Eventually(func(g Gomega) { diff --git a/provider/server/machine_list_test.go b/provider/server/machine_list_test.go index e2375c8d..374ec4d8 100644 --- a/provider/server/machine_list_test.go +++ b/provider/server/machine_list_test.go @@ -12,7 +12,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("ListMachine", func() { +var _ = Describe("ListMachine", Ordered, func() { It("should list machines", func(ctx SpecContext) { By("creating a machine") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ @@ -25,16 +25,6 @@ var _ = Describe("ListMachine", func() { Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Class: machineClassx3xlarge, - Volumes: []*iri.Volume{ - { - Name: "disk-1", - EmptyDisk: &iri.EmptyDisk{ - SizeBytes: 5368709120, - }, - Device: "oda", - }, - }, - NetworkInterfaces: nil, }, }, }) diff --git a/provider/server/machine_networkinterface_attach_test.go b/provider/server/machine_networkinterface_attach_test.go index 078fc61e..1839ab92 100644 --- a/provider/server/machine_networkinterface_attach_test.go +++ b/provider/server/machine_networkinterface_attach_test.go @@ -17,21 +17,17 @@ import ( "libvirt.org/go/libvirtxml" ) -var _ = Describe("AttachNetworkInterface", func() { +var _ = Describe("AttachNetworkInterface", Ordered, func() { It("should attach a network interface to the machine", func(ctx SpecContext) { By("creating a machine") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ - Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, - }, + Metadata: &irimeta.ObjectMetadata{}, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Class: machineClassx2medium, Image: &iri.ImageSpec{ - Image: osImage, + Image: squashfsOSImage, }, }, }, @@ -72,7 +68,8 @@ var _ = Describe("AttachNetworkInterface", func() { attachNetworkResp, err := machineClient.AttachNetworkInterface(ctx, &iri.AttachNetworkInterfaceRequest{ MachineId: createResp.Machine.Metadata.Id, NetworkInterface: &iri.NetworkInterface{ - Name: "nic-1", + Name: "nic-1", + NetworkId: networkID.Name, }, }) Expect(err).NotTo(HaveOccurred()) diff --git a/provider/server/machine_networkinterface_detach_test.go b/provider/server/machine_networkinterface_detach_test.go index 32515213..9802a2c6 100644 --- a/provider/server/machine_networkinterface_detach_test.go +++ b/provider/server/machine_networkinterface_detach_test.go @@ -17,38 +17,26 @@ import ( "libvirt.org/go/libvirtxml" ) -var _ = Describe("DetachNetworkInterface", func() { +var _ = Describe("DetachNetworkInterface", Ordered, func() { It("should detach a network interface from the machine", func(ctx SpecContext) { By("creating a machine") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ - Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, - }, + Metadata: &irimeta.ObjectMetadata{}, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Class: machineClassx2medium, Image: &iri.ImageSpec{ - Image: osImage, + Image: squashfsOSImage, }, NetworkInterfaces: []*iri.NetworkInterface{ { Name: "nic-1", - NetworkId: "nid-1", - Ips: []string{"192.168.1.1"}, - Attributes: map[string]string{ - "key1": "value1", - }, + NetworkId: networkID.Name, }, { Name: "nic-2", - NetworkId: "nid-2", - Ips: []string{"192.168.1.2"}, - Attributes: map[string]string{ - "key2": "value2", - }, + NetworkId: networkID.Name, }, }, }, diff --git a/provider/server/machine_powerstate_update_test.go b/provider/server/machine_powerstate_update_test.go index 5fa4f581..adcda319 100644 --- a/provider/server/machine_powerstate_update_test.go +++ b/provider/server/machine_powerstate_update_test.go @@ -13,7 +13,7 @@ import ( ) // TODO: This test will require update after implementation of: https://github.com/ironcore-dev/libvirt-provider/issues/106 -var _ = Describe("UpdateMachinePower", func() { +var _ = Describe("UpdateMachinePower", Ordered, func() { It("should update machine power state", func(ctx SpecContext) { ignitionData := []byte("urjhikmnbdjfkknhhdddeee") By("creating a machine") diff --git a/provider/server/machine_volume_attach_test.go b/provider/server/machine_volume_attach_test.go index 4c317a04..70f81cb9 100644 --- a/provider/server/machine_volume_attach_test.go +++ b/provider/server/machine_volume_attach_test.go @@ -15,17 +15,16 @@ import ( "libvirt.org/go/libvirtxml" ) -var _ = Describe("AttachVolume", func() { +var _ = Describe("AttachVolume", Ordered, func() { It("should correctly attach volume to machine", func(ctx SpecContext) { By("creating a machine") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ - Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, - }, + Metadata: &irimeta.ObjectMetadata{}, Spec: &iri.MachineSpec{ + Image: &iri.ImageSpec{ + Image: squashfsOSImage, + }, Power: iri.Power_POWER_ON, Class: machineClassx3xlarge, }, @@ -63,6 +62,10 @@ var _ = Describe("AttachVolume", func() { return libvirt.DomainState(domainState) }).Should(Equal(libvirt.DomainRunning)) + Eventually(func(g Gomega) { + isDomainVMUpAndRunning(g, libvirtConn, &domain, &networkID) + }).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) + By("attaching empty disk to a machine") attachEmptyDiskResp, err := machineClient.AttachVolume(ctx, &iri.AttachVolumeRequest{ MachineId: createResp.Machine.Metadata.Id, @@ -130,9 +133,11 @@ var _ = Describe("AttachVolume", func() { g.Expect(domainXML.Unmarshal(domainXMLData)).Should(Succeed()) disks = domainXML.Devices.Disks return len(disks) - }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Equal(2)) - Expect(disks[0].Serial).To(HavePrefix("oda")) - Expect(disks[1].Serial).To(HavePrefix("odb")) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Equal(3)) + for index := range disks { + Expect(disks[index].Serial).To( + SatisfyAny(HavePrefix("oda"), HavePrefix("odb"), HavePrefix("machineboot"))) + } By("ensuring attached volume have been updated in machine status field") Eventually(func(g Gomega) *iri.MachineStatus { diff --git a/provider/server/machine_volume_detach_test.go b/provider/server/machine_volume_detach_test.go index 23b1ec21..70fe7d45 100644 --- a/provider/server/machine_volume_detach_test.go +++ b/provider/server/machine_volume_detach_test.go @@ -15,22 +15,24 @@ import ( "libvirt.org/go/libvirtxml" ) -var _ = Describe("DetachVolume", func() { +var _ = Describe("DetachVolume", Ordered, func() { It("should correctly detach volume from machine", func(ctx SpecContext) { By("creating a machine with two empty disks and single ceph volume") createResp, err := machineClient.CreateMachine(ctx, &iri.CreateMachineRequest{ Machine: &iri.Machine{ - Metadata: &irimeta.ObjectMetadata{ - Labels: map[string]string{ - "foo": "bar", - }, - }, + Metadata: &irimeta.ObjectMetadata{}, Spec: &iri.MachineSpec{ Power: iri.Power_POWER_ON, Image: &iri.ImageSpec{ - Image: osImage, + Image: squashfsOSImage, }, Class: machineClassx3xlarge, + NetworkInterfaces: []*iri.NetworkInterface{ + { + Name: "eth0", + NetworkId: networkID.Name, + }, + }, Volumes: []*iri.Volume{ { Name: "disk-1", @@ -143,8 +145,9 @@ var _ = Describe("DetachVolume", func() { Expect(disks[1].Serial).To(HavePrefix("odb")) Expect(disks[2].Serial).To(HavePrefix("odc")) - // wait to complete machine reconciliation - time.Sleep(20 * time.Second) + Eventually(func(g Gomega) { + isDomainVMUpAndRunning(g, libvirtConn, &domain, &networkID) + }).WithTimeout(5 * time.Minute).WithPolling(5 * time.Second).Should(Succeed()) By("detaching empty disk disk-1 from machine") diskDetachResp, err := machineClient.DetachVolume(ctx, &iri.DetachVolumeRequest{ @@ -165,9 +168,6 @@ var _ = Describe("DetachVolume", func() { return len(disks) }).Should(Equal(3)) - // wait to complete machine reconciliation - time.Sleep(20 * time.Second) - By("detaching ceph volume volume-1 from machine") volumeDetachResp, err := machineClient.DetachVolume(ctx, &iri.DetachVolumeRequest{ MachineId: createResp.Machine.Metadata.Id, diff --git a/provider/server/server_suite_test.go b/provider/server/server_suite_test.go index 89f91181..22eb8773 100644 --- a/provider/server/server_suite_test.go +++ b/provider/server/server_suite_test.go @@ -31,11 +31,11 @@ import ( const ( eventuallyTimeout = 80 * time.Second pollingInterval = 50 * time.Millisecond + pollingIntervalDeletion = 2 * time.Second // it can be max half of resyncGarbageCollectorInterval gracefulShutdownTimeout = 60 * time.Second resyncGarbageCollectorInterval = 5 * time.Second resyncVolumeSizeInterval = 1 * time.Minute consistentlyDuration = 1 * time.Second - probeEveryInterval = 2 * time.Second machineClassx3xlarge = "x3-xlarge" machineClassx2medium = "x2-medium" squashfsOSImage = "ghcr.io/ironcore-dev/ironcore-image/gardenlinux:squashfs-dev-20240123-v2" @@ -53,12 +53,14 @@ var ( cephImage = os.Getenv("CEPH_IMAGE") cephUsername = os.Getenv("CEPH_USERNAME") cephUserkey = os.Getenv("CEPH_USERKEY") + networkID = libvirt.Network{Name: "integration-test"} ) func TestServer(t *testing.T) { - SetDefaultConsistentlyPollingInterval(pollingInterval) SetDefaultEventuallyPollingInterval(pollingInterval) SetDefaultEventuallyTimeout(eventuallyTimeout) + + SetDefaultConsistentlyPollingInterval(pollingInterval) SetDefaultConsistentlyDuration(consistentlyDuration) RegisterFailHandler(Fail) @@ -95,7 +97,7 @@ var _ = BeforeSuite(func() { DeferCleanup(os.Remove, machineClassesFile.Name()) pluginOpts := networkinterfaceplugin.NewDefaultOptions() - pluginOpts.PluginName = "isolated" + pluginOpts.PluginName = "providernet" tempDir = GinkgoT().TempDir() Expect(os.Chmod(tempDir, 0730)).Should(Succeed()) @@ -145,7 +147,13 @@ var _ = BeforeSuite(func() { libvirtConn = libvirt.NewWithDialer(c) Expect(libvirtConn.Connect()).To(Succeed()) Expect(libvirtConn.IsConnected(), BeTrue()) + + Expect(createOrGetNetwork(&networkID)).NotTo(HaveOccurred()) + DeferCleanup(libvirtConn.ConnectClose) + DeferCleanup(func() { + Expect(deleteNetwork(libvirtConn, &networkID)).Should(Succeed()) + }) }) func isSocketAvailable(socketPath string) error { diff --git a/provider/server/status_test.go b/provider/server/status_test.go index 5298cdd3..d373b6b4 100644 --- a/provider/server/status_test.go +++ b/provider/server/status_test.go @@ -11,7 +11,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Status", func() { +var _ = Describe("Status", Ordered, func() { It("should get list of supported machine class with calculated quantity in status", func(ctx SpecContext) { By("getting machine class status") statusResp, err := machineClient.Status(ctx, &iriv1alpha1.StatusRequest{}) diff --git a/provider/server/testutils_test.go b/provider/server/testutils_test.go new file mode 100644 index 00000000..859c5061 --- /dev/null +++ b/provider/server/testutils_test.go @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + "fmt" + "net" + "time" + + "github.com/digitalocean/go-libvirt" + "github.com/onsi/gomega" + "libvirt.org/go/libvirtxml" +) + +func getAnyDomainIPForNetwork(libvirtConn *libvirt.Libvirt, network *libvirt.Network, domain *libvirt.Domain) (string, error) { + domainXMLData, err := libvirtConn.DomainGetXMLDesc(*domain, 0) + if err != nil { + return "", err + } + + domainXML := &libvirtxml.Domain{} + err = domainXML.Unmarshal(domainXMLData) + if err != nil { + return "", nil + } + + mac, err := getDomainMAC(domainXML, network.Name) + if err != nil { + return "", nil + } + + leases, _, err := libvirtConn.NetworkGetDhcpLeases(*network, libvirt.OptString{mac}, 1, 0) + if err != nil { + return "", err + } + + // return first ip + for _, lease := range leases { + if lease.Mac[0] == mac { + return lease.Ipaddr, nil + } + } + + return "", fmt.Errorf("failed to find ip address for domain %s", domain.Name) +} + +func getDomainMAC(domain *libvirtxml.Domain, networkName string) (string, error) { + for _, netIF := range domain.Devices.Interfaces { + if isInterfaceInSpecificNetwork(netIF.Source, networkName) && isMACValid(netIF.MAC) { + return netIF.MAC.Address, nil + } + } + + return "", fmt.Errorf("failed to find mac address for network %s", networkName) +} + +func isInterfaceInSpecificNetwork(source *libvirtxml.DomainInterfaceSource, networkName string) bool { + return source != nil && source.Network != nil && source.Network.Network == networkName +} + +func isMACValid(mac *libvirtxml.DomainInterfaceMAC) bool { + return mac != nil && mac.Address != "" +} + +func isSSHListenToDefualtPort(ip string) bool { + timeout := time.Second + conn, err := net.DialTimeout("tcp", net.JoinHostPort(ip, "22"), timeout) + if err != nil { + return false + } + + if conn != nil { + // tests aren't long running so we can ignore errors + _ = conn.Close() + return true + } + + return false +} + +func isDomainVMUpAndRunning(g gomega.Gomega, libvirtConn *libvirt.Libvirt, domain *libvirt.Domain, network *libvirt.Network) { + ip, err := getAnyDomainIPForNetwork(libvirtConn, network, domain) + g.Expect(err).NotTo(gomega.HaveOccurred()) + g.Expect(isSSHListenToDefualtPort(ip)).Should(gomega.BeTrue()) +} + +func createOrGetNetwork(networkID *libvirt.Network) error { + currentNetwork, err := libvirtConn.NetworkLookupByName(networkID.Name) + if err != nil { + libvirtErr, ok := err.(libvirt.Error) + if !ok { + return err + } + + if libvirtErr.Code != uint32(libvirt.ErrNoNetwork) { + return err + } + + newNetworkXML, err := generateDefaultNetworkXML(networkID.Name) + if err != nil { + return err + } + + newNetworkID, err := libvirtConn.NetworkDefineXML(newNetworkXML) + if err != nil { + return err + } + + networkID.UUID = newNetworkID.UUID + } else { + networkID.UUID = currentNetwork.UUID + } + + // start existing network + active, err := libvirtConn.NetworkIsActive(*networkID) + if err != nil { + return fmt.Errorf("failed to get network '%s' active state: %w", networkID.Name, err) + } + + if active == 1 { + return nil + } + + // create and start defined network + err = libvirtConn.NetworkCreate(*networkID) + if err != nil { + return fmt.Errorf("failed to start network '%s': %w", networkID.Name, err) + } + + return nil +} + +func generateDefaultNetworkXML(name string) (string, error) { + newNetwork := libvirtxml.Network{ + Name: name, + Forward: &libvirtxml.NetworkForward{ + Mode: "nat", + NAT: &libvirtxml.NetworkForwardNAT{ + Ports: []libvirtxml.NetworkForwardNATPort{{Start: 1024, End: 65535}}, + }, + }, + IPs: []libvirtxml.NetworkIP{ + { + // randomly choosed, hopefully it can be potencial problem for somebody + Address: "192.168.168.1", + Netmask: "255.255.255.0", + DHCP: &libvirtxml.NetworkDHCP{ + Ranges: []libvirtxml.NetworkDHCPRange{ + { + Start: "192.168.168.2", + End: "192.168.168.254", + }, + }, + }, + }, + }, + Bridge: &libvirtxml.NetworkBridge{ + Name: "virbrtest0", + STP: "on", + Delay: "0", + }, + } + + return newNetwork.Marshal() +} + +func deleteNetwork(libvirtConn *libvirt.Libvirt, networkID *libvirt.Network) error { + err := libvirtConn.NetworkDestroy(*networkID) + if err != nil { + return err + } + + return libvirtConn.NetworkUndefine(*networkID) +}