containerized-data-importer/tests/datavolume_test.go
Matthew Arnold e17b60a129
VDDK: Add CRD field for extra configuration arguments (#3622)
* Add ExtraArgs field to VDDK CRD.

Signed-off-by: Matthew Arnold <marnold@redhat.com>

* Add tests for VDDK ExtraArgs field.

Add one unit test and rework existing functional test into a table for
both annotation and field entries.

Signed-off-by: Matthew Arnold <marnold@redhat.com>

* Update ExtraArgs DataVolume documentation.

Signed-off-by: Matthew Arnold <marnold@redhat.com>

---------

Signed-off-by: Matthew Arnold <marnold@redhat.com>
2025-04-01 23:56:35 +02:00

3588 lines
152 KiB
Go

package tests
import (
"context"
"encoding/base64"
"fmt"
"reflect"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/google/uuid"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
core "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
"kubevirt.io/containerized-data-importer/pkg/common"
controller "kubevirt.io/containerized-data-importer/pkg/controller/common"
dvc "kubevirt.io/containerized-data-importer/pkg/controller/datavolume"
"kubevirt.io/containerized-data-importer/pkg/controller/populators"
featuregates "kubevirt.io/containerized-data-importer/pkg/feature-gates"
"kubevirt.io/containerized-data-importer/pkg/util/naming"
"kubevirt.io/containerized-data-importer/tests/framework"
"kubevirt.io/containerized-data-importer/tests/utils"
)
const (
fastPollingInterval = 20 * time.Millisecond
pollingInterval = 2 * time.Second
timeout = 270 * time.Second
shortTimeout = 30 * time.Second
)
var _ = Describe("[vendor:cnv-qe@redhat.com][level:component]DataVolume tests", func() {
var sourcePvc *v1.PersistentVolumeClaim
var targetPvc *v1.PersistentVolumeClaim
fillData := "123456789012345678901234567890123456789012345678901234567890"
fillDataFSMD5sum := "fabc176de7eb1b6ca90b3aa4c7e035f3"
testFile := utils.DefaultPvcMountPath + "/source.txt"
fillCommand := "echo \"" + fillData + "\" >> " + testFile
f := framework.NewFramework("dv-func-test")
tinyCoreIsoURL := func() string {
return fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs)
}
httpsTinyCoreIsoURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreIsoURL, f.CdiInstallNs)
}
httpsTinyCoreQcow2URL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs)
}
httpsTinyCoreVmdkURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreVmdkURL, f.CdiInstallNs)
}
httpsTinyCoreVdiURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreVdiURL, f.CdiInstallNs)
}
httpsTinyCoreVhdURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreVhdURL, f.CdiInstallNs)
}
httpsTinyCoreVhdxURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreVhdxURL, f.CdiInstallNs)
}
httpsTinyCoreZstURL := func() string {
return fmt.Sprintf(utils.HTTPSTinyCoreZstURL, f.CdiInstallNs)
}
tinyCoreQcow2URL := func() string {
return fmt.Sprintf(utils.TinyCoreQcow2URL+".gz", f.CdiInstallNs)
}
tinyCoreIsoRegistryURL := func() string {
return fmt.Sprintf(utils.TinyCoreIsoRegistryURL, f.CdiInstallNs)
}
tinyCoreIsoRegistryProxyURL := func() string {
return fmt.Sprintf(utils.TinyCoreIsoRegistryProxyURL, f.CdiInstallNs)
}
tinyCoreIsoAuthURL := func() string {
return fmt.Sprintf(utils.TinyCoreIsoAuthURL, f.CdiInstallNs)
}
tarArchiveURL := func() string {
return fmt.Sprintf(utils.TarArchiveURL, f.CdiInstallNs)
}
InvalidQcowImagesURL := func() string {
return fmt.Sprintf(utils.InvalidQcowImagesURL, f.CdiInstallNs)
}
cirrosURL := func() string {
return fmt.Sprintf(utils.CirrosURL, f.CdiInstallNs)
}
cirrosGCSQCOWURL := func() string {
return fmt.Sprintf(utils.CirrosGCSQCOWURL, f.CdiInstallNs)
}
cirrosGCSRAWURL := func() string {
return fmt.Sprintf(utils.CirrosGCSRAWURL, f.CdiInstallNs)
}
imageioURL := func() string {
return fmt.Sprintf(utils.ImageioURL, f.CdiInstallNs)
}
vcenterURL := func() string {
return fmt.Sprintf(utils.VcenterURL, f.CdiInstallNs)
}
// An image that causes qemu-img info to allocate large amounts of RAM
invalidQcowLargeMemoryURL := func() string {
return InvalidQcowImagesURL() + "invalid-qcow-large-memory.img"
}
errorInvalidQcowLargeMemory := func() string {
return `Unable to process data: qemu-img: Could not open 'json: {"file.driver": "http", "file.url": "` + invalidQcowLargeMemoryURL() + `", "file.timeout": 3600}': L1 size too big`
}
createRegistryImportDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithRegistryImport(dataVolumeName, size, url)
cm, err := utils.CopyRegistryCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.Registry.CertConfigMap = &cm
return dataVolume
}
createProxyRegistryImportDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithRegistryImport(dataVolumeName, size, url)
cm, err := utils.CopyFileHostCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.Registry.CertConfigMap = &cm
return dataVolume
}
createVddkDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
// Find vcenter-simulator pod
pod, err := utils.FindPodByPrefix(f.K8sClient, f.CdiInstallNs, "vcenter-deployment", "app=vcenter")
Expect(err).ToNot(HaveOccurred())
Expect(pod).ToNot(BeNil())
// Get test VM UUID
id, err := f.RunKubectlCommand("exec", "-n", pod.Namespace, pod.Name, "--", "cat", "/tmp/vmid")
Expect(err).ToNot(HaveOccurred())
vmid, err := uuid.Parse(strings.TrimSpace(id))
Expect(err).ToNot(HaveOccurred())
// Get disk name
disk, err := f.RunKubectlCommand("exec", "-n", pod.Namespace, pod.Name, "--", "cat", "/tmp/vmdisk")
Expect(err).ToNot(HaveOccurred())
disk = strings.TrimSpace(disk)
Expect(err).ToNot(HaveOccurred())
// Create VDDK login secret
stringData := map[string]string{
common.KeyAccess: "user",
common.KeySecret: "pass",
}
backingFile := disk
secretRef := "vddksecret"
thumbprint := "testprint"
s, _ := utils.CreateSecretFromDefinition(f.K8sClient, utils.NewSecretDefinition(nil, stringData, nil, f.Namespace.Name, secretRef))
return utils.NewDataVolumeWithVddkImport(dataVolumeName, size, backingFile, s.Name, thumbprint, url, vmid.String())
}
createVddkWarmImportDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return f.CreateVddkWarmImportDataVolume(dataVolumeName, size, url)
}
createImageIoDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
cm, err := utils.CopyImageIOCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
stringData := map[string]string{
common.KeyAccess: "admin@internal",
common.KeySecret: "123456",
}
CreateImageIoDefaultInventory(f)
s, _ := utils.CreateSecretFromDefinition(f.K8sClient, utils.NewSecretDefinition(nil, stringData, nil, f.Namespace.Name, "mysecret"))
return utils.NewDataVolumeWithImageioImport(dataVolumeName, size, url, s.Name, cm, "123")
}
createImageIoDataVolumeNoExtents := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := createImageIoDataVolume(dataVolumeName, size, url)
CreateImageIoInventoryNoExtents(f)
return dataVolume
}
createImageIoWarmImportDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
cm, err := utils.CopyImageIOCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
stringData := map[string]string{
common.KeyAccess: "admin@internal",
common.KeySecret: "123456",
}
s, _ := utils.CreateSecretFromDefinition(f.K8sClient, utils.NewSecretDefinition(nil, stringData, nil, f.Namespace.Name, "mysecret"))
diskID := "disk-678"
snapshots := []string{
"cirros.raw",
"cirros-snapshot1.qcow2",
"cirros-snapshot2.qcow2",
}
CreateImageIoWarmImportInventory(f, diskID, "storagedomain-876", snapshots)
var checkpoints []cdiv1.DataVolumeCheckpoint
parent := ""
for _, checkpoint := range snapshots {
checkpoints = append(checkpoints, cdiv1.DataVolumeCheckpoint{Current: checkpoint, Previous: parent})
parent = checkpoint
}
return utils.NewDataVolumeWithImageioWarmImport(dataVolumeName, size, url, s.Name, cm, diskID, checkpoints, true)
}
updateWebhookPvcRendering := func(webhookRenderingLabel string) {
if webhookRenderingLabel == "true" {
EnableWebhookPvcRendering(f.CrClient)
}
}
AfterEach(func() {
if sourcePvc != nil {
By("[AfterEach] Clean up sourcePvc PVC")
err := f.DeletePVC(sourcePvc)
if err != nil {
fmt.Fprintf(GinkgoWriter, "Err: %s\n", err)
}
sourcePvc = nil
}
if targetPvc != nil {
By("[AfterEach] Clean up targetPvc PVC")
err := f.DeletePVC(targetPvc)
if err != nil {
fmt.Fprintf(GinkgoWriter, "Err: %s\n", err)
}
targetPvc = nil
}
})
Describe("Verify DataVolume", func() {
type dataVolumeTestArguments struct {
name string
size string
url func() string
dvFunc func(string, string, string) *cdiv1.DataVolume
errorMessage string
errorMessageFunc func() string
eventReason string
phase cdiv1.DataVolumePhase
repeat int
checkPermissions bool
addClaimAdoptionAnnotation bool
readyCondition *cdiv1.DataVolumeCondition
boundCondition *cdiv1.DataVolumeCondition
boundConditionWithPopulators *cdiv1.DataVolumeCondition
runningCondition *cdiv1.DataVolumeCondition
}
createHTTPSDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
cm, err := utils.CopyFileHostCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.HTTP.CertConfigMap = cm
return dataVolume
}
createHTTPSDataVolumeWeirdCertFilename := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
cm, err := utils.CreateCertConfigMapWeirdFilename(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.HTTP.CertConfigMap = cm
return dataVolume
}
createHTTPExtraHeaders := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
credentials := fmt.Sprintf("%s:%s", utils.AccessKeyValue, utils.SecretKeyValue)
credentials = base64.StdEncoding.EncodeToString([]byte(credentials))
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
dataVolume.Spec.Source.HTTP.ExtraHeaders = []string{
fmt.Sprintf("Authorization: Basic %s", credentials),
}
return dataVolume
}
createHTTPSecretExtraHeaders := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
credentials := fmt.Sprintf("%s:%s", utils.AccessKeyValue, utils.SecretKeyValue)
credentials = base64.StdEncoding.EncodeToString([]byte(credentials))
secretRef := "secretheaders"
stringData := map[string]string{
"secret": fmt.Sprintf("Authorization: Basic %s", credentials),
}
secret, err := utils.CreateSecretFromDefinition(f.K8sClient, utils.NewSecretDefinition(nil, stringData, nil, f.Namespace.Name, secretRef))
Expect(err).ToNot(HaveOccurred())
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
dataVolume.Spec.Source.HTTP.SecretExtraHeaders = []string{secret.Name}
return dataVolume
}
createCloneDataVolume := func(dataVolumeName, size, command string) *cdiv1.DataVolume {
sourcePodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
pvcDef := utils.NewPVCDefinition(pvcName, size, nil, nil)
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, command)
By(fmt.Sprintf("creating a new target PVC (datavolume) to clone %s", sourcePvc.Name))
return utils.NewCloningDataVolume(dataVolumeName, size, sourcePvc)
}
createBlankRawDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForBlankRawImage(dataVolumeName, size)
}
createUploadDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForUpload(dataVolumeName, size)
}
testDataVolume := func(args dataVolumeTestArguments) {
// Have to call the function in here, to make sure the BeforeEach in the Framework has run.
dataVolume := args.dvFunc(args.name, args.size, args.url())
if args.addClaimAdoptionAnnotation {
controller.AddAnnotation(dataVolume, controller.AnnAllowClaimAdoption, "true")
}
repeat := 1
if utils.IsHostpathProvisioner() && args.repeat > 0 {
// Repeat rapidly to make sure we don't get regular and scratch space on different nodes.
repeat = args.repeat
}
for i := 0; i < repeat; i++ {
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
waitForDvPhase(args.phase, dataVolume, f)
// verify PVC was created
By("verifying pvc was created")
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
By("Verifying the DV has the correct conditions and messages for those conditions")
usePopulator, err := dvc.CheckPVCUsingPopulators(pvc)
Expect(err).ToNot(HaveOccurred())
if usePopulator && args.boundConditionWithPopulators != nil {
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, args.readyCondition, args.runningCondition, args.boundConditionWithPopulators)
} else {
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, args.readyCondition, args.runningCondition, args.boundCondition)
}
By("Verifying event occurred")
Eventually(func() bool {
// Only find DV events, we know the PVC gets the same events
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace, "--field-selector=involvedObject.kind=DataVolume")
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return strings.Contains(events, args.eventReason) && strings.Contains(events, args.errorMessage)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
if args.checkPermissions {
// Verify the created disk image has the right permissions.
By("Verifying permissions are 660")
Eventually(func() bool {
result, _ := f.VerifyPermissions(f.Namespace, pvc)
return result
}, shortTimeout, pollingInterval).Should(BeTrue(), "Permissions on disk image are not 660")
err := utils.DeleteVerifierPod(f.K8sClient, f.Namespace.Name)
Expect(err).ToNot(HaveOccurred())
}
Expect(pvc.Annotations[controller.AnnCreatedForDataVolume]).To(Equal(string(dataVolume.UID)))
By("Cleaning up")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool {
_, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
return k8serrors.IsNotFound(err)
}, timeout, pollingInterval).Should(BeTrue())
}
}
testDataVolumeWithQuota := func(args dataVolumeTestArguments) {
By("Configure namespace quota")
err := f.CreateStorageQuota(int64(2), int64(512*1024*1024))
Expect(err).ToNot(HaveOccurred())
// Have to call the function in here, to make sure the BeforeEach in the Framework has run.
dataVolume := args.dvFunc(args.name, args.size, args.url())
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("Verify Quota was exceeded in events and dv conditions")
expectedPhase := cdiv1.Pending
boundCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "exceeded quota",
Reason: controller.ErrExceededQuota,
}
readyCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
Message: "exceeded quota",
Reason: controller.ErrExceededQuota,
}
waitForDvPhase(expectedPhase, dataVolume, f)
f.ExpectEvent(dataVolume.Namespace).Should(ContainSubstring(controller.ErrExceededQuota))
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, boundCondition, readyCondition)
By("Increase quota")
err = f.UpdateStorageQuota(int64(4), int64(4*1024*1024*1024))
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
waitForDvPhase(args.phase, dataVolume, f)
// verify PVC was created
By("verifying pvc was created")
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
By("Verifying the DV has the correct conditions and messages for those conditions")
usePopulator, err := dvc.CheckPVCUsingPopulators(pvc)
Expect(err).ToNot(HaveOccurred())
if usePopulator && args.boundConditionWithPopulators != nil {
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, args.readyCondition, args.runningCondition, args.boundConditionWithPopulators)
} else {
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, args.readyCondition, args.runningCondition, args.boundCondition)
}
By("Verifying event occurred")
Eventually(func() bool {
// Only find DV events, we know the PVC gets the same events
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace, "--field-selector=involvedObject.kind=DataVolume")
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return strings.Contains(events, args.eventReason) && strings.Contains(events, args.errorMessage)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
By("Cleaning up")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool {
_, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
return k8serrors.IsNotFound(err)
}, timeout, pollingInterval).Should(BeTrue())
err = f.DeleteStorageQuota()
Expect(err).ToNot(HaveOccurred())
}
DescribeTable("should", testDataVolume,
Entry("[rfe_id:1115][crit:high][test_id:1357]succeed creating import dv with given valid url", dataVolumeTestArguments{
name: "dv-http-import",
size: "1Gi",
url: tinyCoreIsoURL,
dvFunc: utils.NewDataVolumeWithHTTPImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("succeed creating import dv with adoption annotation", dataVolumeTestArguments{
name: "dv-http-import",
size: "1Gi",
url: tinyCoreIsoURL,
dvFunc: utils.NewDataVolumeWithHTTPImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
addClaimAdoptionAnnotation: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1115][crit:high][posneg:negative][test_id:1358]fail creating import dv due to invalid DNS entry", dataVolumeTestArguments{
name: "dv-http-import-invalid-url",
size: "1Gi",
url: func() string { return "http://i-made-this-up.kube-system/tinyCore.iso" },
dvFunc: utils.NewDataVolumeWithHTTPImport,
errorMessage: "Unable to connect to http data source",
eventReason: "Error",
phase: cdiv1.ImportInProgress,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import-invalid-url Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC dv-http-import-invalid-url Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Unable to connect to http data source",
Reason: "Error",
}}),
Entry("[rfe_id:1115][crit:high][posneg:negative][test_id:1359]fail creating import dv due to file not found", dataVolumeTestArguments{
name: "dv-http-import-404",
size: "1Gi",
url: func() string { return tinyCoreIsoURL() + "not.real.file" },
dvFunc: utils.NewDataVolumeWithHTTPImport,
errorMessage: "Unable to connect to http data source: expected status code 200, got 404. Status: 404 Not Found",
eventReason: "Error",
phase: cdiv1.ImportInProgress,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import-404 Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC dv-http-import-404 Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Unable to connect to http data source: expected status code 200, got 404. Status: 404 Not Found",
Reason: "Error",
}}),
Entry("[rfe_id:1120][crit:high][posneg:negative][test_id:2253]fail creating import dv: invalid qcow large memory", dataVolumeTestArguments{
name: "dv-invalid-qcow-large-memory",
size: "1Gi",
url: invalidQcowLargeMemoryURL,
dvFunc: utils.NewDataVolumeWithHTTPImport,
errorMessageFunc: errorInvalidQcowLargeMemory,
eventReason: "Error",
phase: cdiv1.ImportInProgress,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-invalid-qcow-large-memory Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC dv-invalid-qcow-large-memory Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "L1 size too big",
Reason: "Error",
}}),
Entry("[test_id:3931]succeed creating import dv with streaming image conversion", dataVolumeTestArguments{
name: "dv-http-stream-import",
size: "1Gi",
url: cirrosURL,
dvFunc: utils.NewDataVolumeWithHTTPImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-stream-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1115][crit:high][test_id:1379]succeed creating import dv with given valid url (https)", dataVolumeTestArguments{
name: "dv-https-import",
size: "1Gi",
url: httpsTinyCoreIsoURL,
dvFunc: createHTTPSDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-https-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1115][crit:high][test_id:1379]succeed creating import dv with given valid qcow2 url (https)", dataVolumeTestArguments{
name: "dv-https-import-qcow2",
size: "1Gi",
url: httpsTinyCoreQcow2URL,
dvFunc: createHTTPSDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-https-import-qcow2 Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1115][crit:high][test_id:1379]succeed creating import dv with given valid zst url (https)", dataVolumeTestArguments{
name: "dv-https-import-zst",
size: "1Gi",
url: httpsTinyCoreZstURL,
dvFunc: createHTTPSDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-https-import-zst Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("succeed creating import dv with custom https cert that has a weird filename", dataVolumeTestArguments{
name: "dv-https-import-qcow2",
size: "1Gi",
url: httpsTinyCoreQcow2URL,
dvFunc: createHTTPSDataVolumeWeirdCertFilename,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-https-import-qcow2 Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:7202][crit:high][posneg:positive][test_id:8277]succeed creating import dv with custom https headers", dataVolumeTestArguments{
name: "dv-http-import-headers",
size: "1Gi",
url: tinyCoreIsoAuthURL,
dvFunc: createHTTPExtraHeaders,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import-headers Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:7202][crit:high][posneg:positive][test_id:8278]succeed creating import dv with custom https headers from a secret", dataVolumeTestArguments{
name: "dv-http-import-headers",
size: "1Gi",
url: tinyCoreIsoAuthURL,
dvFunc: createHTTPSecretExtraHeaders,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import-headers Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1111][crit:high][test_id:1361]succeed creating blank image dv", dataVolumeTestArguments{
name: "blank-image-dv",
size: "1Gi",
url: func() string { return "" },
dvFunc: createBlankRawDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC blank-image-dv Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:138][crit:high][test_id:1362]succeed creating upload dv", dataVolumeTestArguments{
name: "upload-dv",
size: "1Gi",
url: func() string { return "" },
dvFunc: createUploadDataVolume,
eventReason: dvc.UploadReady,
phase: cdiv1.UploadReady,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
Reason: "TransferRunning",
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC upload-dv Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC upload-dv Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionTrue,
Reason: "Pod is running",
}}),
Entry("succeed creating upload dv with adoption annotation", dataVolumeTestArguments{
name: "upload-dv",
size: "1Gi",
url: func() string { return "" },
dvFunc: createUploadDataVolume,
eventReason: dvc.UploadReady,
phase: cdiv1.UploadReady,
addClaimAdoptionAnnotation: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
Reason: "TransferRunning",
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC upload-dv Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC upload-dv Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionTrue,
Reason: "Pod is running",
}}),
Entry("[rfe_id:1947][crit:high][test_id:2145]succeed creating import dv with given tar archive url", dataVolumeTestArguments{
name: "dv-tar-archive",
size: "1Gi",
url: tarArchiveURL,
dvFunc: utils.NewDataVolumeWithArchiveContent,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-tar-archive Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1947][crit:high][test_id:2220]fail creating import dv with non tar archive url", dataVolumeTestArguments{
name: "dv-non-tar-archive",
size: "1Gi",
url: tinyCoreIsoURL,
dvFunc: utils.NewDataVolumeWithArchiveContent,
errorMessage: "Unable to process data",
eventReason: "Error",
phase: cdiv1.ImportInProgress,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-non-tar-archive Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC dv-non-tar-archive Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Unable to process data",
Reason: "Error",
}}),
Entry("[test_id:3932]succeed creating dv from imageio source", Label("ImageIO"), Serial, dataVolumeTestArguments{
name: "dv-imageio-test",
size: "1Gi",
url: imageioURL,
dvFunc: createImageIoDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-imageio-test Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
PEntry("[quarantine][test_id:3937]succeed creating warm import dv from imageio source", Label("ImageIO"), Serial, dataVolumeTestArguments{
// The final snapshot importer pod will give an error due to the static response from the fake imageio
// it returns the previous snapshot data, which will fail the commit to the target image.
// the importer pod will restart and then succeed because the fake imageio now sends the
// right data. This is normal.
name: "dv-imageio-test",
size: "1Gi",
url: imageioURL,
dvFunc: createImageIoWarmImportDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-imageio-test Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[test_id:3945]succeed creating dv from imageio source that does not support extents query", Label("ImageIO"), Serial, dataVolumeTestArguments{
name: "dv-imageio-test",
size: "1Gi",
url: imageioURL,
dvFunc: createImageIoDataVolumeNoExtents,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-imageio-test Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1277][crit:high][test_id:1360]succeed creating clone dv", dataVolumeTestArguments{
name: "dv-clone-test1",
size: "1Gi",
url: func() string { return fillCommand }, // its not URL, but command, but the parameter lines up.
dvFunc: createCloneDataVolume,
eventReason: dvc.CloneSucceeded,
phase: cdiv1.Succeeded,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-clone-test1 Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Clone Complete",
Reason: "Completed",
}}),
Entry("succeed creating clone dv with adoption annotation", dataVolumeTestArguments{
name: "dv-clone-test1",
size: "1Gi",
url: func() string { return fillCommand }, // its not URL, but command, but the parameter lines up.
dvFunc: createCloneDataVolume,
eventReason: dvc.CloneSucceeded,
phase: cdiv1.Succeeded,
addClaimAdoptionAnnotation: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-clone-test1 Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Clone Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:1115][crit:high][test_id:1478]succeed creating import dv with given valid registry url", dataVolumeTestArguments{
name: "dv-import-registry",
size: "1Gi",
url: tinyCoreIsoRegistryURL,
dvFunc: createRegistryImportDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
repeat: 10,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-import-registry Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:4334][test_id:6433]succeed creating import dv with given valid registry url and DV barely big enough", dataVolumeTestArguments{
name: "dv-import-registry",
size: "22Mi", // The image has 18M
url: tinyCoreIsoRegistryURL,
dvFunc: createRegistryImportDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-import-registry Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[test_id:5077]succeed creating import dv from VDDK source", Label("VDDK"), dataVolumeTestArguments{
name: "dv-import-vddk",
size: "1Gi",
url: vcenterURL,
dvFunc: createVddkDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: false,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-import-vddk Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
PEntry("[quarantine][test_id:5078]succeed creating warm import dv from VDDK source", Label("VDDK"), dataVolumeTestArguments{
name: "dv-import-vddk",
size: "1Gi",
url: vcenterURL,
dvFunc: createVddkWarmImportDataVolume,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: false,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-import-vddk Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:XXXX][crit:high][test_id:XXXX]succeed creating import dv from GCS URL using RAW image", dataVolumeTestArguments{
name: "dv-gcs-raw-import",
size: "1Gi",
url: cirrosGCSRAWURL,
dvFunc: utils.NewDataVolumeWithGCSImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-gcs-raw-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[rfe_id:XXXX][crit:high][test_id:XXXX]succeed creating import dv from GCS URL using QCOW2 image", dataVolumeTestArguments{
name: "dv-gcs-qcow-import",
size: "1Gi",
url: cirrosGCSQCOWURL,
dvFunc: utils.NewDataVolumeWithGCSImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: true,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-gcs-qcow-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
)
savedVddkConfigMap := common.VddkConfigMap + "-saved"
createVddkDataVolumeWithInitImageURL := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dv := createVddkDataVolume(dataVolumeName, size, url)
configMap, err := f.K8sClient.CoreV1().ConfigMaps(f.CdiInstallNs).Get(context.TODO(), savedVddkConfigMap, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
vddkURL, ok := configMap.Data[common.VddkConfigDataKey]
Expect(ok).To(BeTrue())
dv.Spec.Source.VDDK.InitImageURL = vddkURL
return dv
}
// Similar to previous table, but with additional cleanup steps to save and restore VDDK image config map
DescribeTable("should", Serial, Label("VDDK"), func(args dataVolumeTestArguments) {
_, err := utils.CopyConfigMap(f.K8sClient, f.CdiInstallNs, common.VddkConfigMap, f.CdiInstallNs, savedVddkConfigMap, "")
Expect(err).ToNot(HaveOccurred())
err = utils.DeleteConfigMap(f.K8sClient, f.CdiInstallNs, common.VddkConfigMap)
Expect(err).ToNot(HaveOccurred())
defer func() {
_, err := utils.CopyConfigMap(f.K8sClient, f.CdiInstallNs, savedVddkConfigMap, f.CdiInstallNs, common.VddkConfigMap, "")
Expect(err).ToNot(HaveOccurred())
err = utils.DeleteConfigMap(f.K8sClient, f.CdiInstallNs, savedVddkConfigMap)
Expect(err).ToNot(HaveOccurred())
}()
testDataVolume(args)
},
Entry("[test_id:5079]should fail with \"AwaitingVDDK\" reason when VDDK image config map is not present", Label("VDDK"), dataVolumeTestArguments{
name: "dv-awaiting-vddk",
size: "1Gi",
url: vcenterURL,
dvFunc: createVddkDataVolume,
eventReason: "Pending",
phase: cdiv1.ImportScheduled,
checkPermissions: false,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: fmt.Sprintf("waiting for %s configmap or %s annotation for VDDK image", common.VddkConfigMap, controller.AnnVddkInitImageURL),
Reason: common.AwaitingVDDK,
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC dv-awaiting-vddk Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
}}),
Entry("[test_id:5080]succeed importing VDDK data volume with init image URL set", Label("VDDK"), dataVolumeTestArguments{
name: "dv-import-vddk",
size: "1Gi",
url: vcenterURL,
dvFunc: createVddkDataVolumeWithInitImageURL,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
checkPermissions: false,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-import-vddk Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
)
// similar to other tables but with check of quota
DescribeTable("should fail create pvc in namespace with storge quota, then succeed once the quota is large enough", testDataVolumeWithQuota,
Entry("[test_id:7737]when creating import dv with given valid url", dataVolumeTestArguments{
name: "dv-http-import",
size: "1Gi",
url: tinyCoreIsoURL,
dvFunc: utils.NewDataVolumeWithHTTPImport,
eventReason: dvc.ImportSucceeded,
phase: cdiv1.Succeeded,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-http-import Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Import Complete",
Reason: "Completed",
}}),
Entry("[test_id:7738]when creating upload dv", dataVolumeTestArguments{
name: "upload-dv",
size: "1Gi",
url: func() string { return "" },
dvFunc: createUploadDataVolume,
eventReason: dvc.UploadReady,
phase: cdiv1.UploadReady,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
Reason: "TransferRunning",
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC upload-dv Bound",
Reason: "Bound",
},
boundConditionWithPopulators: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC upload-dv Pending",
Reason: "Pending",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionTrue,
Reason: "Pod is running",
}}),
Entry("[test_id:7739]when creating clone dv", dataVolumeTestArguments{
name: "dv-clone-test",
size: "500Mi",
url: func() string { return fillCommand }, // its not URL, but command, but the parameter lines up.
dvFunc: createCloneDataVolume,
eventReason: dvc.CloneSucceeded,
phase: cdiv1.Succeeded,
readyCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionTrue,
},
boundCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionTrue,
Message: "PVC dv-clone-test Bound",
Reason: "Bound",
},
runningCondition: &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeRunning,
Status: v1.ConditionFalse,
Message: "Clone Complete",
Reason: "Completed",
}}),
)
DescribeTable("should have an alert suppressing label and inherit labels & annotations from DV on corresponding PVC", func(dvFunc func(string, string, string) *cdiv1.DataVolume, url string) {
dataVolume := dvFunc("dv-labels-behaviour", "1Gi", url)
dataVolume.Labels = map[string]string{"test-label-1": "test-label-1", "test-label-2": "test-label-2"}
dataVolume.Annotations = map[string]string{"test-annotation-1": "test-annotation-1", "test-annotation-2": "test-annotation-2"}
// Stir things up with this non widely used access mode
dataVolume.Spec.PVC.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
// verify PVC was created
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
// Alert-suppressing label exists
Expect(pvc.Labels[common.KubePersistentVolumeFillingUpSuppressLabelKey]).To(Equal(common.KubePersistentVolumeFillingUpSuppressLabelValue))
// All labels and annotations passed
Expect(pvc.Labels["test-label-1"]).To(Equal("test-label-1"))
Expect(pvc.Labels["test-label-2"]).To(Equal("test-label-2"))
Expect(pvc.Annotations["test-annotation-1"]).To(Equal("test-annotation-1"))
Expect(pvc.Annotations["test-annotation-2"]).To(Equal("test-annotation-2"))
},
Entry("[test_id:8043]for import DataVolume", utils.NewDataVolumeWithHTTPImport, tinyCoreIsoURL()),
Entry("[test_id:8044]for upload DataVolume", createUploadDataVolume, tinyCoreIsoURL()),
Entry("[test_id:8045]for clone DataVolume", createCloneDataVolume, fillCommand),
)
Context("default virt storage class", Serial, func() {
var defaultVirtStorageClass *storagev1.StorageClass
var dummyStorageClass *storagev1.StorageClass
var defaultStorageClass *storagev1.StorageClass
getDefaultStorageClassName := func() string {
return utils.DefaultStorageClass.GetName()
}
getDefaultVirtStorageClassName := func() string {
return defaultVirtStorageClass.GetName()
}
getDummyStorageClassName := func() string {
return dummyStorageClass.GetName()
}
importFunc := func() *cdiv1.DataVolume {
return utils.NewDataVolumeWithHTTPImportAndStorageSpec("dv-virt-sc-test-import", "1Gi", tinyCoreIsoURL())
}
importFuncPVCAPI := func() *cdiv1.DataVolume {
return utils.NewDataVolumeWithHTTPImport("dv-virt-sc-test-import", "1Gi", tinyCoreIsoURL())
}
importExplicitScFunc := func() *cdiv1.DataVolume {
dv := utils.NewDataVolumeWithHTTPImportAndStorageSpec("dv-virt-sc-test-import", "1Gi", tinyCoreIsoURL())
sc := getDummyStorageClassName()
dv.Spec.Storage.StorageClassName = &sc
return dv
}
uploadFunc := func() *cdiv1.DataVolume {
return utils.NewDataVolumeForUploadWithStorageAPI("dv-virt-sc-test-upload", "1Gi")
}
cloneFunc := func() *cdiv1.DataVolume {
sourcePodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
pvcDef := utils.NewPVCDefinition(pvcName, "1Gi", nil, nil)
sourcePvc := f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand)
By(fmt.Sprintf("creating a new target PVC (datavolume) to clone %s", sourcePvc.Name))
return utils.NewDataVolumeForImageCloningAndStorageSpec("dv-virt-sc-test-clone", "1Gi", f.Namespace.Name, sourcePvc.Name, nil, nil)
}
archiveFunc := func() *cdiv1.DataVolume {
return utils.NewDataVolumeWithArchiveContentStorage("dv-virt-sc-test-archive", "1Gi", tarArchiveURL())
}
BeforeEach(func() {
addVirtParam := func(sc *storagev1.StorageClass) {
if len(sc.Parameters) == 0 {
sc.Parameters = map[string]string{}
}
sc.Parameters["better.for.kubevirt.io"] = "true"
controller.AddAnnotation(sc, controller.AnnDefaultVirtStorageClass, "true")
}
addDummyAnn := func(sc *storagev1.StorageClass) {
controller.AddAnnotation(sc, "dummy", "true")
}
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), getDefaultStorageClassName(), metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
defaultStorageClass = sc
defaultVirtStorageClass, err = f.CreateNonDefaultVariationOfStorageClass(sc, addVirtParam)
Expect(err).ToNot(HaveOccurred())
dummyStorageClass, err = f.CreateNonDefaultVariationOfStorageClass(sc, addDummyAnn)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err := f.K8sClient.StorageV1().StorageClasses().Delete(context.TODO(), defaultVirtStorageClass.Name, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
err = f.K8sClient.StorageV1().StorageClasses().Delete(context.TODO(), dummyStorageClass.Name, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
if defaultStorageClass.Annotations[controller.AnnDefaultStorageClass] != "true" {
controller.AddAnnotation(defaultStorageClass, controller.AnnDefaultStorageClass, "true")
_, err = f.K8sClient.StorageV1().StorageClasses().Update(context.TODO(), defaultStorageClass, metav1.UpdateOptions{})
Expect(err).ToNot(HaveOccurred())
}
})
DescribeTable("Should", func(dvFunc func() *cdiv1.DataVolume, getExpectedStorageClassName func() string, removeDefault bool) {
var err error
// Default storage class exists check
_ = utils.GetDefaultStorageClass(f.K8sClient)
if removeDefault {
controller.AddAnnotation(defaultStorageClass, controller.AnnDefaultStorageClass, "false")
defaultStorageClass, err = f.K8sClient.StorageV1().StorageClasses().Update(context.TODO(), defaultStorageClass, metav1.UpdateOptions{})
Expect(err).ToNot(HaveOccurred())
}
dataVolume := dvFunc()
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
// verify PVC was created
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.StorageClassName).To(HaveValue(Equal(getExpectedStorageClassName())))
},
Entry("[test_id:10505]respect default virt storage class for import DataVolume", importFunc, getDefaultVirtStorageClassName, false),
Entry("[test_id:10506]respect default virt storage class for upload DataVolume", uploadFunc, getDefaultVirtStorageClassName, false),
Entry("[test_id:10507]respect default virt storage class for clone DataVolume", cloneFunc, getDefaultVirtStorageClassName, false),
Entry("[test_id:10508]respect default virt storage class even if no k8s default exists", importFunc, getDefaultVirtStorageClassName, true),
Entry("[test_id:10509]not respect default virt storage class for contenType other than kubevirt", archiveFunc, getDefaultStorageClassName, false),
Entry("[test_id:10510]not respect default virt storage class for PVC api", importFuncPVCAPI, getDefaultStorageClassName, false),
Entry("[test_id:10511]not respect default virt storage class if explicit storage class provided", importExplicitScFunc, getDummyStorageClassName, false),
)
})
It("Should handle a pre populated DV", func() {
By(fmt.Sprintf("initializing dataVolume marked as prePopulated %s", dataVolumeName))
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", cirrosURL())
dataVolume.Annotations["cdi.kubevirt.io/storage.prePopulated"] = dataVolumeName
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("Verifying Pending without PVC")
Eventually(func() cdiv1.DataVolumePhase {
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
return dv.Status.Phase
}, timeout, pollingInterval).Should(Equal(cdiv1.Pending))
_, err = f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(k8serrors.IsNotFound(err)).To(BeTrue(), "PVC Should not exist")
By("Create PVC")
annotations := map[string]string{"cdi.kubevirt.io/storage.populatedFor": dataVolumeName}
pvc := utils.NewPVCDefinition(dataVolumeName, "100Mi", annotations, nil)
pvc = f.CreateBoundPVCFromDefinition(pvc)
By("Verifying Succeed with PVC Bound")
err = utils.WaitForDataVolumePhase(f, dataVolume.Namespace, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Verifying PVC owned by DV")
Eventually(func() bool {
pvc, err = f.K8sClient.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(context.TODO(), pvc.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
pvcOwner := metav1.GetControllerOf(pvc)
return pvcOwner != nil && pvcOwner.Kind == "DataVolume" && pvcOwner.Name == dataVolume.Name
}, timeout, pollingInterval).Should(BeTrue())
})
It("[test_id:4961] should handle a pre populated PVC for import DV", func() {
By(fmt.Sprintf("initializing target PVC %s", dataVolumeName))
targetPodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
annotations := map[string]string{controller.AnnPopulatedFor: dataVolumeName}
targetPvcDef := utils.NewPVCDefinition(dataVolumeName, "1G", annotations, nil)
targetPvc = f.CreateAndPopulateSourcePVC(targetPvcDef, targetPodFillerName, fillCommand)
By(fmt.Sprintf("creating new populated datavolume %s", dataVolumeName))
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", cirrosURL())
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool {
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
pvcName := dv.Annotations["cdi.kubevirt.io/storage.prePopulated"]
return pvcName == targetPvcDef.Name &&
dv.Status.Phase == cdiv1.Succeeded &&
string(dv.Status.Progress) == "N/A"
}, timeout, pollingInterval).Should(BeTrue())
By("Verify no import - the contents of prepopulated volume did not change")
md5Match, err := f.VerifyTargetPVCContentMD5(f.Namespace, targetPvc, testFile, fillDataFSMD5sum)
Expect(err).ToNot(HaveOccurred())
Expect(md5Match).To(BeTrue())
})
It("should handle a pre populated PVC for upload DV", func() {
By(fmt.Sprintf("initializing target PVC %s", dataVolumeName))
targetPodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
annotations := map[string]string{controller.AnnPopulatedFor: dataVolumeName}
targetPvcDef := utils.NewPVCDefinition(dataVolumeName, "1G", annotations, nil)
targetPvc = f.CreateAndPopulateSourcePVC(targetPvcDef, targetPodFillerName, fillCommand)
By(fmt.Sprintf("creating new populated datavolume %s", dataVolumeName))
dataVolume := utils.NewDataVolumeForUpload(dataVolumeName, "1Gi")
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool {
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
pvcName := dv.Annotations["cdi.kubevirt.io/storage.prePopulated"]
return pvcName == targetPvcDef.Name &&
dv.Status.Phase == cdiv1.Succeeded &&
string(dv.Status.Progress) == "N/A"
}, timeout, pollingInterval).Should(BeTrue())
By("Verify no upload - the contents of prepopulated volume did not change")
md5Match, err := f.VerifyTargetPVCContentMD5(f.Namespace, targetPvc, testFile, fillDataFSMD5sum)
Expect(err).ToNot(HaveOccurred())
Expect(md5Match).To(BeTrue())
})
})
Describe("[rfe_id:1111][test_id:2001][crit:low][vendor:cnv-qe@redhat.com][level:component]Verify multiple blank disk creations in parallel", Serial, func() {
var (
dataVolume1, dataVolume2, dataVolume3, dataVolume4 *cdiv1.DataVolume
)
AfterEach(func() {
dvs := []*cdiv1.DataVolume{dataVolume1, dataVolume2, dataVolume3, dataVolume4}
for _, dv := range dvs {
cleanDv(f, dv)
}
})
It("Should create all of them successfully", func() {
dataVolume1 = utils.NewDataVolumeForBlankRawImage("dv-1", "100Mi")
dataVolume2 = utils.NewDataVolumeForBlankRawImage("dv-2", "100Mi")
dataVolume3 = utils.NewDataVolumeForBlankRawImage("dv-3", "100Mi")
dataVolume4 = utils.NewDataVolumeForBlankRawImage("dv-4", "100Mi")
dvs := []*cdiv1.DataVolume{dataVolume1, dataVolume2, dataVolume3, dataVolume4}
for _, dv := range dvs {
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
}
By("Waiting for Datavolume to have succeeded")
for _, dv := range dvs {
err := utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.Succeeded, dv.Name)
Expect(err).ToNot(HaveOccurred())
Expect(f.VerifyBlankDisk(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(dv))).To(BeTrue())
}
})
})
Describe("Verify DataVolume with block mode", func() {
var err error
var dataVolume *cdiv1.DataVolume
AfterEach(func() {
if dataVolume != nil {
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
}
})
DescribeTable("should", func(name, command string, url func() string, dataVolumeName, eventReason string, phase cdiv1.DataVolumePhase) {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
switch name {
case "import-http":
dataVolume = utils.NewDataVolumeWithHTTPImportToBlockPV(dataVolumeName, "1G", url(), f.BlockSCName)
case "import-vddk":
dataVolume = createVddkDataVolume(dataVolumeName, "1Gi", vcenterURL())
utils.ModifyDataVolumeWithImportToBlockPV(dataVolume, f.BlockSCName)
case "warm-import-vddk":
dataVolume = createVddkWarmImportDataVolume(dataVolumeName, "1Gi", vcenterURL())
utils.ModifyDataVolumeWithImportToBlockPV(dataVolume, f.BlockSCName)
case "import-imageio":
dataVolume = createImageIoDataVolume(dataVolumeName, "1Gi", imageioURL())
utils.ModifyDataVolumeWithImportToBlockPV(dataVolume, f.BlockSCName)
case "warm-import-imageio":
dataVolume = createImageIoWarmImportDataVolume(dataVolumeName, "1Gi", imageioURL())
utils.ModifyDataVolumeWithImportToBlockPV(dataVolume, f.BlockSCName)
}
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By(fmt.Sprintf("waiting for datavolume to match phase %s", string(phase)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, phase, dataVolume.Name)
if err != nil {
fmt.Fprintf(GinkgoWriter, "Failed to wait for DataVolume phase: %v", err)
dv, dverr := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
if dverr != nil {
Fail(fmt.Sprintf("datavolume %s phase %s", dv.Name, dv.Status.Phase))
}
}
Expect(err).ToNot(HaveOccurred())
// verify PVC was created
By("verifying pvc was created")
_, err = f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
By("Verifying event occurred")
Eventually(func() bool {
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace)
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return strings.Contains(events, eventReason)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
},
Entry("[test_id:3933]succeed creating import dv with given valid url", "import-http", "", tinyCoreIsoURL, "dv-phase-test-1", dvc.ImportSucceeded, cdiv1.Succeeded),
Entry("[test_id:3935]succeed import from VDDK to block volume", Label("VDDK"), "import-vddk", "", nil, "dv-vddk-import-test", dvc.ImportSucceeded, cdiv1.Succeeded),
Entry("[test_id:3936]succeed warm import from VDDK to block volume", Label("VDDK"), "warm-import-vddk", "", nil, "dv-vddk-warm-import-test", dvc.ImportSucceeded, cdiv1.Succeeded),
Entry("[test_id:3938]succeed import from ImageIO to block volume", Label("ImageIO"), Serial, "import-imageio", "", nil, "dv-imageio-import-test", dvc.ImportSucceeded, cdiv1.Succeeded),
Entry("[test_id:3944]succeed warm import from ImageIO to block volume", Label("ImageIO"), Serial, "warm-import-imageio", "", nil, "dv-imageio-warm-import-test", dvc.ImportSucceeded, cdiv1.Succeeded),
)
})
DescribeTable("Succeed HTTPS import in various formats", func(url func() string, skipOnOpenshift bool) {
if skipOnOpenshift && utils.IsOpenshift(f.K8sClient) {
Skip("This test doesn't work when building using centos, see: https://bugzilla.redhat.com/show_bug.cgi?id=2013331")
}
By(fmt.Sprintf("Importing from %s", url()))
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", url())
cm, err := utils.CopyFileHostCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.HTTP.CertConfigMap = cm
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Verify content")
pvc, err := utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
md5, err := f.GetMD5(f.Namespace, pvc, utils.DefaultImagePath, utils.MD5PrefixSize)
Expect(err).ToNot(HaveOccurred())
Expect(md5).To(Equal(utils.TinyCoreMD5))
},
Entry("when importing in the VMDK format", httpsTinyCoreVmdkURL, false),
Entry("When importing in the VDI format", httpsTinyCoreVdiURL, true),
Entry("when importing in the VHD format", httpsTinyCoreVhdURL, false),
Entry("when importing in the VHDX format", httpsTinyCoreVhdxURL, false),
)
Describe("[rfe_id:1115][crit:high][posneg:negative]Delete resources of DataVolume with an invalid URL (POD in retry loop)", Serial, func() {
Context("using invalid import URL for DataVolume", func() {
dataVolumeName := "invalid-url-dv"
url := "http://nothing.2.c/here.iso"
It("[test_id:1363]should create/delete all resources", func() {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", url)
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By(fmt.Sprintf("waiting for datavolume to match phase %s", cdiv1.ImportInProgress))
Expect(utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.ImportInProgress, dataVolume.Name)).To(Succeed())
By("verifying pvc and pod were created")
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
usePopulator, err := dvc.CheckPVCUsingPopulators(pvc)
Expect(err).ToNot(HaveOccurred())
podName := pvc.Annotations[controller.AnnImportPod]
if usePopulator {
pvcPrimeName := populators.PVCPrimeName(pvc)
pvcPrime, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), pvcPrimeName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
podName = pvcPrime.Annotations[controller.AnnImportPod]
}
pod, err := f.K8sClient.CoreV1().Pods(f.Namespace.Name).Get(context.TODO(), podName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
By("deleting DataVolume")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolumeName)
Expect(err).ToNot(HaveOccurred())
By("verifying pod was deleted")
deleted, err := utils.WaitPodDeleted(f.K8sClient, pod.Name, f.Namespace.Name, timeout)
Expect(deleted).To(BeTrue())
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was deleted")
deleted, err = utils.WaitPVCDeleted(f.K8sClient, pvc.Name, f.Namespace.Name, timeout)
Expect(deleted).To(BeTrue())
Expect(err).ToNot(HaveOccurred())
})
})
})
Describe("Create/Delete same datavolume in a loop", Serial, func() {
Context("retry loop", func() {
numTries := 5
It(fmt.Sprintf("[test_id:3939][test_id:3940][test_id:3941][test_id:3942][test_id:3943] should succeed on %d loops", numTries), func() {
var prevPvcUID string
dataVolumeName := "test-dv"
dataVolumeNamespace := f.Namespace
for i := 1; i <= numTries; i++ {
By(fmt.Sprintf("running loop %d", i))
url := fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs)
dv := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", url)
By(fmt.Sprintf("creating new datavolume %s", dataVolumeName))
// the DV creation must not fail eventhough the PVC of the previous created DV is still terminating
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, dataVolumeNamespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By("verifying pvc was created and is bound")
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("verifying if the created pvc is not the old deleted one")
var pvc *v1.PersistentVolumeClaim
Eventually(func() bool {
pvc, err = f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
if err != nil {
return false
}
// Return true if the pvc is not being deleted, and the UID is no longer the original one.
return pvc.DeletionTimestamp == nil && string(pvc.UID) != prevPvcUID
}, timeout, pollingInterval).Should(BeTrue())
// We use the PVC UID to confirm later a new PVC was created
prevPvcUID = string(pvc.UID)
By(fmt.Sprintf("waiting for datavolume to match phase %s", cdiv1.Succeeded))
err = utils.WaitForDataVolumePhase(f, dataVolume.Namespace, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("deleting DataVolume")
err = utils.DeleteDataVolume(f.CdiClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
}
})
})
})
Describe("Pass specific datavolume annotations to the transfer pods", func() {
verifyAnnotations := func(pod *v1.Pod) {
By("verifying passed annotation")
Expect(pod.Annotations[controller.AnnPodNetwork]).To(Equal("net1"))
Expect(pod.Annotations[controller.AnnPodSidecarInjectionIstio]).To(Equal(controller.AnnPodSidecarInjectionIstioDefault))
Expect(pod.Annotations[controller.AnnPodSidecarInjectionLinkerd]).To(Equal(controller.AnnPodSidecarInjectionLinkerdDefault))
By("verifying non-passed annotation")
Expect(pod.Annotations["annot1"]).ToNot(Equal("value1"))
}
It("[test_id:5353]Importer pod should have specific datavolume annotations passed but not others", func() {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs))
By(fmt.Sprintf("creating new datavolume %s with annotations", dataVolume.Name))
dataVolume.Annotations[controller.AnnPodNetwork] = "net1"
dataVolume.Annotations["annot1"] = "value1"
dataVolume.Annotations[controller.AnnPodRetainAfterCompletion] = "true"
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("find importer pod")
var sourcePod *v1.Pod
Eventually(func() bool {
sourcePod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.ImporterPodName, common.CDILabelSelector)
return err == nil
}, timeout, pollingInterval).Should(BeTrue())
By(fmt.Sprintf("Verifying pod %s has correct annotation", sourcePod.Name))
verifyAnnotations(sourcePod)
})
It("[test_id:5365]Uploader pod should have specific datavolume annotations passed but not others", func() {
dataVolume := utils.NewDataVolumeForUpload(dataVolumeName, "1Gi")
By(fmt.Sprintf("creating new datavolume %s with annotations", dataVolume.Name))
dataVolume.Annotations[controller.AnnPodNetwork] = "net1"
dataVolume.Annotations["annot1"] = "value1"
dataVolume.Annotations[controller.AnnPodRetainAfterCompletion] = "true"
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("find uploader pod")
var sourcePod *v1.Pod
Eventually(func() bool {
sourcePod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.UploadPodName, common.CDILabelSelector)
return err == nil
}, timeout, pollingInterval).Should(BeTrue())
verifyAnnotations(sourcePod)
})
It("[test_id:5366]Cloner pod should have specific datavolume annotations passed but not others", func() {
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), f.SnapshotSCName, metav1.GetOptions{})
if err == nil {
value, ok := sc.Annotations["storageclass.kubernetes.io/is-default-class"]
if smartApplicable && ok && strings.Compare(value, "true") == 0 {
Skip("Cannot test if annotations are present when all pvcs are smart clone capable.")
}
}
sourceDv := utils.NewDataVolumeWithHTTPImport("source-dv", "1Gi", tinyCoreQcow2URL())
Expect(sourceDv).ToNot(BeNil())
By(fmt.Sprintf("creating new source dv %s with annotations", sourceDv.Name))
sourceDv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, sourceDv.Namespace, sourceDv.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
dataVolume := utils.NewCloningDataVolume(dataVolumeName, "1Gi", pvc)
Expect(dataVolume).ToNot(BeNil())
By(fmt.Sprintf("creating new datavolume %s with annotations", dataVolume.Name))
dataVolume.Annotations[controller.AnnPodNetwork] = "net1"
dataVolume.Annotations["annot1"] = "value1"
dataVolume.Annotations[controller.AnnPodRetainAfterCompletion] = "true"
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
var sourcePod *v1.Pod
var uploadPod *v1.Pod
Eventually(func() error {
uploadPod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.UploadPodName, common.CDILabelSelector)
return err
}, timeout, pollingInterval).Should(BeNil())
verifyAnnotations(uploadPod)
// Remove non existent network so upload pod succeeds and clone can continue (some envs like OpenShift check network validity)
Eventually(func() error {
uploadPod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, "cdi-upload", common.CDILabelSelector)
if err != nil {
return err
}
delete(uploadPod.Annotations, controller.AnnPodNetwork)
_, err = f.K8sClient.CoreV1().Pods(dataVolume.Namespace).Update(context.TODO(), uploadPod, metav1.UpdateOptions{})
return err
}, 60*time.Second, 2*time.Second).Should(BeNil())
Eventually(func() error {
sourcePod, err = utils.FindPodBySuffix(f.K8sClient, dataVolume.Namespace, "source-pod", common.CDILabelSelector)
return err
}, timeout, pollingInterval).Should(BeNil())
verifyAnnotations(sourcePod)
})
})
Describe("Create a PVC using data from StorageProfile", Serial, func() {
var (
config *cdiv1.CDIConfig
origSpec *cdiv1.CDIConfigSpec
originalProfileSpec *cdiv1.StorageProfileSpec
defaultSc *storagev1.StorageClass
defaultScName string
err error
)
fillData := "123456789012345678901234567890123456789012345678901234567890"
testFile := utils.DefaultPvcMountPath + "/source.txt"
fillCommand := "echo \"" + fillData + "\" >> " + testFile
createLabeledDataVolumeForImport := func(f *framework.Framework, storageSpec cdiv1.StorageSpec, labels map[string]string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithHTTPImportAndStorageSpec(
dataVolumeName, "1Gi", fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs))
dataVolume.Spec.Storage = &storageSpec
dataVolume.Labels = labels
dv, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
return dv
}
createDataVolumeForImport := func(f *framework.Framework, storageSpec cdiv1.StorageSpec) *cdiv1.DataVolume {
return createLabeledDataVolumeForImport(f, storageSpec, nil)
}
createDataVolumeForUpload := func(f *framework.Framework, storageSpec cdiv1.StorageSpec) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeForUpload(dataVolumeName, "1Mi")
dataVolume.Spec.PVC = nil
dataVolume.Spec.Storage = &storageSpec
dv, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
return dv
}
createCloneDataVolume := func(dataVolumeName string, storageSpec cdiv1.StorageSpec, command string) *cdiv1.DataVolume {
sourcePodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
pvcDef := utils.NewPVCDefinition(pvcName, "10Mi", nil, nil)
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, command)
By(fmt.Sprintf("creating a new target PVC (datavolume) to clone %s", sourcePvc.Name))
dataVolume := utils.NewCloningDataVolume(dataVolumeName, "10Mi", sourcePvc)
dataVolume.Spec.PVC = nil
dataVolume.Spec.Storage = &storageSpec
dataVolume.Annotations[controller.AnnImmediateBinding] = "true"
dv, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
return dv
}
getStorageProfileSpec := func(client client.Client, storageClassName string) *cdiv1.StorageProfileSpec {
storageProfile := &cdiv1.StorageProfile{}
err := client.Get(context.TODO(), types.NamespacedName{Name: storageClassName}, storageProfile)
Expect(err).ToNot(HaveOccurred())
originalProfileSpec := storageProfile.Spec.DeepCopy()
return originalProfileSpec
}
updateStorageProfileSpec := func(client client.Client, name string, spec cdiv1.StorageProfileSpec) {
storageProfile := &cdiv1.StorageProfile{}
Eventually(func() error {
err := client.Get(context.TODO(), types.NamespacedName{Name: name}, storageProfile)
Expect(err).ToNot(HaveOccurred())
storageProfile.Spec = spec
return client.Update(context.TODO(), storageProfile)
}, 15*time.Second, time.Second).Should(BeNil())
}
configureStorageProfile := func(client client.Client,
storageClassName string,
accessModes []v1.PersistentVolumeAccessMode,
volumeMode v1.PersistentVolumeMode) {
propertySet := cdiv1.ClaimPropertySet{AccessModes: accessModes, VolumeMode: &volumeMode}
updateStorageProfileSpec(client,
storageClassName,
cdiv1.StorageProfileSpec{ClaimPropertySets: []cdiv1.ClaimPropertySet{propertySet}})
Eventually(func() cdiv1.ClaimPropertySet {
profile, err := f.CdiClient.CdiV1beta1().StorageProfiles().Get(context.TODO(), storageClassName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
if len(profile.Status.ClaimPropertySets) > 0 {
return profile.Status.ClaimPropertySets[0]
}
return cdiv1.ClaimPropertySet{}
}, time.Second*30, time.Second).Should(Equal(propertySet))
}
findStorageProfileWithoutAccessModes := func(client client.Client) string {
storageProfiles := &cdiv1.StorageProfileList{}
err := client.List(context.TODO(), storageProfiles)
Expect(err).ToNot(HaveOccurred())
for _, storageProfile := range storageProfiles.Items {
if len(storageProfile.Status.ClaimPropertySets) == 0 {
// No access modes set.
return *storageProfile.Status.StorageClass
}
for _, properties := range storageProfile.Status.ClaimPropertySets {
if len(properties.AccessModes) == 0 {
// No access modes set.
return *storageProfile.Status.StorageClass
}
}
}
Skip("No storage profiles without access mode found")
return ""
}
BeforeEach(func() {
defaultScName = utils.DefaultStorageClass.GetName()
utils.DefaultStorageClass, err = f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), defaultScName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
defaultSc = utils.DefaultStorageClass
config, err = f.CdiClient.CdiV1beta1().CDIConfigs().Get(context.TODO(), common.ConfigName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
origSpec = config.Spec.DeepCopy()
originalProfileSpec = getStorageProfileSpec(f.CrClient, defaultScName)
})
AfterEach(func() {
if defaultSc.Annotations[controller.AnnDefaultStorageClass] != "true" {
By("Restoring default storage class")
defaultSc.Annotations[controller.AnnDefaultStorageClass] = "true"
utils.DefaultStorageClass, err = f.K8sClient.StorageV1().StorageClasses().Update(context.TODO(), defaultSc, metav1.UpdateOptions{})
Expect(err).ToNot(HaveOccurred())
}
if originalProfileSpec != nil {
By("Restoring the StorageProfile to original state")
updateStorageProfileSpec(f.CrClient, defaultScName, *originalProfileSpec)
}
By("Restoring CDIConfig to original state")
err := utils.UpdateCDIConfig(f.CrClient, func(config *cdiv1.CDIConfigSpec) {
origSpec.DeepCopyInto(config)
})
DisableWebhookPvcRendering(f.CrClient)
Eventually(func() bool {
config, err = f.CdiClient.CdiV1beta1().CDIConfigs().Get(context.TODO(), common.ConfigName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
return reflect.DeepEqual(config.Spec, *origSpec)
}, 30*time.Second, time.Second).Should(BeTrue())
})
It("[test_id:5911]Import succeeds creating a PVC from DV without accessModes and storageClass name", func() {
By(fmt.Sprintf("configure storage profile %s", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeFilesystem)
requestedSize := resource.MustParse("100Mi")
spec := cdiv1.StorageSpec{
AccessModes: nil,
VolumeMode: nil,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
dataVolume := createDataVolumeForImport(f, spec)
By("verifying pvc created with correct accessModes and storageclass name")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
Expect(*pvc.Spec.StorageClassName).To(SatisfyAll(Not(BeNil()), Equal(defaultScName)))
})
It("[test_id:8170]Import succeeds when storage class is not specified, but access mode is", func() {
By(fmt.Sprintf("configure storage profile %s", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeFilesystem)
requestedSize := resource.MustParse("100Mi")
expectedMode := []v1.PersistentVolumeAccessMode{v1.ReadWriteMany}
spec := cdiv1.StorageSpec{
AccessModes: expectedMode,
VolumeMode: nil,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
dataVolume := createDataVolumeForImport(f, spec)
By("verifying pvc created with correct accessModes and storageclass name")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal(expectedMode))
Expect(*pvc.Spec.StorageClassName).To(SatisfyAll(Not(BeNil()), Equal(defaultScName)))
})
It("[test_id:8169]Import succeeds when storage class is not specified, but volume mode is", func() {
By(fmt.Sprintf("configure storage profile %s", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeFilesystem)
requestedSize := resource.MustParse("100Mi")
expectedVolumeMode := v1.PersistentVolumeBlock
spec := cdiv1.StorageSpec{
AccessModes: nil,
VolumeMode: &expectedVolumeMode,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
dataVolume := createDataVolumeForImport(f, spec)
By("verifying pvc created with correct accessModes and storageclass name")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
Expect(*pvc.Spec.VolumeMode).To(Equal(expectedVolumeMode))
Expect(*pvc.Spec.StorageClassName).To(SatisfyAll(Not(BeNil()), Equal(defaultScName)))
})
verifyControllerRenderingEvent := func(events string) bool {
return strings.Contains(events, controller.ErrClaimNotValid) && strings.Contains(events, "no accessMode defined DV nor on StorageProfile")
}
verifyControllerRenderingNoDefaultScEvent := func(events string) bool {
return strings.Contains(events, controller.ErrClaimNotValid) && strings.Contains(events, "PVC spec is missing accessMode and no storageClass to choose profile")
}
verifyWebhookRenderingEvent := func(events string) bool {
return strings.Contains(events, controller.NotFound) && strings.Contains(events, "No PVC found")
}
DescribeTable("Import fails creating a PVC from DV without accessModes and volume mode, no profile", func(webhookRenderingLabel string, verifyEvent func(string) bool) {
updateWebhookPvcRendering(webhookRenderingLabel)
// assumes local is available and has no volumeMode
storageProfileName := findStorageProfileWithoutAccessModes(f.CrClient)
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
requestedSize := resource.MustParse("100Mi")
spec := cdiv1.StorageSpec{
AccessModes: nil,
VolumeMode: nil,
StorageClassName: &storageProfileName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
dataVolume := createLabeledDataVolumeForImport(f, spec,
map[string]string{common.PvcApplyStorageProfileLabel: webhookRenderingLabel})
By("verifying pvc not created")
_, err := utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(k8serrors.IsNotFound(err)).To(BeTrue())
By("verifying event occurred")
Eventually(func() bool {
// Only find DV events, we know the PVC gets the same events
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace, "--field-selector=involvedObject.kind=DataVolume")
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return verifyEvent(events)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
},
Entry("[test_id:5912] (controller rendering)", "false", verifyControllerRenderingEvent),
Entry("[rfe_id:10985][crit:high][test_id:11045] (webhook rendering)", Serial, "true", verifyWebhookRenderingEvent),
)
DescribeTable("Import fails when no default storage class, and recovers when default is set", func(webhookRenderingLabel string, verifyEvent func(string) bool) {
updateWebhookPvcRendering(webhookRenderingLabel)
By("updating to no default storage class")
defaultSc.Annotations[controller.AnnDefaultStorageClass] = "false"
defaultSc, err = f.K8sClient.StorageV1().StorageClasses().Update(context.TODO(), defaultSc, metav1.UpdateOptions{})
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
requestedSize := resource.MustParse("100Mi")
spec := cdiv1.StorageSpec{
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
dataVolume := createLabeledDataVolumeForImport(f, spec,
map[string]string{common.PvcApplyStorageProfileLabel: webhookRenderingLabel})
By("verifying event occurred")
Eventually(func() bool {
// Only find DV events, we know the PVC gets the same events
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace, "--field-selector=involvedObject.kind=DataVolume")
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return verifyEvent(events)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
By("verifying pvc not created")
_, err = utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(k8serrors.IsNotFound(err)).To(BeTrue())
By("restoring default storage class")
defaultSc.Annotations[controller.AnnDefaultStorageClass] = "true"
defaultSc, err = f.K8sClient.StorageV1().StorageClasses().Update(context.TODO(), defaultSc, metav1.UpdateOptions{})
Expect(err).ToNot(HaveOccurred())
By("verifying pvc created")
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
},
Entry("[test_id:8383] (controller rendering)", "false", verifyControllerRenderingNoDefaultScEvent),
Entry("[rfe_id:10985][crit:high][test_id:11046] (webhook rendering)", Serial, "true", verifyWebhookRenderingEvent),
)
DescribeTable("Import recovers when user adds accessModes to profile", func(webhookRenderingLabel string, verifyEvent func(string) bool) {
updateWebhookPvcRendering(webhookRenderingLabel)
// assumes local is available and has no volumeMode
storageProfileName := findStorageProfileWithoutAccessModes(f.CrClient)
By(fmt.Sprintf("creating new datavolume %s without accessModes", dataVolumeName))
requestedSize := resource.MustParse("100Mi")
spec := cdiv1.StorageSpec{
AccessModes: nil,
VolumeMode: nil,
StorageClassName: &storageProfileName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}
dataVolume := createLabeledDataVolumeForImport(f, spec,
map[string]string{common.PvcApplyStorageProfileLabel: webhookRenderingLabel})
By("verifying pvc not created")
_, err := utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(k8serrors.IsNotFound(err)).To(BeTrue())
By("verifying event occurred")
Eventually(func() bool {
// Only find DV events, we know the PVC gets the same events
events, err := f.RunKubectlCommand("get", "events", "-n", dataVolume.Namespace, "--field-selector=involvedObject.kind=DataVolume")
if err == nil {
fmt.Fprintf(GinkgoWriter, "%s", events)
return verifyEvent(events)
}
fmt.Fprintf(GinkgoWriter, "ERROR: %s\n", err.Error())
return false
}, timeout, pollingInterval).Should(BeTrue())
By(fmt.Sprintf("configure storage profile %s", storageProfileName))
originalProfileSpec := getStorageProfileSpec(f.CrClient, storageProfileName)
configureStorageProfile(f.CrClient,
storageProfileName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeFilesystem)
By("verifying pvc created with correct accessModes")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
By("Restore the profile")
updateStorageProfileSpec(f.CrClient, storageProfileName, *originalProfileSpec)
},
Entry("[test_id:5913] (controller rendering)", "false", verifyControllerRenderingEvent),
Entry("[rfe_id:10985][crit:high][test_id:11047] (webhook rendering)", Serial, "true", verifyWebhookRenderingEvent),
)
It("[test_id:6483]Import pod should not have size corrected on block", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By("creating datavolume for upload")
volumeMode := v1.PersistentVolumeBlock
dataVolume := createDataVolumeForImport(f,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct size")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
})
It("[test_id:6484]Import pod should not have size corrected on block, when no volumeMode on DV", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By(fmt.Sprintf("configure storage profile %s to volumeModeBlock", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeBlock)
By("creating datavolume for upload")
dataVolume := createDataVolumeForImport(f,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct size")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
})
It("[test_id:6485]Import pod should have size corrected on filesystem", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// given 50 percent overhead, expected size is 2x requestedSize
expectedSize := resource.MustParse("200Mi")
By("creating clone dataVolume")
volumeMode := v1.PersistentVolumeFilesystem
dataVolume := createDataVolumeForImport(f,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct size")
// eventually because pvc will have to be resized if smart clone
Eventually(func() bool {
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
return pvc.Spec.Resources.Requests.Storage().Cmp(expectedSize) == 0
}, 1*time.Minute, 2*time.Second).Should(BeTrue())
})
It("[test_id:6099]Upload pvc should have size corrected on filesystem volume", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// given 50 percent overhead, expected size is 2x requestedSize
expectedSize := resource.MustParse("200Mi")
By("creating datavolume for upload")
volumeMode := v1.PersistentVolumeFilesystem
dataVolume := createDataVolumeForUpload(f, cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct accessModes")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
Expect(*pvc.Spec.StorageClassName).To(SatisfyAll(Not(BeNil()), Equal(defaultScName)))
})
It("[test_id:6100]Upload pvc should not have size corrected on block volume", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By("creating datavolume for upload")
volumeMode := v1.PersistentVolumeBlock
dataVolume := createDataVolumeForUpload(f, cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct accessModes")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
})
It("[test_id:6486]Upload pvc should not have size corrected on block volume, when no volumeMode on DV", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By(fmt.Sprintf("configure storage profile %s to volumeModeBlock", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeBlock)
By("creating datavolume for upload")
dataVolume := createDataVolumeForUpload(f, cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
})
By("verifying pvc created with correct accessModes and size")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
})
It("[test_id:6101]Clone pod should not have size corrected on block", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By("creating datavolume for upload")
volumeMode := v1.PersistentVolumeBlock
dataVolume := createCloneDataVolume(dataVolumeName,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}, fillCommand)
By("verifying pvc created with correct size")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
Expect(*pvc.Spec.StorageClassName).To(SatisfyAll(Not(BeNil()), Equal(defaultScName)))
})
It("[test_id:6487]Clone pod should not have size corrected on block, when no volumeMode on DV", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// volumeMode Block, so no overhead applied
expectedSize := resource.MustParse("100Mi")
By(fmt.Sprintf("configure storage profile %s to volumeModeBlock", defaultScName))
configureStorageProfile(f.CrClient,
defaultScName,
[]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
v1.PersistentVolumeBlock)
By("creating datavolume for upload")
dataVolume := createCloneDataVolume(dataVolumeName,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}, fillCommand)
By("verifying pvc created with correct size")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.Resources.Requests.Storage().Value()).To(Equal(expectedSize.Value()))
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
})
It("[test_id:6102]Clone pod should have size corrected on filesystem", func() {
SetFilesystemOverhead(f, "0.50", "0.50")
requestedSize := resource.MustParse("100Mi")
// given 50 percent overhead, expected size is 2x requestedSize
expectedSize := resource.MustParse("200Mi")
By("creating clone dataVolume")
volumeMode := v1.PersistentVolumeFilesystem
dataVolume := createCloneDataVolume(dataVolumeName,
cdiv1.StorageSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &volumeMode,
StorageClassName: &defaultScName,
Resources: v1.VolumeResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: requestedSize,
},
},
}, fillCommand)
By("verifying pvc created with correct size")
// eventually because pvc will have to be resized if smart clone
Eventually(func() bool {
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
Expect(pvc.Spec.AccessModes).To(Equal([]v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}))
return pvc.Spec.Resources.Requests.Storage().Cmp(expectedSize) == 0
}, 1*time.Minute, 2*time.Second).Should(BeTrue())
})
})
Describe("Verify that when the required storage class is missing", Serial, func() {
var (
testSc *storagev1.StorageClass
pvName string
)
testScName := "test-sc"
updatePV := func(updateFunc func(*v1.PersistentVolume)) {
Eventually(func() error {
pv, err := f.K8sClient.CoreV1().PersistentVolumes().Get(context.TODO(), pvName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
updateFunc(pv)
// We shouldn't make the test fail if there's a conflict with the update request.
// These errors are usually transient and should be fixed in subsequent retries.
_, err = f.K8sClient.CoreV1().PersistentVolumes().Update(context.TODO(), pv, metav1.UpdateOptions{})
return err
}, timeout, pollingInterval).Should(Succeed())
}
createPV := func(scName string) {
dv := utils.NewDataVolumeForBlankRawImage("blank-source", "106Mi")
dv, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dv)
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.Succeeded, dv.Name)
Expect(err).ToNot(HaveOccurred())
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dv.Namespace).Get(context.TODO(), dv.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
pvName = pvc.Spec.VolumeName
By("retaining pv")
updatePV(func(pv *v1.PersistentVolume) {
pv.Spec.PersistentVolumeReclaimPolicy = v1.PersistentVolumeReclaimRetain
})
utils.CleanupDvPvc(f.K8sClient, f.CdiClient, pvc.Namespace, pvc.Name)
updatePV(func(pv *v1.PersistentVolume) {
pv.Spec.StorageClassName = scName
pv.Spec.ClaimRef = nil
})
}
createStorageClass := func(scName string) {
var err error
By(fmt.Sprintf("creating storage class %s", scName))
sc := utils.DefaultStorageClass.DeepCopy()
sc.Name = scName
sc.ResourceVersion = ""
sc.Annotations[controller.AnnDefaultStorageClass] = "false"
testSc, err = f.K8sClient.StorageV1().StorageClasses().Create(context.TODO(), sc, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())
}
AfterEach(func() {
if testSc != nil {
err := f.K8sClient.StorageV1().StorageClasses().Delete(context.TODO(), testScName, metav1.DeleteOptions{})
Expect(err).ToNot(HaveOccurred())
testSc = nil
}
if pvName != "" {
updatePV(func(pv *v1.PersistentVolume) {
pv.Spec.PersistentVolumeReclaimPolicy = v1.PersistentVolumeReclaimDelete
})
pvName = ""
}
DisableWebhookPvcRendering(f.CrClient)
})
verifyControllerRenderingEventAndConditions := func(dv *cdiv1.DataVolume) {
By("verifying event occurred")
f.ExpectEvent(dv.Namespace).Should(And(ContainSubstring(controller.ErrClaimNotValid), ContainSubstring(dvc.MessageErrStorageClassNotFound)))
By("verifying conditions")
boundCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: dvc.MessageErrStorageClassNotFound,
Reason: controller.ErrClaimNotValid,
}
readyCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
Message: dvc.MessageErrStorageClassNotFound,
Reason: controller.ErrClaimNotValid,
}
utils.WaitForConditions(f, dv.Name, f.Namespace.Name, timeout, pollingInterval, boundCondition, readyCondition)
}
verifyWebhookRenderingEventAndConditions := func(dv *cdiv1.DataVolume) {
By("verifying event occurred")
f.ExpectEvent(dv.Namespace).Should(And(ContainSubstring(controller.NotFound), ContainSubstring("No PVC found")))
By("verifying conditions")
boundCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "No PVC found",
Reason: controller.NotFound,
}
readyCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
}
utils.WaitForConditions(f, dv.Name, f.Namespace.Name, timeout, pollingInterval, boundCondition, readyCondition)
}
DescribeTable("import DV using StorageSpec without AccessModes, PVC is created only when", func(webhookRenderingLabel, scName string, dvFunc func(*cdiv1.DataVolume), scFunc func(string)) {
if utils.IsDefaultSCNoProvisioner() {
Skip("Default storage class has no provisioner. The new storage class won't work")
}
updateWebhookPvcRendering(webhookRenderingLabel)
By(fmt.Sprintf("verifying no storage class %s", testScName))
_, err := f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), scName, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
By(fmt.Sprintf("creating new datavolume %s with StorageClassName %s", dataVolumeName, scName))
dataVolume := utils.NewDataVolumeWithHTTPImportAndStorageSpec(
dataVolumeName, "100Mi", fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs))
dataVolume.Labels = map[string]string{common.PvcApplyStorageProfileLabel: webhookRenderingLabel}
dataVolume.Spec.Storage.StorageClassName = ptr.To[string](scName)
dataVolume.Spec.Storage.AccessModes = nil
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
dvFunc(dataVolume)
By("verifying pvc not created")
_, err = utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(k8serrors.IsNotFound(err)).To(BeTrue())
scFunc(scName)
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By("waiting for pvc bound phase")
err = utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, dataVolume.Namespace, v1.ClaimBound, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("waiting for dv succeeded")
err = utils.WaitForDataVolumePhase(f, dataVolume.Namespace, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
},
Entry("[test_id:9922]the storage class is created (controller rendering)", "false", testScName, verifyControllerRenderingEventAndConditions, createStorageClass),
Entry("[test_id:9924]PV with the SC name is created (controller rendering)", "false", testScName, verifyControllerRenderingEventAndConditions, createPV),
Entry("[test_id:9925]PV with the SC name (\"\" blank) is created (controller rendering)", "false", "", verifyControllerRenderingEventAndConditions, createPV),
Entry("[rfe_id:10985][crit:high][test_id:11049]the storage class is created (webhook rendering)", Serial, "true", testScName, verifyWebhookRenderingEventAndConditions, createStorageClass),
Entry("[rfe_id:10985][crit:high][test_id:11050]PV with the SC name is created (webhook rendering)", Serial, "true", testScName, verifyWebhookRenderingEventAndConditions, createPV),
Entry("[rfe_id:10985][crit:high][test_id:11051]PV with the SC name (\"\" blank) is created (webhook rendering)", Serial, "true", "", verifyWebhookRenderingEventAndConditions, createPV),
)
newDataVolumeWithStorageSpec := func(scName string) *cdiv1.DataVolume {
dv := utils.NewDataVolumeWithHTTPImportAndStorageSpec(
dataVolumeName, "100Mi", fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs))
dv.Spec.Storage.StorageClassName = ptr.To[string](scName)
return dv
}
newDataVolumeWithPvcSpec := func(scName string) *cdiv1.DataVolume {
dv := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "100Mi", fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs))
dv.Spec.PVC.StorageClassName = ptr.To[string](scName)
return dv
}
DescribeTable("import DV with AccessModes, PVC is pending until", func(scName string, scFunc func(string), dvFunc func(string) *cdiv1.DataVolume) {
if utils.IsDefaultSCNoProvisioner() {
Skip("Default storage class has no provisioner. The new storage class won't work")
}
By(fmt.Sprintf("verifying no storage class %s", testScName))
_, err := f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), scName, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dvFunc(scName))
Expect(err).ToNot(HaveOccurred())
By("verifying event occurred")
f.ExpectEvent(dataVolume.Namespace).Should(And(ContainSubstring("Pending"), ContainSubstring("PVC test-dv Pending")))
By("verifying conditions")
boundCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeBound,
Status: v1.ConditionFalse,
Message: "PVC test-dv Pending",
Reason: "Pending",
}
readyCondition := &cdiv1.DataVolumeCondition{
Type: cdiv1.DataVolumeReady,
Status: v1.ConditionFalse,
}
utils.WaitForConditions(f, dataVolume.Name, f.Namespace.Name, timeout, pollingInterval, boundCondition, readyCondition)
By("verifying pvc created")
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
scFunc(scName)
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By("waiting for pvc bound phase")
err = utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, dataVolume.Namespace, v1.ClaimBound, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("waiting for dv succeeded")
err = utils.WaitForDataVolumePhase(f, dataVolume.Namespace, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
},
Entry("[test_id:9926]the storage class is created (PvcSpec)", testScName, createStorageClass, newDataVolumeWithPvcSpec),
Entry("[test_id:9927]PV with the SC name is created (PvcSpec)", testScName, createPV, newDataVolumeWithPvcSpec),
Entry("[test_id:9928]PV with the SC name (\"\" blank) is created (PvcSpec)", "", createPV, newDataVolumeWithPvcSpec),
Entry("[test_id:9929]the storage class is created (StorageSpec)", testScName, createStorageClass, newDataVolumeWithStorageSpec),
Entry("[test_id:9930]PV with the SC name is created (StorageSpec)", testScName, createPV, newDataVolumeWithStorageSpec),
Entry("[test_id:9931]PV with the SC name (\"\" blank) is created (StorageSpec)", "", createPV, newDataVolumeWithStorageSpec),
)
})
Describe("Progress reporting on import datavolume", func() {
DescribeTable("Should report progress while importing", func(url string) {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", fmt.Sprintf(url, f.CdiInstallNs))
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
//Due to the rate limit, this will take a while, so we can expect the phase to be in progress.
By(fmt.Sprintf("waiting for datavolume to match phase %s", string(cdiv1.ImportInProgress)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.ImportInProgress, dataVolume.Name)
if err != nil {
fmt.Fprintf(GinkgoWriter, "Failed to wait for DataVolume phase: %v", err)
dv, dverr := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
if dverr != nil {
Fail(fmt.Sprintf("datavolume %s phase %s", dv.Name, dv.Status.Phase))
}
}
Expect(err).ToNot(HaveOccurred())
Eventually(func(g Gomega) string {
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: progress: %s\n", dv.Status.Progress)
return string(dv.Status.Progress)
}, timeout, pollingInterval).Should(MatchRegexp(`^([1-9]{1,2})(\.\d+)?%$`), "DataVolume is not reporting import progress")
},
Entry("[test_id:3934]when image is qcow2", utils.TinyCoreQcow2URLRateLimit),
Entry("[test_id:6902]when image is qcow2.gz", utils.TinyCoreQcow2GzURLRateLimit),
)
})
Describe("[rfe_id:4223][crit:high] DataVolume - WaitForFirstConsumer", Serial, func() {
createBlankRawDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForBlankRawImage(dataVolumeName, size)
}
createHTTPSDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
cm, err := utils.CopyFileHostCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.HTTP.CertConfigMap = cm
return dataVolume
}
createUploadDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForUpload(dataVolumeName, size)
}
createCloneDataVolume := func(dataVolumeName, size, command string) *cdiv1.DataVolume {
sourcePodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
pvcDef := utils.NewPVCDefinition(pvcName, size, nil, nil)
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, command)
By(fmt.Sprintf("creating a new target PVC (datavolume) to clone %s", sourcePvc.Name))
return utils.NewCloningDataVolume(dataVolumeName, size, sourcePvc)
}
var original *bool
noSuchFileFileURL := utils.InvalidQcowImagesURL + "no-such-file.img"
BeforeEach(func() {
previousValue, err := utils.DisableFeatureGate(f.CrClient, featuregates.HonorWaitForFirstConsumer)
Expect(err).ToNot(HaveOccurred())
original = previousValue
})
AfterEach(func() {
if original != nil && *original {
// restore
_, err := utils.EnableFeatureGate(f.CrClient, featuregates.HonorWaitForFirstConsumer)
Expect(err).ToNot(HaveOccurred())
}
})
DescribeTable("Feature Gate - disabled", func(
dvName string,
url func() string,
dvFunc func(string, string, string) *cdiv1.DataVolume,
phase cdiv1.DataVolumePhase) {
if !utils.IsHostpathProvisioner() {
Skip("Not HPP")
}
size := "1Gi"
By("Verify No FeatureGates")
config, err := f.CdiClient.CdiV1beta1().CDIConfigs().Get(context.TODO(), common.ConfigName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
Expect(config.Spec.FeatureGates).To(BeNil())
dataVolume := dvFunc(dvName, size, url())
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
// verify PVC was created
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
expectedPVCPhase := v1.ClaimBound
if phase != cdiv1.Succeeded && pvc.Spec.DataSourceRef != nil {
expectedPVCPhase = v1.ClaimPending
}
By(fmt.Sprintf("waiting for pvc to match phase %s", string(expectedPVCPhase)))
err = utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, pvc.Namespace, expectedPVCPhase, pvc.Name)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("waiting for datavolume to match phase %s", string(phase)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, phase, dataVolume.Name)
if err != nil {
dv, dverr := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
if dverr != nil {
Fail(fmt.Sprintf("datavolume %s phase %s", dv.Name, dv.Status.Phase))
}
}
Expect(err).ToNot(HaveOccurred())
By("Cleaning up")
utils.CleanupDvPvc(f.K8sClient, f.CdiClient, f.Namespace.Name, dataVolume.Name)
Eventually(func() bool {
_, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
return k8serrors.IsNotFound(err)
}, timeout, pollingInterval).Should(BeTrue())
},
Entry("[test_id:4459] Import Positive flow",
"dv-wffc-http-import",
func() string { return fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs) },
utils.NewDataVolumeWithHTTPImport,
cdiv1.Succeeded),
Entry("[test_id:4460] Import invalid url",
"dv-wffc-http-url-not-valid-import",
func() string { return fmt.Sprintf(noSuchFileFileURL, f.CdiInstallNs) },
utils.NewDataVolumeWithHTTPImport,
cdiv1.ImportInProgress),
Entry("[test_id:4461] Import qcow2 scratch space",
"dv-wffc-qcow2-import",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createHTTPSDataVolume,
cdiv1.Succeeded),
Entry("[test_id:4462] Import blank image",
"dv-wffc-blank-import",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createBlankRawDataVolume,
cdiv1.Succeeded),
Entry("[test_id:4463] Upload - positive flow",
"dv-wffc-upload",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createUploadDataVolume,
cdiv1.UploadReady),
Entry("[test_id:4464] Clone - positive flow",
"dv-wffc-clone",
func() string { return fillCommand }, // its not URL, but command, but the parameter lines up.
createCloneDataVolume,
cdiv1.Succeeded),
)
})
Describe("[crit:high] DataVolume - WaitForFirstConsumer", func() {
createBlankRawDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForBlankRawImage(dataVolumeName, size)
}
createHTTPSDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, size, url)
cm, err := utils.CopyFileHostCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dataVolume.Spec.Source.HTTP.CertConfigMap = cm
return dataVolume
}
createUploadDataVolume := func(dataVolumeName, size, url string) *cdiv1.DataVolume {
return utils.NewDataVolumeForUpload(dataVolumeName, size)
}
createCloneDataVolume := func(dataVolumeName, size, command string) *cdiv1.DataVolume {
sourcePodFillerName := fmt.Sprintf("%s-filler-pod", dataVolumeName)
pvcDef := utils.NewPVCDefinition(pvcName, size, nil, nil)
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, command)
By(fmt.Sprintf("creating a new target PVC (datavolume) to clone %s", sourcePvc.Name))
return utils.NewCloningDataVolume(dataVolumeName, size, sourcePvc)
}
DescribeTable("WFFC Feature Gate enabled - ImmediateBinding requested", func(
dvName string,
url func() string,
dvFunc func(string, string, string) *cdiv1.DataVolume,
phase cdiv1.DataVolumePhase) {
if !utils.IsHostpathProvisioner() {
Skip("Not HPP")
}
size := "1Gi"
dataVolume := dvFunc(dvName, size, url())
dataVolume.Annotations[controller.AnnImmediateBinding] = "true"
By(fmt.Sprintf("creating new datavolume %s", dataVolume.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
// verify PVC was created
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
expectedPVCPhase := v1.ClaimBound
if phase != cdiv1.Succeeded && pvc.Spec.DataSourceRef != nil {
expectedPVCPhase = v1.ClaimPending
}
By(fmt.Sprintf("waiting for pvc to match phase %s", string(expectedPVCPhase)))
err = utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, pvc.Namespace, expectedPVCPhase, pvc.Name)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("waiting for datavolume to match phase %s", string(phase)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, phase, dataVolume.Name)
if err != nil {
dv, dverr := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
if dverr != nil {
Fail(fmt.Sprintf("datavolume %s phase %s", dv.Name, dv.Status.Phase))
}
}
Expect(err).ToNot(HaveOccurred())
By("Cleaning up")
utils.CleanupDvPvc(f.K8sClient, f.CdiClient, f.Namespace.Name, dataVolume.Name)
Eventually(func() bool {
_, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
return k8serrors.IsNotFound(err)
}, timeout, pollingInterval).Should(BeTrue())
},
Entry("Import qcow2 scratch space",
"dv-immediate-wffc-qcow2-import",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createHTTPSDataVolume,
cdiv1.Succeeded),
Entry("Import blank image",
"dv-immediate-wffc-blank-import",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createBlankRawDataVolume,
cdiv1.Succeeded),
Entry("Upload - positive flow",
"dv-immediate-wffc-upload",
func() string { return fmt.Sprintf(utils.HTTPSTinyCoreQcow2URL, f.CdiInstallNs) },
createUploadDataVolume,
cdiv1.UploadReady),
Entry("Clone - positive flow",
"dv-immediate-wffc-clone",
func() string { return fillCommand }, // its not URL, but command, but the parameter lines up.
createCloneDataVolume,
cdiv1.Succeeded),
)
})
Describe("[rfe_id:1115][crit:high][vendor:cnv-qe@redhat.com][level:component][test] CDI Import from HTTP/S3", Serial, func() {
const (
originalImageName = "cirros-qcow2.img"
testImageName = "cirros-qcow2-1990.img"
)
var (
dataVolume *cdiv1.DataVolume
err error
tinyCoreIsoRateLimitURL = func() string { return "http://cdi-file-host." + f.CdiInstallNs + ":82/cirros-qcow2-1990.img" }
)
BeforeEach(func() {
By("Prepare the file")
fileHostPod, err := utils.FindPodByPrefix(f.K8sClient, f.CdiInstallNs, utils.FileHostName, "name="+utils.FileHostName)
Expect(err).ToNot(HaveOccurred())
_, _, err = f.ExecCommandInContainerWithFullOutput(fileHostPod.Namespace, fileHostPod.Name, "http",
"/bin/sh",
"-c",
"cp /tmp/shared/images/"+originalImageName+" /tmp/shared/images/"+testImageName)
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
By("Delete DV")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Cleanup the file")
fileHostPod, err := utils.FindPodByPrefix(f.K8sClient, f.CdiInstallNs, utils.FileHostName, "name="+utils.FileHostName)
Expect(err).ToNot(HaveOccurred())
_, _, err = f.ExecCommandInContainerWithFullOutput(fileHostPod.Namespace, fileHostPod.Name, "http",
"/bin/sh",
"-c",
"rm -f /tmp/shared/images/"+testImageName)
Expect(err).ToNot(HaveOccurred())
By("Verifying pvc was deleted")
deleted, err := utils.WaitPVCDeleted(f.K8sClient, dataVolume.Name, dataVolume.Namespace, timeout)
Expect(deleted).To(BeTrue())
Expect(err).ToNot(HaveOccurred())
})
It("[test_id:1990] CDI Data Volume - file is removed from http server while import is in progress", func() {
dvName := "import-file-removed"
By(fmt.Sprintf("Creating new datavolume %s", dvName))
dv := utils.NewDataVolumeWithHTTPImport(dvName, "500Mi", tinyCoreIsoRateLimitURL())
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
phase := cdiv1.ImportInProgress
By(fmt.Sprintf("Waiting for datavolume to match phase %s", string(phase)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, phase, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
// here we want to have more than 0, to be sure it started
Eventually(func(g Gomega) string {
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
g.Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: progress: %s\n", dv.Status.Progress)
return string(dv.Status.Progress)
}, timeout, pollingInterval).Should(MatchRegexp(`^([1-9]{1,2})(\.\d+)?%$`), "DataVolume is not reporting import progress")
By("Remove source image file & kill http container to force restart")
fileHostPod, err := utils.FindPodByPrefix(f.K8sClient, f.CdiInstallNs, utils.FileHostName, "name="+utils.FileHostName)
Expect(err).ToNot(HaveOccurred())
_, _, err = f.ExecCommandInContainerWithFullOutput(fileHostPod.Namespace, fileHostPod.Name, "http",
"/bin/sh",
"-c",
"rm /tmp/shared/images/"+testImageName)
Expect(err).ToNot(HaveOccurred())
By("Restore the file, import should progress")
Expect(utils.WaitTimeoutForPodReady(f.K8sClient, fileHostPod.Name, fileHostPod.Namespace, utils.PodWaitForTime)).To(Succeed())
_, _, err = f.ExecCommandInContainerWithFullOutput(fileHostPod.Namespace, fileHostPod.Name, "http",
"/bin/sh",
"-c",
"cp /tmp/shared/images/"+originalImageName+" /tmp/shared/images/"+testImageName)
Expect(err).ToNot(HaveOccurred())
By("Wait for the eventual success")
err = utils.WaitForDataVolumePhaseWithTimeout(f, f.Namespace.Name, cdiv1.Succeeded, dataVolume.Name, 300*time.Second)
Expect(err).ToNot(HaveOccurred())
By("Verify content")
pvc, err := utils.FindPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
md5, err := f.GetMD5(f.Namespace, pvc, utils.DefaultImagePath, utils.MD5PrefixSize)
Expect(err).ToNot(HaveOccurred())
Expect(md5).To(Equal(utils.ImageioMD5))
err = utils.DeleteVerifierPod(f.K8sClient, f.Namespace.Name)
Expect(err).ToNot(HaveOccurred())
})
})
Describe("Delete PVC during registry import", func() {
var dataVolume *cdiv1.DataVolume
AfterEach(func() {
if dataVolume != nil {
By("[AfterEach] Clean up DV")
err := utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
dataVolume = nil
}
})
It("[test_id:4962]Should create a new PVC when PVC is deleted during import", func() {
dataVolumeSpec := createProxyRegistryImportDataVolume(dataVolumeName, "1Gi", tinyCoreIsoRegistryProxyURL())
By(fmt.Sprintf("Creating new datavolume %s", dataVolumeSpec.Name))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolumeSpec)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By("Waiting for DV's PVC")
pvc, err := utils.WaitForPVC(f.K8sClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
pvcUID := pvc.GetUID()
By("Wait for import to start")
Expect(utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.ImportInProgress, dataVolume.Name)).To(Succeed())
By(fmt.Sprintf("Deleting PVC %v (id: %v)", pvc.Name, pvcUID))
err = utils.DeletePVC(f.K8sClient, f.Namespace.Name, pvc.Name)
Expect(err).ToNot(HaveOccurred())
deleted, err := f.WaitPVCDeletedByUID(pvc, 30*time.Second)
Expect(err).ToNot(HaveOccurred())
Expect(deleted).To(BeTrue())
By("Wait for PVC to be recreated")
pvc, err = utils.WaitForPVC(f.K8sClient, f.Namespace.Name, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("Recreated PVC %v (id: %v)", pvc.Name, pvc.GetUID()))
Expect(pvc.GetUID()).ToNot(Equal(pvcUID))
f.ForceBindIfWaitForFirstConsumer(pvc)
By("Wait for DV to succeed")
err = utils.WaitForDataVolumePhaseWithTimeout(f, f.Namespace.Name, cdiv1.Succeeded, dataVolume.Name, 10*time.Minute)
Expect(err).ToNot(HaveOccurred())
})
})
Describe("Registry import with missing configmap", func() {
cmName := "cert-registry-cm"
It("[test_id:4963]Import POD should remain pending until CM exists", func() {
var pvc *v1.PersistentVolumeClaim
dataVolumeDef := utils.NewDataVolumeWithRegistryImport("missing-cm-registry-dv", "1Gi", tinyCoreIsoRegistryURL())
dataVolumeDef.Spec.Source.Registry.CertConfigMap = &cmName
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolumeDef)
Expect(err).ToNot(HaveOccurred())
f.ForceBindPvcIfDvIsWaitForFirstConsumer(dataVolume)
By("verifying pvc was created")
Eventually(func() bool {
// TODO: fix this to use the mechanism to find the correct PVC once we decouple the DV and PVC names
pvc, _ = f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
return pvc != nil && pvc.Name != ""
}, timeout, pollingInterval).Should(BeTrue())
By("Verifying the POD remains pending for 30 seconds")
podName := naming.GetResourceName(common.ImporterPodName, pvc.Name)
Consistently(func() bool {
pod, err := f.K8sClient.CoreV1().Pods(f.Namespace.Name).Get(context.TODO(), podName, metav1.GetOptions{})
if err == nil {
// Found the pod
Expect(pod.Status.Phase).To(Equal(v1.PodPending))
if len(pod.Status.ContainerStatuses) == 1 && pod.Status.ContainerStatuses[0].State.Waiting != nil {
Expect(pod.Status.ContainerStatuses[0].State.Waiting.Reason).To(Equal("ContainerCreating"))
}
fmt.Fprintf(GinkgoWriter, "INFO: pod found, pending, container creating: %s\n", podName)
} else if k8serrors.IsNotFound(err) {
fmt.Fprintf(GinkgoWriter, "INFO: pod not found: %s\n", podName)
} else {
Expect(err).ToNot(HaveOccurred())
}
return true
}, time.Second*30, time.Second).Should(BeTrue())
By("Creating the config map")
_, err = utils.CopyRegistryCertConfigMapDestName(f.K8sClient, f.Namespace.Name, f.CdiInstallNs, cmName)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("waiting for datavolume to match phase %s", string(cdiv1.Succeeded)))
err = utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
})
})
Describe("Priority class on datavolume should transfer to pods", func() {
verifyPodAnnotations := func(pod *v1.Pod) {
By("verifying priority class")
Expect(pod.Spec.PriorityClassName).To(Equal("system-cluster-critical"))
}
It("Importer pod should have priority class specified on datavolume", func() {
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "1Gi", fmt.Sprintf(utils.TinyCoreQcow2URLRateLimit, f.CdiInstallNs))
By(fmt.Sprintf("creating new datavolume %s with priority class", dataVolume.Name))
dataVolume.Spec.PriorityClassName = "system-cluster-critical"
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("verifying the Datavolume is not complete yet")
foundDv, err := f.CdiClient.CdiV1beta1().DataVolumes(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
if foundDv.Status.Phase != cdiv1.Succeeded {
By("find importer pod")
var sourcePod *v1.Pod
Eventually(func() bool {
sourcePod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.ImporterPodName, common.CDILabelSelector)
return err == nil
}, timeout, pollingInterval).Should(BeTrue())
verifyPodAnnotations(sourcePod)
}
})
It("Uploader pod should have priority class specified on datavolume", func() {
dataVolume := utils.NewDataVolumeForUpload(dataVolumeName, "1Gi")
By(fmt.Sprintf("creating new datavolume %s with priority class\"", dataVolume.Name))
dataVolume.Spec.PriorityClassName = "system-cluster-critical"
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("verifying the Datavolume is not complete yet")
foundDv, err := f.CdiClient.CdiV1beta1().DataVolumes(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
if foundDv.Status.Phase != cdiv1.Succeeded {
By("find uploader pod")
var sourcePod *v1.Pod
Eventually(func() bool {
sourcePod, err = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.UploadPodName, common.CDILabelSelector)
return err == nil
}, timeout, pollingInterval).Should(BeTrue())
verifyPodAnnotations(sourcePod)
}
})
It("Cloner pod should have priority class specified on datavolume", func() {
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(context.TODO(), f.SnapshotSCName, metav1.GetOptions{})
if err == nil {
value, ok := sc.Annotations["storageclass.kubernetes.io/is-default-class"]
if smartApplicable && ok && strings.Compare(value, "true") == 0 {
Skip("Cannot test if annotations are present when all pvcs are smart clone capable.")
}
}
sourceDv := utils.NewDataVolumeWithHTTPImport("source-dv", "1Gi", tinyCoreQcow2URL())
Expect(sourceDv).ToNot(BeNil())
By(fmt.Sprintf("creating new source dv %s with priority class", sourceDv.Name))
sourceDv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err := utils.WaitForPVC(f.K8sClient, sourceDv.Namespace, sourceDv.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
dataVolume := utils.NewCloningDataVolume(dataVolumeName, "1Gi", pvc)
Expect(dataVolume).ToNot(BeNil())
By(fmt.Sprintf("creating new datavolume %s with priority class", dataVolume.Name))
dataVolume.Spec.PriorityClassName = "system-cluster-critical"
dataVolume.Annotations[controller.AnnPodRetainAfterCompletion] = "true"
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying pvc was created")
pvc, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("verifying the Datavolume is not complete yet")
foundDv, err := f.CdiClient.CdiV1beta1().DataVolumes(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
if foundDv.Status.Phase != cdiv1.Succeeded {
By("find source and target pod")
var sourcePod *v1.Pod
var uploadPod *v1.Pod
Eventually(func() bool {
if sourcePod == nil {
sourcePod, _ = utils.FindPodBySuffix(f.K8sClient, dataVolume.Namespace, "source-pod", common.CDILabelSelector)
}
if uploadPod == nil {
uploadPod, _ = utils.FindPodByPrefix(f.K8sClient, dataVolume.Namespace, common.UploadPodName, common.CDILabelSelector)
}
return sourcePod != nil && uploadPod != nil
}, timeout, pollingInterval).Should(BeTrue())
verifyPodAnnotations(sourcePod)
verifyPodAnnotations(uploadPod)
}
})
})
Describe("Default instance type labels", func() {
var (
sourceDataVolume *cdiv1.DataVolume
err error
)
BeforeEach(func() {
By("creating a labelled DataVolume and PVC")
sourceDataVolume = utils.NewDataVolumeForBlankRawImage("", "1Gi")
sourceDataVolume.GenerateName = "source-datavolume"
sourceDataVolume.Labels = make(map[string]string)
for _, defaultInstancetypeLabel := range controller.DefaultInstanceTypeLabels {
sourceDataVolume.Labels[defaultInstancetypeLabel] = "defined"
}
sourceDataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDataVolume)
Expect(err).ToNot(HaveOccurred())
By("verifying PVC was created")
_, err = utils.WaitForPVC(f.K8sClient, sourceDataVolume.Namespace, sourceDataVolume.Name)
Expect(err).ToNot(HaveOccurred())
})
DescribeTable("should be passed to DataVolume from source", func(createDataVolume func() *cdiv1.DataVolume) {
dv := createDataVolume()
By("asserting that all default instance type labels have been passed to the DataVolume")
Eventually(func() bool {
dv, err = f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), dv.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
for _, defaultInstancetypeLabel := range controller.DefaultInstanceTypeLabels {
if _, ok := dv.Labels[defaultInstancetypeLabel]; !ok {
return false
}
}
return true
}, 60*time.Second, 1*time.Second).Should(BeTrue())
},
Entry("PVC", func() *cdiv1.DataVolume {
By("creating a DataVolume pointing to a labelled PVC")
dv := utils.NewDataVolumeForImageCloning("datavolume-from-pvc", "1Gi", sourceDataVolume.Namespace, sourceDataVolume.Name, nil, nil)
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
return dv
}),
Entry("Snapshot", func() *cdiv1.DataVolume {
if !f.IsSnapshotStorageClassAvailable() {
Skip("Clone from volumesnapshot does not work without snapshot capable storage")
}
By("creating a labelled VolumeSnapshot")
snapClass := f.GetSnapshotClass()
snapshot := utils.NewVolumeSnapshot(sourceDataVolume.Name, sourceDataVolume.Namespace, sourceDataVolume.Name, &snapClass.Name)
snapshot.Labels = make(map[string]string)
for _, defaultInstancetypeLabel := range controller.DefaultInstanceTypeLabels {
snapshot.Labels[defaultInstancetypeLabel] = "defined"
}
err = f.CrClient.Create(context.TODO(), snapshot)
Expect(err).ToNot(HaveOccurred())
By("creating a DataVolume pointing to a labelled VolumeSnapshot")
dv := utils.NewDataVolumeForSnapshotCloning("datavolume-from-snapshot", "1Gi", sourceDataVolume.Namespace, sourceDataVolume.Name, nil, nil)
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
return dv
}),
Entry("Registry", func() *cdiv1.DataVolume {
By("creating a DataVolume pointing to a Containerdisk")
dv := utils.NewDataVolumeWithRegistryImport("datavolume-from-registry", "1Gi", tinyCoreIsoRegistryURL())
cm, err := utils.CopyRegistryCertConfigMap(f.K8sClient, f.Namespace.Name, f.CdiInstallNs)
Expect(err).ToNot(HaveOccurred())
dv.Spec.Source.Registry.CertConfigMap = &cm
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
By("verifying PVC was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dv.Namespace, dv.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("Wait for DV to succeed")
err = utils.WaitForDataVolumePhaseWithTimeout(f, dv.Namespace, cdiv1.Succeeded, dv.Name, 10*time.Minute)
Expect(err).ToNot(HaveOccurred())
return dv
}),
Entry("DataSource", func() *cdiv1.DataVolume {
By("createing a labelled DataSource")
ds := utils.NewPvcDataSource("datasource-from-pvc", f.Namespace.Name, sourceDataVolume.Name, sourceDataVolume.Namespace)
ds.Labels = make(map[string]string)
for _, defaultInstancetypeLabel := range controller.DefaultInstanceTypeLabels {
ds.Labels[defaultInstancetypeLabel] = "defined"
}
ds, err := f.CdiClient.CdiV1beta1().DataSources(f.Namespace.Name).Create(context.TODO(), ds, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())
By("createing a DataVolume pointing to a labelled DataSource")
dv := utils.NewDataVolumeWithSourceRef("datavolume-from-datasource", "1Gi", f.Namespace.Name, ds.Name)
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
return dv
}),
)
})
DescribeTable("extra configuration options for VDDK imports", Label("VDDK"), func(tweakDataVolume func(*cdiv1.DataVolume)) {
vddkConfigOptions := []string{
"VixDiskLib.nfcAio.Session.BufSizeIn64KB=16",
"vixDiskLib.nfcAio.Session.BufCount=4",
}
vddkConfigMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "vddk-extras",
},
Data: map[string]string{
common.VddkArgsKeyName: strings.Join(vddkConfigOptions, "\n"),
},
}
_, err := f.K8sClient.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), vddkConfigMap, metav1.CreateOptions{})
if !k8serrors.IsAlreadyExists(err) {
Expect(err).ToNot(HaveOccurred())
}
vcenterURL := fmt.Sprintf(utils.VcenterURL, f.CdiInstallNs)
dataVolume := createVddkDataVolume("import-pod-vddk-config-test", "100Mi", vcenterURL)
By(fmt.Sprintf("Create new DataVolume %s", dataVolume.Name))
tweakDataVolume(dataVolume)
controller.AddAnnotation(dataVolume, controller.AnnPodRetainAfterCompletion, "true")
dataVolume, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By("Verify PVC was created")
pvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(pvc)
By("Wait for import to be completed")
err = utils.WaitForDataVolumePhase(f, dataVolume.Namespace, cdiv1.Succeeded, dataVolume.Name)
Expect(err).ToNot(HaveOccurred(), "DataVolume not in phase succeeded in time")
By("Find importer pods after completion")
pvcName := dataVolume.Name
// When using populators, the PVC Prime name is used to build the importer pod
if usePopulator, _ := dvc.CheckPVCUsingPopulators(pvc); usePopulator {
pvcName = populators.PVCPrimeName(pvc)
}
By("Find importer pod " + pvcName)
importer, err := utils.FindPodByPrefixOnce(f.K8sClient, dataVolume.Namespace, common.ImporterPodName, common.CDILabelSelector)
Expect(err).ToNot(HaveOccurred())
Expect(importer.DeletionTimestamp).To(BeNil())
Eventually(func() (string, error) {
out, err := f.K8sClient.CoreV1().
Pods(importer.Namespace).
GetLogs(importer.Name, &core.PodLogOptions{SinceTime: &meta.Time{Time: CurrentSpecReport().StartTime}}).
DoRaw(context.Background())
return string(out), err
}, time.Minute, pollingInterval).Should(And(
ContainSubstring(vddkConfigOptions[0]),
ContainSubstring(vddkConfigOptions[1]),
))
},
Entry("[test_id:XXXX]succeed importing VDDK data volume with extra arguments ConfigMap annotation set", func(dataVolume *cdiv1.DataVolume) {
controller.AddAnnotation(dataVolume, controller.AnnVddkExtraArgs, "vddk-extras")
}),
Entry("[test_id:XXXX]succeed importing VDDK data volume with extra arguments ConfigMap field set", func(dataVolume *cdiv1.DataVolume) {
dataVolume.Spec.Source.VDDK.ExtraArgs = "vddk-extras"
}),
)
})
func SetFilesystemOverhead(f *framework.Framework, globalOverhead, scOverhead string) {
defaultSCName := utils.DefaultStorageClass.GetName()
testedFilesystemOverhead := &cdiv1.FilesystemOverhead{}
if globalOverhead != "" {
testedFilesystemOverhead.Global = cdiv1.Percent(globalOverhead)
}
if scOverhead != "" {
testedFilesystemOverhead.StorageClass = map[string]cdiv1.Percent{defaultSCName: cdiv1.Percent(scOverhead)}
}
By(fmt.Sprintf("Updating CDIConfig filesystem overhead to %v", testedFilesystemOverhead))
err := utils.UpdateCDIConfig(f.CrClient, func(config *cdiv1.CDIConfigSpec) {
config.FilesystemOverhead = testedFilesystemOverhead.DeepCopy()
})
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("Waiting for filsystem overhead status to be set to %v", testedFilesystemOverhead))
Eventually(func() bool {
config, err := f.CdiClient.CdiV1beta1().CDIConfigs().Get(context.TODO(), common.ConfigName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
if scOverhead != "" {
return config.Status.FilesystemOverhead.StorageClass[defaultSCName] == cdiv1.Percent(scOverhead)
}
return config.Status.FilesystemOverhead.StorageClass[defaultSCName] == cdiv1.Percent(globalOverhead)
}, timeout, pollingInterval).Should(BeTrue(), "CDIConfig filesystem overhead wasn't set")
}
func EnableWebhookPvcRendering(c client.Client) {
By("enabling WebhookPvcRendering feature gate")
_, err := utils.EnableFeatureGate(c, featuregates.WebhookPvcRendering)
Expect(err).ToNot(HaveOccurred())
Eventually(func() error {
whc := &admissionregistrationv1.MutatingWebhookConfiguration{}
return c.Get(context.TODO(), types.NamespacedName{Name: "cdi-api-pvc-mutate"}, whc)
}, timeout, pollingInterval).ShouldNot(HaveOccurred())
}
func DisableWebhookPvcRendering(c client.Client) {
enabled, err := featuregates.IsWebhookPvcRenderingEnabled(c)
Expect(err).ToNot(HaveOccurred())
if enabled {
By("disabling WebhookPvcRendering feature gate")
_, err := utils.DisableFeatureGate(c, featuregates.WebhookPvcRendering)
Expect(err).ToNot(HaveOccurred())
}
Eventually(func() bool {
whc := &admissionregistrationv1.MutatingWebhookConfiguration{}
err := c.Get(context.TODO(), types.NamespacedName{Name: "cdi-api-pvc-mutate"}, whc)
return err != nil && k8serrors.IsNotFound(err)
}, timeout, pollingInterval).Should(BeTrue())
}