Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Commit

Permalink
Sequential import of two VMs functional tests
Browse files Browse the repository at this point in the history
Signed-off-by: Jakub Dzon <jdzon@redhat.com>
  • Loading branch information
Jakub Dzon committed Jun 24, 2020
1 parent 001e60f commit 355d82c
Show file tree
Hide file tree
Showing 24 changed files with 416 additions and 48 deletions.
2 changes: 1 addition & 1 deletion automation/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ make cluster-sync
./cluster/kubectl.sh create -f tests/cirros/secret.yml

KUBECONFIG=$(./cluster/kubeconfig.sh)
go test ./tests/ovirt --v -timeout 90m -kubeconfig "$KUBECONFIG" -ovirt-secret "default/ovirt-secret"
go test ./tests/ovirt --v -timeout 120m -kubeconfig "$KUBECONFIG" -ovirt-secret "default/ovirt-secret"
12 changes: 8 additions & 4 deletions docs/functional-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@
## Basic Net VM
| Test description | Implemented |
| :---------------- | :-----------: |
| Networked VM import should create started VM | &check; |
| Networked VM import should create started VM with pod network when type in network resource mapping is 'pod' | &check; |
| Networked VM import should create started VM with pod network when type in network resource mapping is missing (nil) | &check; |

### Multiple VMs import
| Test description | Implemented |
| :---------------- | :-----------: |
| Two VMs should be imported in parallel to the same namespace and started | &cross; |
| Two VMs should be imported in sequence to the same namespace and started | &cross; |
| Import of the same VM second time in a row to the same namespace should fail | &cross; |
| Same VM imported second time in a row to a different namespace than the first time should be started | &cross; |
| Two VMs should be imported in sequence to the same namespace and started | &check; |
| Import of the same source VM with NIC (and MAC address) to the same namespace should fail | &check; |
| Import of the same source VM with NIC (and MAC address) to a different namespace should fail | &check; |
| Same source VM with no NICs imported second time with the same target name to the same namespace should not be created but import should be successful | &check; |
| Same VM with no NICs imported second time in a row to a different namespace than the first time should be started | &check; |

## Various VM configurations
| Test description | Implemented |
Expand Down Expand Up @@ -99,6 +102,7 @@
| :---------------- | :---------:
| Import with missing network resource mapping should be blocked | &check; |
| Import with network mapping to a non-existing target network should be blocked | &check; |
| Import with network mapping to unsupported target type should be blocked | &check; |
| Import with storage mapping to a non-existing target storage class should be blocked | &check; |
| Import with disk mapping to a non-existing target storage class should be blocked | &check; |

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/operator-framework/operator-lifecycle-manager v0.0.0-20191115003340-16619cd27fa5
github.com/operator-framework/operator-sdk v0.15.2
github.com/ovirt/go-ovirt v4.3.4+incompatible
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/spf13/pflag v1.0.5
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
github.com/voxelbrain/goptions v0.0.0-20180630082107-58cddc247ea2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,8 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
2 changes: 1 addition & 1 deletion pkg/providers/ovirt/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ func (o *OvirtMapper) getNetworkForNic(vnicProfile *ovirtsdk.VnicProfile) kubevi
}

func (o *OvirtMapper) mapNetworkType(mapping v2vv1alpha1.ResourceMappingItem, kubevirtNet *kubevirtv1.Network) {
if *mapping.Type == networkTypePod {
if mapping.Type == nil || *mapping.Type == networkTypePod {
kubevirtNet.Pod = &kubevirtv1.PodNetwork{}
} else if *mapping.Type == networkTypeMultus {
kubevirtNet.Multus = &kubevirtv1.MultusNetwork{
Expand Down
2 changes: 2 additions & 0 deletions tests/common-vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ var (
PodType = "pod"
// MultusType defines `pod` resource mapping network type
MultusType = "multus"
// UnsupportedType defines non-existing, unsupported `unsupported resource mapping network type
UnsupportedType = "unsupported"
)
6 changes: 5 additions & 1 deletion tests/framework/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (

// CreateOvirtSecretFromBlueprint copies secret from `f.OVirtSecretName` to the test namespace
func (f *Framework) CreateOvirtSecretFromBlueprint() (corev1.Secret, error) {
return f.CreateOvirtSecretInNamespaceFromBlueprint(f.Namespace.Name)
}

// CreateOvirtSecretInNamespaceFromBlueprint copies secret from `f.OVirtSecretName` to given namespace
func (f *Framework) CreateOvirtSecretInNamespaceFromBlueprint(namespace string) (corev1.Secret, error) {
if f.OVirtSecretName == nil {
return corev1.Secret{}, fmt.Errorf("OVirt secret namespace and name have not been provided")
}
Expand All @@ -18,7 +23,6 @@ func (f *Framework) CreateOvirtSecretFromBlueprint() (corev1.Secret, error) {
return corev1.Secret{}, err
}
testSecret := blueprint.DeepCopy()
namespace := f.Namespace.Name
testSecret.ObjectMeta = metav1.ObjectMeta{
GenerateName: f.NsPrefix,
Namespace: namespace,
Expand Down
10 changes: 5 additions & 5 deletions tests/framework/vm-import.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
)

// EnsureVMIDoesNotExist blocks until VM import with given name does not exist in the cluster
func (f *Framework) EnsureVMIDoesNotExist(vmiName string) error {
// EnsureVMImportDoesNotExist blocks until VM import with given name does not exist in the cluster
func (f *Framework) EnsureVMImportDoesNotExist(vmiName string) error {
return wait.PollImmediate(2*time.Second, 1*time.Minute, func() (bool, error) {
_, err := f.VMImportClient.V2vV1alpha1().VirtualMachineImports(f.Namespace.Name).Get(vmiName, metav1.GetOptions{})
if err != nil {
Expand All @@ -27,9 +27,9 @@ func (f *Framework) EnsureVMIDoesNotExist(vmiName string) error {
}

// WaitForVMImportConditionInStatus blocks until VM import with given name has given status condition with given status
func (f *Framework) WaitForVMImportConditionInStatus(pollInterval time.Duration, timeout time.Duration, vmiName string, conditionType v2vv1alpha1.VirtualMachineImportConditionType, status corev1.ConditionStatus, reason string) error {
func (f *Framework) WaitForVMImportConditionInStatus(pollInterval time.Duration, timeout time.Duration, vmiName string, conditionType v2vv1alpha1.VirtualMachineImportConditionType, status corev1.ConditionStatus, reason string, namespace string) error {
pollErr := wait.PollImmediate(pollInterval, timeout, func() (bool, error) {
retrieved, err := f.VMImportClient.V2vV1alpha1().VirtualMachineImports(f.Namespace.Name).Get(vmiName, metav1.GetOptions{})
retrieved, err := f.VMImportClient.V2vV1alpha1().VirtualMachineImports(namespace).Get(vmiName, metav1.GetOptions{})
if err != nil {
return false, err
}
Expand All @@ -53,5 +53,5 @@ func (f *Framework) WaitForVMImportConditionInStatus(pollInterval time.Duration,

// WaitForVMToBeProcessing blocks until VM import with given name is in Processing state
func (f *Framework) WaitForVMToBeProcessing(vmiName string) error {
return f.WaitForVMImportConditionInStatus(2*time.Second, time.Minute, vmiName, v2vv1alpha1.Processing, corev1.ConditionTrue, "")
return f.WaitForVMImportConditionInStatus(2*time.Second, time.Minute, vmiName, v2vv1alpha1.Processing, corev1.ConditionTrue, "", f.Namespace.Name)
}
13 changes: 13 additions & 0 deletions tests/matchers/vm-import.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ func BeSuccessful(testFramework *framework.Framework) types.GomegaMatcher {
return &matcher
}

// BeUnsuccessful creates the matcher checking whether Virtual Machine Import is unsuccessful
func BeUnsuccessful(testFramework *framework.Framework) types.GomegaMatcher {
matcher := hasConditionInStatus{}
matcher.timeout = 3 * time.Minute
matcher.pollInterval = 5 * time.Second
matcher.testFramework = testFramework

matcher.conditionType = v2vv1alpha1.Succeeded
matcher.status = corev1.ConditionFalse
return &matcher
}

// HaveDataVolumeCreationFailure creates the matcher checking whether Virtual Machine Import failed to create datavolume
func HaveDataVolumeCreationFailure(testFramework *framework.Framework) types.GomegaMatcher {
matcher := hasConditionInStatus{}
Expand All @@ -91,6 +103,7 @@ func (matcher *hasConditionInStatus) Match(actual interface{}) (bool, error) {
matcher.conditionType,
matcher.status,
matcher.reason,
vmBluePrint.Namespace,
)
if pollErr != nil {
return false, pollErr
Expand Down
10 changes: 7 additions & 3 deletions tests/ovirt/basic_net_vm_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/kubevirt/vm-import-operator/tests/utils"
sapi "github.com/machacekondra/fakeovirt/pkg/api/stubbing"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -36,12 +37,12 @@ var _ = Describe("Networked VM import ", func() {
secret = s
})

It("should create started VM", func() {
table.DescribeTable("should create started VM with pod network", func(networkType *string) {
vmID := vms.BasicVmID
vmi := utils.VirtualMachineImportCr(vmID, namespace, secret.Name, f.NsPrefix, true)
vmi.Spec.Source.Ovirt.Mappings = &v2vv1alpha1.OvirtMappings{
NetworkMappings: &[]v2vv1alpha1.ResourceMappingItem{
{Source: v2vv1alpha1.Source{ID: &vms.VNicProfile1ID}, Type: &tests.PodType},
{Source: v2vv1alpha1.Source{ID: &vms.VNicProfile1ID}, Type: networkType},
},
}
test.stub(vmID)
Expand All @@ -58,7 +59,10 @@ var _ = Describe("Networked VM import ", func() {

vm := test.validateTargetConfiguration(vmBlueprint.Name)
Expect(vm.Spec.Template.Spec.Volumes[0].DataVolume.Name).To(HaveDefaultStorageClass(f))
})
},
table.Entry("when type in network resource mapping is 'pod'", &tests.PodType),
table.Entry("when type in network resource mapping is missing (nil)", nil),
)
})

func (t *networkedVmImportTest) validateTargetConfiguration(vmName string) *v1.VirtualMachine {
Expand Down
2 changes: 1 addition & 1 deletion tests/ovirt/basic_vm_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ var _ = Describe("Basic VM import ", func() {
}

By("Waiting for VM import removal")
err = f.EnsureVMIDoesNotExist(retrieved.Name)
err = f.EnsureVMImportDoesNotExist(retrieved.Name)
Expect(err).ToNot(HaveOccurred())
})

Expand Down
2 changes: 1 addition & 1 deletion tests/ovirt/cancel_vm_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ var _ = Describe("VM import cancellation ", func() {
})

By("Waiting for VM import removal")
err = f.EnsureVMIDoesNotExist(vmiName)
err = f.EnsureVMImportDoesNotExist(vmiName)
Expect(err).ToNot(HaveOccurred())

By("Temporary config map no longer existing")
Expand Down
196 changes: 196 additions & 0 deletions tests/ovirt/multiple_vms_import_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ovirt_test

import (
v2vv1alpha1 "github.com/kubevirt/vm-import-operator/pkg/apis/v2v/v1alpha1"
"github.com/kubevirt/vm-import-operator/tests"
fwk "github.com/kubevirt/vm-import-operator/tests/framework"
. "github.com/kubevirt/vm-import-operator/tests/matchers"
"github.com/kubevirt/vm-import-operator/tests/ovirt/vms"
"github.com/kubevirt/vm-import-operator/tests/utils"
sapi "github.com/machacekondra/fakeovirt/pkg/api/stubbing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "kubevirt.io/client-go/api/v1"
)

type multipleVmsImportTest struct {
framework *fwk.Framework
namespace string
secret corev1.Secret
}

var _ = Describe("Multiple VMs import ", func() {
var (
f = fwk.NewFrameworkOrDie("multiple-vms-import")
namespace string
test = multipleVmsImportTest{framework: f}
)

BeforeEach(func() {
namespace = f.Namespace.Name
test.namespace = namespace
s, err := f.CreateOvirtSecretFromBlueprint()
if err != nil {
Fail("Cannot create secret: " + err.Error())
}
test.secret = s
})

Context("executed in sequence", func() {
It("should create two started VMs in the same namespace from two different source VMs", func() {
By("Importing and starting first VM")
test.stubAndImportVMAndMakeSureItsRunning(vms.MultipleVmsNo1VmID, namespace, "vm-no-1", "56:6f:05:0f:00:05")

By("Importing and starting second VM")
test.stubAndImportVMAndMakeSureItsRunning(vms.MultipleVmsNo2VmID, namespace, "vm-no-2", "56:6f:05:0f:00:06")

By("Confirm the first VM instance is still running")
vmBlueprint := v1.VirtualMachine{ObjectMeta: metav1.ObjectMeta{Name: "vm-no-1", Namespace: namespace}}
Expect(vmBlueprint).To(BeRunning(f))
})

It("should fail importing the same source VM with NIC to the same namespace", func() {
vmID := vms.MultipleVmsNo1VmID
By("VMs having same MAC address")
test.stub(vmID, "56:6f:05:0f:00:05")

By("Importing and starting first VM")
test.importVMWithSecretAndMakeSureItsRunning(vmID, namespace, "vm-no-1", test.secret.Name)

By("Importing second VM")
created, err := test.triggerVMImport(vmID, namespace, "vm-no-2", test.secret.Name)
Expect(err).NotTo(HaveOccurred())
Expect(created).To(BeUnsuccessful(f))
})

It("should fail importing the same source VM with NIC to different namespace", func() {
namespace2, err := f.CreateNamespace(f.NsPrefix, make(map[string]string))
Expect(err).ToNot(HaveOccurred())
f.AddNamespaceToDelete(namespace2)
secret, err := f.CreateOvirtSecretInNamespaceFromBlueprint(namespace2.Name)
if err != nil {
Fail("Cannot create secret: " + err.Error())
}

By("VMs having same MAC address")
vmID := vms.MultipleVmsNo1VmID
test.stub(vmID, "56:6f:05:0f:00:05")

By("Importing and starting first VM")
test.importVMWithSecretAndMakeSureItsRunning(vmID, namespace, "vm-no-1", test.secret.Name)

By("Importing second VM")
created, err := test.triggerVMImport(vmID, namespace2.Name, "vm-no-2", secret.Name)
Expect(err).NotTo(HaveOccurred())
Expect(created).To(BeUnsuccessful(f))
})

It("should create one started VM from two imports of the same NIC-less source VM with same target name to one namespace ", func() {
vmID := vms.MultipleVmsNo1VmID
vmName := "vm-no-1"
nicsXML := f.LoadFile("nics/empty.xml")
test.stubWithNicsXML(vmID, nicsXML)

By("Importing and starting first VM")
test.importVMWithSecretAndMakeSureItsRunning(vmID, namespace, vmName, test.secret.Name)

By("Importing second VM")
created, err := test.triggerVMImport(vmID, namespace, vmName, test.secret.Name)
Expect(err).NotTo(HaveOccurred())
Expect(created).To(BeSuccessful(f))

By("Having only one VM imported in the end")
vms, err := f.KubeVirtClient.VirtualMachine(namespace).List(&metav1.ListOptions{})
if err != nil {
Fail(err.Error())
}
Expect(vms.Items).To(HaveLen(1))
Expect(vms.Items[0].Name).To(BeEquivalentTo(vmName))
})

It("should create two started VMs from the same NIC-less source VM and with same target name in different namespaces", func() {
namespace2, err := f.CreateNamespace(f.NsPrefix, make(map[string]string))
Expect(err).ToNot(HaveOccurred())
f.AddNamespaceToDelete(namespace2)
secret, err := f.CreateOvirtSecretInNamespaceFromBlueprint(namespace2.Name)
if err != nil {
Fail("Cannot create secret: " + err.Error())
}
vmID := vms.MultipleVmsNo1VmID
nicsXML := f.LoadFile("nics/empty.xml")
test.stubWithNicsXML(vmID, nicsXML)

By("Importing and starting first VM")
test.importVMWithSecretAndMakeSureItsRunning(vmID, namespace, "vm-no-1", test.secret.Name)

By("Importing and starting second VM")
test.importVMWithSecretAndMakeSureItsRunning(vmID, namespace2.Name, "vm-no-1", secret.Name)

By("Confirm the first VM instance is still running")
vmBlueprint := v1.VirtualMachine{ObjectMeta: metav1.ObjectMeta{Name: "vm-no-1", Namespace: namespace}}
Expect(vmBlueprint).To(BeRunning(f))
})
})
})

func (t *multipleVmsImportTest) stubAndImportVMAndMakeSureItsRunning(vmID string, namespace string, vmName string, mac string) {
t.stub(vmID, mac)
t.importVMWithSecretAndMakeSureItsRunning(vmID, namespace, vmName, t.secret.Name)
}

func (t *multipleVmsImportTest) importVMWithSecretAndMakeSureItsRunning(vmID string, namespace string, vmName string, secretName string) {
created, err := t.triggerVMImport(vmID, namespace, vmName, secretName)
Expect(created).To(BeSuccessful(t.framework))

retrieved, _ := t.framework.VMImportClient.V2vV1alpha1().VirtualMachineImports(namespace).Get(created.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())

effectiveTargetVMName := retrieved.Status.TargetVMName
Expect(effectiveTargetVMName).To(BeEquivalentTo(vmName))

vmBlueprint := v1.VirtualMachine{ObjectMeta: metav1.ObjectMeta{Name: effectiveTargetVMName, Namespace: namespace}}
Expect(vmBlueprint).To(BeRunning(t.framework))
}

func (t *multipleVmsImportTest) triggerVMImport(vmID string, namespace string, vmName string, secretName string) (*v2vv1alpha1.VirtualMachineImport, error) {
vmi := utils.VirtualMachineImportCrWithName(vmID, namespace, secretName, t.framework.NsPrefix+"-"+vmID, true, vmName)
vmi.Spec.Source.Ovirt.Mappings = &v2vv1alpha1.OvirtMappings{
NetworkMappings: &[]v2vv1alpha1.ResourceMappingItem{
{Source: v2vv1alpha1.Source{ID: &vms.VNicProfile1ID}, Type: &tests.PodType},
},
}
created, err := t.framework.VMImportClient.V2vV1alpha1().VirtualMachineImports(namespace).Create(&vmi)

Expect(err).NotTo(HaveOccurred())
return created, err
}

func (t *multipleVmsImportTest) stub(vmID string, mac string) {
nicsXML := t.framework.LoadTemplate("nics/mac-template.xml", map[string]string{"@MAC": mac})
t.stubWithNicsXML(vmID, nicsXML)
}

func (t *multipleVmsImportTest) stubWithNicsXML(vmID string, nicsXML string) {
diskAttachmentsXML := t.framework.LoadFile("disk-attachments/one.xml")
diskXML := t.framework.LoadTemplate("disks/disk-1.xml", map[string]string{"@DISKSIZE": "46137344"})
domainXML := t.framework.LoadFile("storage-domains/domain-1.xml")
consolesXML := t.framework.LoadFile("graphic-consoles/vnc.xml")
networkXML := t.framework.LoadFile("networks/net-1.xml")
vnicProfileXML := t.framework.LoadFile("vnic-profiles/vnic-profile-1.xml")
vmXML := t.framework.LoadTemplate("vms/basic-vm.xml", map[string]string{"@VMID": vmID})
builder := sapi.NewStubbingBuilder().
StubGet("/ovirt-engine/api/vms/"+vmID+"/diskattachments", &diskAttachmentsXML).
StubGet("/ovirt-engine/api/vms/"+vmID+"/graphicsconsoles", &consolesXML).
StubGet("/ovirt-engine/api/vms/"+vmID+"/nics", &nicsXML).
StubGet("/ovirt-engine/api/disks/disk-1", &diskXML).
StubGet("/ovirt-engine/api/networks/net-1", &networkXML).
StubGet("/ovirt-engine/api/vnicprofiles/vnic-profile-1", &vnicProfileXML).
StubGet("/ovirt-engine/api/storagedomains/domain-1", &domainXML).
StubGet("/ovirt-engine/api/vms/"+vmID, &vmXML)
err := t.framework.OvirtStubbingClient.Stub(builder.Build())
if err != nil {
Fail(err.Error())
}
}

0 comments on commit 355d82c

Please sign in to comment.