From f451cd9ce2171a11f6623ea7050fac6435388f57 Mon Sep 17 00:00:00 2001 From: Ajay Victor Date: Thu, 3 Oct 2024 21:38:42 +0530 Subject: [PATCH] test/e2e: Adding tests for multiple PodVM scenario Negative test for multiple PodVM images on libvirt provider with a non existing volume Fixes confidential-containers#1282 Signed-off-by: Ajay Victor --- .../test/e2e/assessment_runner.go | 33 ++++ .../test/e2e/common_suite.go | 39 ++++ .../test/e2e/libvirt_test.go | 10 ++ .../provisioner/libvirt/provision_common.go | 170 ++++++++++-------- 4 files changed, 174 insertions(+), 78 deletions(-) diff --git a/src/cloud-api-adaptor/test/e2e/assessment_runner.go b/src/cloud-api-adaptor/test/e2e/assessment_runner.go index a1d28322ba..3c0b6d86d9 100644 --- a/src/cloud-api-adaptor/test/e2e/assessment_runner.go +++ b/src/cloud-api-adaptor/test/e2e/assessment_runner.go @@ -78,6 +78,7 @@ type TestCase struct { testInstanceTypes InstanceValidatorFunctions isNydusSnapshotter bool FailReason string + alternateImageName string } func (tc *TestCase) WithConfigMap(configMap *v1.ConfigMap) *TestCase { @@ -135,6 +136,11 @@ func (tc *TestCase) WithInstanceTypes(testInstanceTypes InstanceValidatorFunctio return tc } +func (tc *TestCase) WithMultiplePodVM(alternateImageName string) *TestCase { + tc.alternateImageName = alternateImageName + return tc +} + func (pod *ExtraPod) WithTestCommands(TestCommands []TestCommand) *ExtraPod { pod.testCommands = TestCommands return pod @@ -545,6 +551,33 @@ func (tc *TestCase) Run() { } } + + if tc.alternateImageName != "" { + var podlist v1.PodList + var podLogString string + expectedSuccessMessage := "Choosing " + tc.alternateImageName + if err := client.Resources("confidential-containers-system").List(ctx, &podlist); err != nil { + t.Fatal(err) + } + for _, pod := range podlist.Items { + if pod.Labels["app"] == "cloud-api-adaptor" { + podLogString, _ = GetPodLog(ctx, client, pod) + break + } + } + if strings.Contains(podLogString, expectedSuccessMessage) { + t.Logf("PodVM was brought up using the alternate PodVM image %s", tc.alternateImageName) + } else { + t.Logf("cloud-api-adaptor pod logs: %s", podLogString) + yamlData, err := yaml.Marshal(tc.pod.Status) + if err != nil { + log.Errorf("Error marshaling pod.Status to JSON: %s", err.Error()) + } else { + t.Logf("Current Pod State: %v", string(yamlData)) + } + t.Fatal("failed due to unknown reason") + } + } return ctx }). Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context { diff --git a/src/cloud-api-adaptor/test/e2e/common_suite.go b/src/cloud-api-adaptor/test/e2e/common_suite.go index c575c8103c..e787dcc028 100644 --- a/src/cloud-api-adaptor/test/e2e/common_suite.go +++ b/src/cloud-api-adaptor/test/e2e/common_suite.go @@ -406,6 +406,45 @@ func DoTestPodVMwithAnnotationsLargerCPU(t *testing.T, e env.Environment, assert NewTestCase(t, e, "PodVMwithAnnotationsLargerCPU", assert, "Failed to Create PodVM with Annotations Larger CPU").WithPod(pod).WithInstanceTypes(testInstanceTypes).WithCustomPodState(v1.PodPending).Run() } +func DoTestCreatePeerPodContainerWithValidAlternateImage(t *testing.T, e env.Environment, assert CloudAssert) { + podName := "annotations-valid-alternate-image" + containerName := "busybox" + imageName := BUSYBOX_IMAGE + alternateImageName := "another-podvm-base.qcow2" + annotationData := map[string]string{ + "io.katacontainers.config.hypervisor.image": alternateImageName, + } + pod := NewPod(E2eNamespace, podName, containerName, imageName, WithCommand([]string{"/bin/sh", "-c", "sleep 3600"}), WithAnnotations(annotationData)) + + NewTestCase(t, e, "PodVMwithAnnotationsValidAlternateImage", assert, "PodVM created with an alternate image").WithPod(pod).WithMultiplePodVM(alternateImageName).Run() +} + +func DoTestCreatePeerPodContainerWithInvalidAlternateImage(t *testing.T, e env.Environment, assert CloudAssert) { + podName := "annotations-invalid-alternate-image" + containerName := "busybox" + imageName := BUSYBOX_IMAGE + nonExistingImageName := "non-existing-image" + expectedErrorMessage := "Error in creating volume: Can't retrieve volume " + nonExistingImageName + annotationData := map[string]string{ + "io.katacontainers.config.hypervisor.image": nonExistingImageName, + } + pod := NewPod(E2eNamespace, podName, containerName, imageName, WithCommand([]string{"/bin/sh", "-c", "sleep 3600"}), WithAnnotations(annotationData)) + + testInstanceTypes := InstanceValidatorFunctions{ + testSuccessfn: IsStringEmpty, + testFailurefn: func(errorMsg error) bool { + if strings.Contains(errorMsg.Error(), expectedErrorMessage) { + t.Logf("Got Expected Error: %v", errorMsg.Error()) + return true + } else { + t.Logf("Failed to Get Expected Error: %v", errorMsg.Error()) + return false + } + }, + } + NewTestCase(t, e, "PodVMwithAnnotationsInvalidAlternateImage", assert, "Failed to Create PodVM with a non-existent image").WithPod(pod).WithInstanceTypes(testInstanceTypes).WithCustomPodState(v1.PodPending).Run() +} + func DoTestPodToServiceCommunication(t *testing.T, e env.Environment, assert CloudAssert) { clientPodName := "test-client" clientContainerName := "busybox" diff --git a/src/cloud-api-adaptor/test/e2e/libvirt_test.go b/src/cloud-api-adaptor/test/e2e/libvirt_test.go index 0c6e17f319..1164881f52 100644 --- a/src/cloud-api-adaptor/test/e2e/libvirt_test.go +++ b/src/cloud-api-adaptor/test/e2e/libvirt_test.go @@ -35,6 +35,16 @@ func TestLibvirtCreatePeerPodContainerWithExternalIPAccess(t *testing.T) { } +func TestLibvirtCreatePeerPodContainerWithValidAlternateImage(t *testing.T) { + assert := LibvirtAssert{} + DoTestCreatePeerPodContainerWithValidAlternateImage(t, testEnv, assert) +} + +func TestLibvirtCreatePeerPodContainerWithInvalidAlternateImage(t *testing.T) { + assert := LibvirtAssert{} + DoTestCreatePeerPodContainerWithInvalidAlternateImage(t, testEnv, assert) +} + func TestLibvirtCreatePeerPodWithJob(t *testing.T) { assert := LibvirtAssert{} DoTestCreatePeerPodWithJob(t, testEnv, assert) diff --git a/src/cloud-api-adaptor/test/provisioner/libvirt/provision_common.go b/src/cloud-api-adaptor/test/provisioner/libvirt/provision_common.go index 226ec2c3c2..aa4d27ed48 100644 --- a/src/cloud-api-adaptor/test/provisioner/libvirt/provision_common.go +++ b/src/cloud-api-adaptor/test/provisioner/libvirt/provision_common.go @@ -21,16 +21,17 @@ import ( // LibvirtProvisioner implements the CloudProvisioner interface for Libvirt. type LibvirtProvisioner struct { - conn *libvirt.Connect // Libvirt connection - network string // Network name - ssh_key_file string // SSH key file used to connect to Libvirt - storage string // Storage pool name - uri string // Libvirt URI - wd string // libvirt's directory path on this repository - volumeName string // Podvm volume name - clusterName string // Cluster name - kbs_image string // KBS Service OCI Image URL - kbs_image_tag string // KBS Service OCI Image Tag + conn *libvirt.Connect // Libvirt connection + network string // Network name + ssh_key_file string // SSH key file used to connect to Libvirt + storage string // Storage pool name + uri string // Libvirt URI + wd string // libvirt's directory path on this repository + volumeName string // Podvm volume name + alternateVolumeName string // Alternate Podvm volume name + clusterName string // Cluster name + kbs_image string // KBS Service OCI Image URL + kbs_image_tag string // KBS Service OCI Image Tag } // LibvirtInstallOverlay implements the InstallOverlay interface @@ -68,6 +69,9 @@ func NewLibvirtProvisioner(properties map[string]string) (pv.CloudProvisioner, e vol_name = properties["libvirt_vol_name"] } + // alternate_vol_name is used for multi-podvm testing. + alternate_vol_name := "another-podvm-base.qcow2" + conn_uri := "qemu:///system" if properties["libvirt_conn_uri"] != "" { conn_uri = properties["libvirt_conn_uri"] @@ -94,16 +98,17 @@ func NewLibvirtProvisioner(properties map[string]string) (pv.CloudProvisioner, e // TODO: Check network and storage are not nil? return &LibvirtProvisioner{ - conn: conn, - network: network, - ssh_key_file: ssh_key_file, - storage: storage, - uri: uri, - wd: wd, - volumeName: vol_name, - clusterName: clusterName, - kbs_image: kbs_image, - kbs_image_tag: kbs_image_tag, + conn: conn, + network: network, + ssh_key_file: ssh_key_file, + storage: storage, + uri: uri, + wd: wd, + volumeName: vol_name, + alternateVolumeName: alternate_vol_name, + clusterName: clusterName, + kbs_image: kbs_image, + kbs_image_tag: kbs_image_tag, }, nil } @@ -152,30 +157,35 @@ func (l *LibvirtProvisioner) CreateVPC(ctx context.Context, cfg *envconf.Config) return fmt.Errorf("Storage pool '%s' not found. It should be created beforehand", l.storage) } - // Create the podvm storage volume if it does not exist. - if _, err = sPool.LookupStorageVolByName(l.volumeName); err != nil { - volCfg := libvirtxml.StorageVolume{ - Name: l.volumeName, - Capacity: &libvirtxml.StorageVolumeSize{ - Unit: "GiB", - Value: 20, - }, - Allocation: &libvirtxml.StorageVolumeSize{ - Unit: "GiB", - Value: 2, - }, - Target: &libvirtxml.StorageVolumeTarget{ - Format: &libvirtxml.StorageVolumeTargetFormat{ - Type: "qcow2", + // Create two volumes to test the multiple podvm image scenario. + lVolumes := [2]string{l.volumeName, l.alternateVolumeName} + + // Create the podvm storage volumes if it does not exist. + for _, volume := range lVolumes { + if _, err = sPool.LookupStorageVolByName(volume); err != nil { + volCfg := libvirtxml.StorageVolume{ + Name: volume, + Capacity: &libvirtxml.StorageVolumeSize{ + Unit: "GiB", + Value: 20, }, - }, - } - xml, err := volCfg.Marshal() - if err != nil { - return err - } - if _, err = sPool.StorageVolCreateXML(xml, libvirt.STORAGE_VOL_CREATE_PREALLOC_METADATA); err != nil { - return err + Allocation: &libvirtxml.StorageVolumeSize{ + Unit: "GiB", + Value: 2, + }, + Target: &libvirtxml.StorageVolumeTarget{ + Format: &libvirtxml.StorageVolumeTargetFormat{ + Type: "qcow2", + }, + }, + } + xml, err := volCfg.Marshal() + if err != nil { + return err + } + if _, err = sPool.StorageVolCreateXML(xml, libvirt.STORAGE_VOL_CREATE_PREALLOC_METADATA); err != nil { + return err + } } } return nil @@ -227,52 +237,56 @@ func (l *LibvirtProvisioner) UploadPodvm(imagePath string, ctx context.Context, } length := fileStat.Size() - sVol, err := sPool.LookupStorageVolByName(l.volumeName) - if err != nil { - return err - } + lVolumes := [2]string{l.volumeName, l.alternateVolumeName} - stream, err := l.conn.NewStream(0) - if err != nil { - return err - } - - if err := sVol.Upload(stream, 0, uint64(length), libvirt.STORAGE_VOL_UPLOAD_SPARSE_STREAM); err != nil { - return err - } + for _, volume := range lVolumes { + sVol, err := sPool.LookupStorageVolByName(volume) + if err != nil { + return err + } - fileByteSlice, err := os.ReadFile(imagePath) - if err != nil { - return err - } + stream, err := l.conn.NewStream(0) + if err != nil { + return err + } - sent := 0 - source := func(stream *libvirt.Stream, nbytes int) ([]byte, error) { - tosend := nbytes - if tosend > (len(fileByteSlice) - sent) { - tosend = len(fileByteSlice) - sent + if err := sVol.Upload(stream, 0, uint64(length), libvirt.STORAGE_VOL_UPLOAD_SPARSE_STREAM); err != nil { + return err } - if tosend == 0 { - return []byte{}, nil + fileByteSlice, err := os.ReadFile(imagePath) + if err != nil { + return err } - data := fileByteSlice[sent : sent+tosend] - sent += tosend + sent := 0 + source := func(stream *libvirt.Stream, nbytes int) ([]byte, error) { + tosend := nbytes + if tosend > (len(fileByteSlice) - sent) { + tosend = len(fileByteSlice) - sent + } - return data, nil - } + if tosend == 0 { + return []byte{}, nil + } - if err := stream.SendAll(source); err != nil { - return err - } + data := fileByteSlice[sent : sent+tosend] + sent += tosend - if err := stream.Finish(); err != nil { - return err - } + return data, nil + } - if err := stream.Free(); err != nil { - return err + if err := stream.SendAll(source); err != nil { + return err + } + + if err := stream.Finish(); err != nil { + return err + } + + if err := stream.Free(); err != nil { + return err + } } return nil