containerized-data-importer/tests/cloner_test.go
Bartosz Rybacki ab8b9c025e
Generating label names (#1200)
* Handle labels length correctly

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Handle service name generation correctly

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Remove not needed labels

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Store import pod name in annotation

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Enable long DV name

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Handle name with dot when creating service/label name

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Test long names on import,  upload and clone

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Store upload pod name in annotation

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Store importer scratch pvc name in annotation

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Quick fix for tests (need improvements)

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Cleanup handling scratch name

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Ensure pod/service name conflicts are handled

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Handle client errors when trying to get the import pod

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Style improvements, and other code review fixes.

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Store clone source pod name in an annotation

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Correct name initialization and tests

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Do not init name if pod already exists. It is not needed.

The situation of having a pod but not name on annotation can happen after the upgrade, when we have a legacy pvc and pod already existing, but clone operation not finished. But when we already have the pod, then in the code (currently) we do not need the name from annotation.

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Cleanup scratch name handling

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Use constant for max dv name in validation

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>

* Simplify clone source pod name initialization

Signed-off-by: Bartosz Rybacki <brybacki@redhat.com>
2020-05-29 19:55:32 +02:00

1010 lines
48 KiB
Go

package tests
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1alpha1"
"kubevirt.io/containerized-data-importer/pkg/common"
"kubevirt.io/containerized-data-importer/pkg/controller"
"kubevirt.io/containerized-data-importer/tests/framework"
"kubevirt.io/containerized-data-importer/tests/utils"
)
const (
namespacePrefix = "cloner"
sourcePodFillerName = "fill-source"
sourcePVCName = "source-pvc"
fillData = "123456789012345678901234567890123456789012345678901234567890"
fillDataFSMD5sum = "fabc176de7eb1b6ca90b3aa4c7e035f3"
testBaseDir = utils.DefaultPvcMountPath
testFile = "/disk.img"
fillCommand = "echo \"" + fillData + "\" >> " + testBaseDir
blockFillCommand = "dd if=/dev/urandom bs=4096 of=" + testBaseDir + " || echo this is fine"
assertionPollInterval = 2 * time.Second
cloneCompleteTimeout = 270 * time.Second
verifyPodDeletedTimeout = 270 * time.Second
controllerSkipPVCCompleteTimeout = 270 * time.Second
)
var _ = Describe("[rfe_id:1277][crit:high][vendor:cnv-qe@redhat.com][level:component]Cloner Test Suite", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
var sourcePvc *v1.PersistentVolumeClaim
var targetPvc *v1.PersistentVolumeClaim
AfterEach(func() {
if sourcePvc != nil {
By("[AfterEach] Clean up source PVC")
err := f.DeletePVC(sourcePvc)
Expect(err).ToNot(HaveOccurred())
}
if targetPvc != nil {
By("[AfterEach] Clean up target PVC")
err := f.DeletePVC(targetPvc)
Expect(err).ToNot(HaveOccurred())
}
})
It("[test_id:1354]Should clone data within same namespace", func() {
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(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 host assisted cloning for within namespace when all pvcs are smart clone capable.")
}
}
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
doFileBasedCloneTest(f, pvcDef, f.Namespace, "target-dv")
})
It("Should clone imported data within same namespace and preserve fsGroup", func() {
diskImagePath := filepath.Join(testBaseDir, testFile)
dataVolume := utils.NewDataVolumeWithHTTPImport(dataVolumeName, "500Mi", fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs))
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dataVolume)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("waiting for source datavolume to match phase %s", string(cdiv1.Succeeded)))
err = utils.WaitForDataVolumePhase(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, dataVolume.Name)
if err != nil {
dv, dverr := f.CdiClient.CdiV1alpha1().DataVolumes(f.Namespace.Name).Get(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")
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(dataVolume.Namespace).Get(dataVolume.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
sourceMD5, err := f.GetMD5(f.Namespace, pvc, diskImagePath, 0)
Expect(err).ToNot(HaveOccurred())
// Create targetPvc in new NS.
targetDV := utils.NewCloningDataVolume("target-dv", "1G", pvc)
targetDataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
targetPvc, err := utils.WaitForPVC(f.K8sClient, targetDataVolume.Namespace, targetDataVolume.Name)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: wait for PVC claim phase: %s\n", targetPvc.Name)
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, f.Namespace.Name, v1.ClaimBound, targetPvc.Name)
sourcePvcDiskGroup, err := f.GetDiskGroup(f.Namespace, pvc)
fmt.Fprintf(GinkgoWriter, "INFO: %s\n", sourcePvcDiskGroup)
Expect(err).ToNot(HaveOccurred())
completeClone(f, f.Namespace, targetPvc, diskImagePath, sourceMD5, sourcePvcDiskGroup)
})
It("[test_id:1355]Should clone data across different namespaces", func() {
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
doFileBasedCloneTest(f, pvcDef, targetNs, "target-dv")
})
It("[test_id:1356]Should not clone anything when CloneOf annotation exists", func() {
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
cloneOfAnnoExistenceTest(f, f.Namespace.Name)
})
It("[posneg:negative][test_id:3617]Should clone across nodes when multiple local volumes exist,", func() {
// Get nodes, need at least 2
nodeList, err := f.K8sClient.CoreV1().Nodes().List(metav1.ListOptions{})
Expect(err).ToNot(HaveOccurred())
if len(nodeList.Items) < 2 {
Skip("Need at least 2 nodes to copy accross nodes")
}
nodeMap := make(map[string]bool)
for _, node := range nodeList.Items {
if ok, _ := nodeMap[node.Name]; !ok {
nodeMap[node.Name] = true
}
}
// Find PVs and identify local storage, the PVs should already exist.
pvList, err := f.K8sClient.CoreV1().PersistentVolumes().List(metav1.ListOptions{})
Expect(err).ToNot(HaveOccurred())
var sourcePV, targetPV *v1.PersistentVolume
var storageClassName string
// Verify we have PVs to at least 2 nodes.
pvNodeNames := make(map[string]int)
for _, pv := range pvList.Items {
if pv.Spec.NodeAffinity == nil || pv.Spec.NodeAffinity.Required == nil || len(pv.Spec.NodeAffinity.Required.NodeSelectorTerms) == 0 {
// Not a local volume PV
continue
}
pvNode := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions[0].Values[0]
if pv.Spec.ClaimRef == nil {
// PV is available and not claimed.
if val, ok := pvNodeNames[pvNode]; !ok {
pvNodeNames[pvNode] = 1
} else {
pvNodeNames[pvNode] = val + 1
}
}
}
if len(pvNodeNames) < 2 {
Skip("Need PVs on at least 2 nodes to test")
}
// Find the source and target PVs so we can label them.
for _, pv := range pvList.Items {
if pv.Spec.NodeAffinity == nil || pv.Spec.NodeAffinity.Required == nil ||
len(pv.Spec.NodeAffinity.Required.NodeSelectorTerms) == 0 || pv.Status.Phase != v1.VolumeAvailable {
// Not an available local volume PV
continue
}
if sourcePV == nil {
if pv.Spec.StorageClassName != "" {
storageClassName = pv.Spec.StorageClassName
}
pvNode := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions[0].Values[0]
if ok, val := nodeMap[pvNode]; ok && val {
nodeMap[pvNode] = false
By("Labeling PV " + pv.Name + " as source")
sourcePV = &pv
if sourcePV.GetLabels() == nil {
sourcePV.SetLabels(make(map[string]string))
}
sourcePV.GetLabels()["source-pv"] = "yes"
sourcePV, err = f.K8sClient.CoreV1().PersistentVolumes().Update(sourcePV)
Expect(err).ToNot(HaveOccurred())
}
} else if targetPV == nil {
pvNode := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions[0].Values[0]
if ok, val := nodeMap[pvNode]; ok && val {
nodeMap[pvNode] = false
By("Labeling PV " + pv.Name + " as target")
targetPV = &pv
if targetPV.GetLabels() == nil {
targetPV.SetLabels(make(map[string]string))
}
targetPV.GetLabels()["target-pv"] = "yes"
targetPV, err = f.K8sClient.CoreV1().PersistentVolumes().Update(targetPV)
Expect(err).ToNot(HaveOccurred())
break
}
}
}
Expect(sourcePV).ToNot(BeNil())
Expect(targetPV).ToNot(BeNil())
// Source and target PVs have been annotated, now create PVCs with label selectors.
sourceSelector := make(map[string]string)
sourceSelector["source-pv"] = "yes"
sourcePVCDef := utils.NewPVCDefinitionWithSelector(sourcePVCName, "1G", storageClassName, sourceSelector, nil, nil)
sourcePVCDef.Namespace = f.Namespace.Name
sourcePVC := f.CreateAndPopulateSourcePVC(sourcePVCDef, sourcePodFillerName, fillCommand+testFile)
sourcePVC, err = f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Get(sourcePVC.Name, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
Expect(sourcePVC.Spec.VolumeName).To(Equal(sourcePV.Name))
targetDV := utils.NewCloningDataVolume("target-dv", "1G", sourcePVCDef)
targetDV.Spec.PVC.StorageClassName = &storageClassName
targetLabelSelector := metav1.LabelSelector{
MatchLabels: map[string]string{
"target-pv": "yes",
},
}
targetDV.Spec.PVC.Selector = &targetLabelSelector
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool {
targetPvc, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
return targetPvc.Spec.VolumeName == targetPV.Name
}, 60, 1).Should(BeTrue())
fmt.Fprintf(GinkgoWriter, "INFO: wait for PVC claim phase: %s\n", targetPvc.Name)
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, f.Namespace.Name, v1.ClaimBound, targetPvc.Name)
sourcePvcDiskGroup, err := f.GetDiskGroup(f.Namespace, sourcePVC)
Expect(err).ToNot(HaveOccurred())
completeClone(f, f.Namespace, targetPvc, filepath.Join(testBaseDir, testFile), fillDataFSMD5sum, sourcePvcDiskGroup)
})
})
var _ = Describe("Validate creating multiple clones of same source Data Volume", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
tinyCoreIsoURL := fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs)
var (
sourceDv, targetDv1, targetDv2, targetDv3 *cdiv1.DataVolume
err error
)
AfterEach(func() {
dvs := []*cdiv1.DataVolume{sourceDv, targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
cleanDv(f, dv)
}
})
It("[rfe_id:1277][test_id:1891][crit:High][vendor:cnv-qe@redhat.com][level:component]Should allow multiple clones from a single source datavolume", func() {
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImport("source-dv", "200Mi", tinyCoreIsoURL)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum /pvc/disk.img")
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
By("Cloning from the source DataVolume to target1")
targetDv1 = utils.NewDataVolumeForImageCloning("target-dv1", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
By("Cloning from the source DataVolume to target2")
targetDv2 = utils.NewDataVolumeForImageCloning("target-dv2", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
By("Cloning from the target1 DataVolume to target3")
targetDv3 = utils.NewDataVolumeForImageCloning("target-dv3", "200Mi", f.Namespace.Name, targetDv1.Name, targetDv1.Spec.PVC.StorageClassName, targetDv1.Spec.PVC.VolumeMode)
dvs := []*cdiv1.DataVolume{targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, dv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
matchFile := filepath.Join(testBaseDir, "disk.img")
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(dv), matchFile, md5sum[:32])).To(BeTrue())
_, err = utils.WaitPodDeleted(f.K8sClient, "verify-pvc-md5", f.Namespace.Name, verifyPodDeletedTimeout)
Expect(err).ToNot(HaveOccurred())
}
})
It("[rfe_id:1277][test_id:1891][crit:High][vendor:cnv-qe@redhat.com][level:component]Should allow multiple clones from a single source datavolume in block volume mode", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImportToBlockPV("source-dv", "200Mi", tinyCoreIsoURL, f.BlockSCName)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum "+testBaseDir)
retry := 0
for err != nil && retry < 10 {
retry++
md5sum, err = f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum "+testBaseDir)
}
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
By("Cloning from the source DataVolume to target1")
targetDv1 = utils.NewDataVolumeForImageCloning("target-dv1", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
By("Cloning from the source DataVolume to target2")
targetDv2 = utils.NewDataVolumeForImageCloning("target-dv2", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
By("Cloning from the target1 DataVolume to target3")
targetDv3 = utils.NewDataVolumeForImageCloning("target-dv3", "200Mi", f.Namespace.Name, targetDv1.Name, targetDv1.Spec.PVC.StorageClassName, targetDv1.Spec.PVC.VolumeMode)
dvs := []*cdiv1.DataVolume{targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, dv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(dv), testBaseDir, md5sum[:32])).To(BeTrue())
_, err = utils.WaitPodDeleted(f.K8sClient, "verify-pvc-md5", f.Namespace.Name, verifyPodDeletedTimeout)
Expect(err).ToNot(HaveOccurred())
}
})
})
var _ = Describe("Validate Data Volume clone to smaller size", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
tinyCoreIsoURL := fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs)
var (
sourceDv, targetDv *cdiv1.DataVolume
err error
)
AfterEach(func() {
if sourceDv != nil {
By("Cleaning up source DV")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, sourceDv.Name)
Expect(err).ToNot(HaveOccurred())
}
if targetDv != nil {
By("Cleaning up target DV")
err = utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, targetDv.Name)
Expect(err).ToNot(HaveOccurred())
}
})
It("[rfe_id:1126][test_id:1896][crit:High][vendor:cnv-qe@redhat.com][level:component] Should not allow cloning into a smaller sized data volume", func() {
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImport("source-dv", "200Mi", tinyCoreIsoURL)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum /pvc/disk.img")
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
By("Cloning from the source DataVolume to under sized target")
targetDv = utils.NewDataVolumeForImageCloning("target-dv", "50Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv)
Expect(err).To(HaveOccurred())
Expect(strings.Contains(err.Error(), "target resources requests storage size is smaller than the source")).To(BeTrue())
By("Cloning from the source DataVolume to properly sized target")
targetDv = utils.NewDataVolumeForImageCloning("target-dv", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, targetDv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
matchFile := filepath.Join(testBaseDir, "disk.img")
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(targetDv), matchFile, md5sum[:32])).To(BeTrue())
By("Verifying the image is sparse")
Expect(f.VerifySparse(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(targetDv))).To(BeTrue())
})
It("[rfe_id:1126][test_id:1896][crit:High][vendor:cnv-qe@redhat.com][level:component] Should not allow cloning into a smaller sized data volume in block volume mode", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImportToBlockPV("source-dv", "200Mi", tinyCoreIsoURL, f.BlockSCName)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum "+testBaseDir)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
By("Cloning from the source DataVolume to under sized target")
targetDv = utils.NewDataVolumeForImageCloning("target-dv", "50Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv)
Expect(err).To(HaveOccurred())
Expect(strings.Contains(err.Error(), "target resources requests storage size is smaller than the source")).To(BeTrue())
By("Cloning from the source DataVolume to properly sized target")
targetDv = utils.NewDataVolumeForImageCloning("target-dv", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, targetDv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(targetDv), testBaseDir, md5sum[:32])).To(BeTrue())
})
})
var _ = Describe("Validate Data Volume should clone multiple clones in parallel", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
tinyCoreIsoURL := fmt.Sprintf(utils.TinyCoreIsoURL, f.CdiInstallNs)
var (
sourceDv, targetDv1, targetDv2, targetDv3 *cdiv1.DataVolume
err error
)
AfterEach(func() {
dvs := []*cdiv1.DataVolume{sourceDv, targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
cleanDv(f, dv)
}
})
It("[rfe_id:1277][test_id:1899][crit:High][vendor:cnv-qe@redhat.com][level:component] Should allow multiple cloning operations in parallel", func() {
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImport("source-dv", "200Mi", tinyCoreIsoURL)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum /pvc/disk.img")
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
// By not waiting for completion, we will start 3 transfers in parallell
By("Cloning from the source DataVolume to target1")
targetDv1 = utils.NewDataVolumeForImageCloning("target-dv1", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv1)
Expect(err).ToNot(HaveOccurred())
By("Cloning from the source DataVolume to target2 in parallel")
targetDv2 = utils.NewDataVolumeForImageCloning("target-dv2", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv2)
Expect(err).ToNot(HaveOccurred())
By("Cloning from the source DataVolume to target3 in parallel")
targetDv3 = utils.NewDataVolumeForImageCloning("target-dv3", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv3)
Expect(err).ToNot(HaveOccurred())
dvs := []*cdiv1.DataVolume{targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, dv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
matchFile := filepath.Join(testBaseDir, "disk.img")
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(dv), matchFile, md5sum[:32])).To(BeTrue())
_, err = utils.WaitPodDeleted(f.K8sClient, "verify-pvc-md5", f.Namespace.Name, verifyPodDeletedTimeout)
Expect(err).ToNot(HaveOccurred())
}
})
It("[rfe_id:1277][test_id:1899][crit:High][vendor:cnv-qe@redhat.com][level:component] Should allow multiple cloning operations in parallel for block devices", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
By("Creating a source from a real image")
sourceDv = utils.NewDataVolumeWithHTTPImportToBlockPV("source-dv", "200Mi", tinyCoreIsoURL, f.BlockSCName)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, sourceDv)
Expect(err).ToNot(HaveOccurred())
By("Waiting for import to be completed")
utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, sourceDv.Name, 3*90*time.Second)
By("Calculating the md5sum of the source data volume")
md5sum, err := f.RunCommandAndCaptureOutput(utils.PersistentVolumeClaimFromDataVolume(sourceDv), "md5sum "+testBaseDir)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: MD5SUM for source is: %s\n", md5sum[:32])
// By not waiting for completion, we will start 3 transfers in parallell
By("Cloning from the source DataVolume to target1")
targetDv1 = utils.NewDataVolumeForImageCloning("target-dv1", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv1)
Expect(err).ToNot(HaveOccurred())
By("Cloning from the source DataVolume to target2 in parallel")
targetDv2 = utils.NewDataVolumeForImageCloning("target-dv2", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv2)
Expect(err).ToNot(HaveOccurred())
By("Cloning from the source DataVolume to target3 in parallel")
targetDv3 = utils.NewDataVolumeForImageCloning("target-dv3", "200Mi", f.Namespace.Name, sourceDv.Name, sourceDv.Spec.PVC.StorageClassName, sourceDv.Spec.PVC.VolumeMode)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDv3)
Expect(err).ToNot(HaveOccurred())
dvs := []*cdiv1.DataVolume{targetDv1, targetDv2, targetDv3}
for _, dv := range dvs {
By("Waiting for clone to be completed")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, dv.Name, 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
Expect(f.VerifyTargetPVCContentMD5(f.Namespace, utils.PersistentVolumeClaimFromDataVolume(dv), testBaseDir, md5sum[:32])).To(BeTrue())
_, err = utils.WaitPodDeleted(f.K8sClient, "verify-pvc-md5", f.Namespace.Name, verifyPodDeletedTimeout)
Expect(err).ToNot(HaveOccurred())
}
})
})
var _ = Describe("Block PV Cloner Test", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
It("Should clone data across namespaces", func() {
if !f.IsBlockVolumeStorageClassAvailable() {
Skip("Storage Class for block volume is not available")
}
pvcDef := utils.NewBlockPVCDefinition(sourcePVCName, "500M", nil, nil, f.BlockSCName)
sourcePvc := f.CreateAndPopulateSourcePVC(pvcDef, "fill-source-block-pod", blockFillCommand)
sourceMD5, err := f.GetMD5(f.Namespace, sourcePvc, testBaseDir, 0)
Expect(err).ToNot(HaveOccurred())
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
targetDV := utils.NewDataVolumeCloneToBlockPV("target-dv", "500M", sourcePvc.Namespace, sourcePvc.Name, f.BlockSCName)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, targetNs.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
targetPvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: wait for PVC claim phase: %s\n", targetPvc.Name)
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, f.Namespace.Name, v1.ClaimBound, targetPvc.Name)
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, targetNs.Name, cdiv1.Succeeded, "target-dv", 3*90*time.Second)
Expect(err).ToNot(HaveOccurred())
Expect(f.VerifyTargetPVCContentMD5(targetNs, targetPvc, testBaseDir, sourceMD5)).To(BeTrue())
})
})
var _ = Describe("Namespace with quota", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
var (
orgConfig *v1.ResourceRequirements
sourcePvc *v1.PersistentVolumeClaim
targetPvc *v1.PersistentVolumeClaim
)
BeforeEach(func() {
By("Capturing original CDIConfig state")
config, err := f.CdiClient.CdiV1alpha1().CDIConfigs().Get(common.ConfigName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
orgConfig = config.Status.DefaultPodResourceRequirements
})
AfterEach(func() {
By("Restoring CDIConfig to original state")
reqCPU, _ := orgConfig.Requests.Cpu().AsInt64()
reqMem, _ := orgConfig.Requests.Memory().AsInt64()
limCPU, _ := orgConfig.Limits.Cpu().AsInt64()
limMem, _ := orgConfig.Limits.Memory().AsInt64()
err := f.UpdateCdiConfigResourceLimits(reqCPU, reqMem, limCPU, limMem)
Expect(err).ToNot(HaveOccurred())
if sourcePvc != nil {
By("[AfterEach] Clean up source PVC")
err := f.DeletePVC(sourcePvc)
Expect(err).ToNot(HaveOccurred())
}
if targetPvc != nil {
By("[AfterEach] Clean up target PVC")
err := f.DeletePVC(targetPvc)
Expect(err).ToNot(HaveOccurred())
}
})
It("Should create clone in namespace with quota", func() {
err := f.CreateQuotaInNs(int64(1), int64(1024*1024*1024), int64(2), int64(2*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(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 host assisted cloning for within namespace when all pvcs are smart clone capable.")
}
}
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
doFileBasedCloneTest(f, pvcDef, f.Namespace, "target-dv")
})
It("Should fail to clone in namespace with quota when pods have higher requirements", func() {
err := f.UpdateCdiConfigResourceLimits(int64(2), int64(1024*1024*1024), int64(2), int64(1*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
err = f.CreateQuotaInNs(int64(1), int64(1024*1024*1024), int64(2), int64(2*1024*1024*1024))
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(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 host assisted cloning for within namespace when all pvcs are smart clone capable.")
}
}
By("Populating source PVC")
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
// Create targetPvc in new NS.
By("Creating new DV")
targetDV := utils.NewCloningDataVolume("target-dv", "1G", pvcDef)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Verify Quota was exceeded in logs")
matchString := fmt.Sprintf("\\\"cdi-upload-target-dv\\\" is forbidden: exceeded quota: test-quota, requested")
Eventually(func() string {
log, err := RunKubectlCommand(f, "logs", f.ControllerPod.Name, "-n", f.CdiInstallNs)
Expect(err).NotTo(HaveOccurred())
return log
}, controllerSkipPVCCompleteTimeout, assertionPollInterval).Should(ContainSubstring(matchString))
})
It("Should fail to clone in namespace with quota when pods have higher requirements, then succeed when quota increased", func() {
err := f.UpdateCdiConfigResourceLimits(int64(1), int64(1024*1024*1024), int64(1), int64(1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
err = f.CreateQuotaInNs(int64(1), int64(512*1024*1024), int64(1), int64(512*1024*1024))
Expect(err).NotTo(HaveOccurred())
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(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 host assisted cloning for within namespace when all pvcs are smart clone capable.")
}
}
By("Populating source PVC")
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
// Create targetPvc in new NS.
By("Creating new DV")
targetDV := utils.NewCloningDataVolume("target-dv", "1G", pvcDef)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Verify Quota was exceeded in logs")
matchString := fmt.Sprintf("\\\"cdi-upload-target-dv\\\" is forbidden: exceeded quota: test-quota, requested")
Eventually(func() string {
log, err := RunKubectlCommand(f, "logs", f.ControllerPod.Name, "-n", f.CdiInstallNs)
Expect(err).NotTo(HaveOccurred())
return log
}, controllerSkipPVCCompleteTimeout, assertionPollInterval).Should(ContainSubstring(matchString))
err = f.UpdateQuotaInNs(int64(2), int64(2*1024*1024*1024), int64(2), int64(2*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, f.Namespace.Name, v1.ClaimBound, targetDV.Name)
targetPvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
sourcePvcDiskGroup, err := f.GetDiskGroup(f.Namespace, sourcePvc)
Expect(err).ToNot(HaveOccurred())
completeClone(f, f.Namespace, targetPvc, filepath.Join(testBaseDir, testFile), fillDataFSMD5sum, sourcePvcDiskGroup)
})
It("Should create clone in namespace with quota when pods requirements are low enough", func() {
err := f.UpdateCdiConfigResourceLimits(int64(0), int64(0), int64(1), int64(512*1024*1024))
Expect(err).NotTo(HaveOccurred())
err = f.CreateQuotaInNs(int64(1), int64(1024*1024*1024), int64(2), int64(2*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
smartApplicable := f.IsSnapshotStorageClassAvailable()
sc, err := f.K8sClient.StorageV1().StorageClasses().Get(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 host assisted cloning for within namespace when all pvcs are smart clone capable.")
}
}
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
doFileBasedCloneTest(f, pvcDef, f.Namespace, "target-dv")
})
It("Should fail clone data across namespaces, if a namespace doesn't have enough quota", func() {
err := f.UpdateCdiConfigResourceLimits(int64(2), int64(1024*1024*1024), int64(2), int64(1*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
err = f.CreateQuotaInNs(int64(1), int64(1024*1024*1024), int64(2), int64(2*1024*1024*1024))
Expect(err).NotTo(HaveOccurred())
pvcDef := utils.NewPVCDefinition(sourcePVCName, "500M", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
targetDV := utils.NewDataVolumeForImageCloning("target-dv", "500M", sourcePvc.Namespace, sourcePvc.Name, sourcePvc.Spec.StorageClassName, sourcePvc.Spec.VolumeMode)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, targetNs.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
_, err = utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
By("Verify Quota was exceeded in logs")
targetPvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
matchString := fmt.Sprintf("\\\"%s-source-pod\\\" is forbidden: exceeded quota: test-quota, requested: requests.cpu=2, used: requests.cpu=0, limited: requests.cpu=1", targetPvc.GetUID())
Eventually(func() string {
log, err := RunKubectlCommand(f, "logs", f.ControllerPod.Name, "-n", f.CdiInstallNs)
Expect(err).NotTo(HaveOccurred())
return log
}, controllerSkipPVCCompleteTimeout, assertionPollInterval).Should(ContainSubstring(matchString))
})
})
var _ = Describe("[rfe_id:1277][crit:high][vendor:cnv-qe@redhat.com][level:component]Cloner Test Suite", func() {
f := framework.NewFrameworkOrDie(namespacePrefix)
var sourcePvc *v1.PersistentVolumeClaim
var targetPvc *v1.PersistentVolumeClaim
AfterEach(func() {
if sourcePvc != nil {
By("[AfterEach] Clean up source PVC")
err := f.DeletePVC(sourcePvc)
Expect(err).ToNot(HaveOccurred())
}
if targetPvc != nil {
By("[AfterEach] Clean up target PVC")
err := f.DeletePVC(targetPvc)
Expect(err).ToNot(HaveOccurred())
}
})
It("[test_id:3999] Create a data volume and then clone it and verify retry count", func() {
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
targetDvName := "target-dv"
doFileBasedCloneTest(f, pvcDef, targetNs, targetDvName)
By("Verify retry annotation on PVC")
targetPvc, err := utils.WaitForPVC(f.K8sClient, targetNs.Name, targetDvName)
Expect(err).ToNot(HaveOccurred())
restartsValue, status, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnPodRestarts)
Expect(err).ToNot(HaveOccurred())
Expect(status).To(BeTrue())
Expect(restartsValue).To(Equal("0"))
By("Verify the number of retries on the datavolume")
dv, err := f.CdiClient.CdiV1alpha1().DataVolumes(targetNs.Name).Get(targetDvName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(dv.Status.RestartCount).To(BeNumerically("==", 0))
})
It("[test_id:4000] Create a data volume and then clone it while killing the container and verify retry count", func() {
By("Prepare source PVC")
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
By("Create clone DV")
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
targetDV := utils.NewCloningDataVolume("target-dv", "1G", pvcDef)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, targetNs.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
targetPvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: wait for PVC claim phase: %s\n", targetPvc.Name)
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, targetNs.Name, v1.ClaimBound, targetPvc.Name)
By("Wait for upload pod")
err = utils.WaitTimeoutForPodReady(f.K8sClient, "cdi-upload-target-dv", targetNs.Name, utils.PodWaitForTime)
Expect(err).ToNot(HaveOccurred())
By("Kill upload pod to force error")
utils.UploadPodName(targetPvc)
_, errLog, err := f.ExecShellInPodWithFullOutput(targetNs.Name, "cdi-upload-target-dv", "kill 1")
Expect(err).To(BeNil())
Expect(errLog).To(BeEmpty())
By("Verify retry annotation on PVC")
Eventually(func() int {
restarts, status, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnPodRestarts)
Expect(err).ToNot(HaveOccurred())
Expect(status).To(BeTrue())
i, err := strconv.Atoi(restarts)
Expect(err).ToNot(HaveOccurred())
return i
}, timeout, pollingInterval).Should(BeNumerically(">=", 1))
By("Verify the number of retries on the datavolume")
Eventually(func() int32 {
dv, err := f.CdiClient.CdiV1alpha1().DataVolumes(dataVolume.Namespace).Get(dataVolume.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
restarts := dv.Status.RestartCount
return restarts
}, timeout, pollingInterval).Should(BeNumerically(">=", 1))
})
It("[test_id:4276] Clone datavolume with short name", func() {
shortDvName := "import-long-name-dv"
By(fmt.Sprintf("Create PVC %s", shortDvName))
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetDvName := shortDvName
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
doFileBasedCloneTest(f, pvcDef, targetNs, targetDvName)
By("Verify retry annotation on PVC")
targetPvc, err := utils.WaitForPVC(f.K8sClient, targetNs.Name, targetDvName)
Expect(err).ToNot(HaveOccurred())
restartsValue, status, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnPodRestarts)
Expect(err).ToNot(HaveOccurred())
Expect(status).To(BeTrue())
Expect(restartsValue).To(Equal("0"))
By("Verify the number of retries on the datavolume")
dv, err := f.CdiClient.CdiV1alpha1().DataVolumes(targetNs.Name).Get(targetDvName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(dv.Status.RestartCount).To(BeNumerically("==", 0))
})
It("[test_id:4277] Clone datavolume with long name", func() {
// 20 chars + 100ch + 40chars
dvName160Characters := "import-long-name-dv-" +
"123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-" +
"123456789-123456789-123456789-1234567890"
By(fmt.Sprintf("Create PVC %s", dvName160Characters))
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetDvName := dvName160Characters
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
doFileBasedCloneTest(f, pvcDef, targetNs, targetDvName)
By("Verify retry annotation on PVC")
targetPvc, err := utils.WaitForPVC(f.K8sClient, targetNs.Name, targetDvName)
Expect(err).ToNot(HaveOccurred())
restartsValue, status, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnPodRestarts)
Expect(err).ToNot(HaveOccurred())
Expect(status).To(BeTrue())
Expect(restartsValue).To(Equal("0"))
By("Verify the number of retries on the datavolume")
dv, err := f.CdiClient.CdiV1alpha1().DataVolumes(targetNs.Name).Get(targetDvName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(dv.Status.RestartCount).To(BeNumerically("==", 0))
})
It("[test_id:4278] Clone datavolume with long name including special character '.'", func() {
// 20 chars + 100ch + 40chars
dvName160Characters := "import-long-name-dv." +
"123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-" +
"123456789-123456789-123456789-1234567890"
By(fmt.Sprintf("Create PVC %s", dvName160Characters))
pvcDef := utils.NewPVCDefinition(sourcePVCName, "1G", nil, nil)
pvcDef.Namespace = f.Namespace.Name
sourcePvc = f.CreateAndPopulateSourcePVC(pvcDef, sourcePodFillerName, fillCommand+testFile)
targetDvName := dvName160Characters
targetNs, err := f.CreateNamespace(f.NsPrefix, map[string]string{
framework.NsPrefixLabel: f.NsPrefix,
})
Expect(err).NotTo(HaveOccurred())
f.AddNamespaceToDelete(targetNs)
doFileBasedCloneTest(f, pvcDef, targetNs, targetDvName)
By("Verify retry annotation on PVC")
targetPvc, err := utils.WaitForPVC(f.K8sClient, targetNs.Name, targetDvName)
Expect(err).ToNot(HaveOccurred())
restartsValue, status, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnPodRestarts)
Expect(err).ToNot(HaveOccurred())
Expect(status).To(BeTrue())
Expect(restartsValue).To(Equal("0"))
By("Verify the number of retries on the datavolume")
dv, err := f.CdiClient.CdiV1alpha1().DataVolumes(targetNs.Name).Get(targetDvName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(dv.Status.RestartCount).To(BeNumerically("==", 0))
})
})
func doFileBasedCloneTest(f *framework.Framework, srcPVCDef *v1.PersistentVolumeClaim, targetNs *v1.Namespace, targetDv string) {
// Create targetPvc in new NS.
targetDV := utils.NewCloningDataVolume(targetDv, "1G", srcPVCDef)
dataVolume, err := utils.CreateDataVolumeFromDefinition(f.CdiClient, targetNs.Name, targetDV)
Expect(err).ToNot(HaveOccurred())
targetPvc, err := utils.WaitForPVC(f.K8sClient, dataVolume.Namespace, dataVolume.Name)
Expect(err).ToNot(HaveOccurred())
fmt.Fprintf(GinkgoWriter, "INFO: wait for PVC claim phase: %s\n", targetPvc.Name)
utils.WaitForPersistentVolumeClaimPhase(f.K8sClient, targetNs.Name, v1.ClaimBound, targetPvc.Name)
sourcePvcDiskGroup, err := f.GetDiskGroup(f.Namespace, srcPVCDef)
fmt.Fprintf(GinkgoWriter, "INFO: %s\n", sourcePvcDiskGroup)
Expect(err).ToNot(HaveOccurred())
completeClone(f, targetNs, targetPvc, filepath.Join(testBaseDir, testFile), fillDataFSMD5sum, sourcePvcDiskGroup)
}
func completeClone(f *framework.Framework, targetNs *v1.Namespace, targetPvc *v1.PersistentVolumeClaim, filePath, expectedMD5, sourcePvcDiskGroup string) {
By("Verify the clone annotation is on the target PVC")
_, cloneAnnotationFound, err := utils.WaitForPVCAnnotation(f.K8sClient, targetNs.Name, targetPvc, controller.AnnCloneOf)
if err != nil {
PrintControllerLog(f)
}
Expect(err).ToNot(HaveOccurred())
Expect(cloneAnnotationFound).To(BeTrue())
By("Verify the clone status is success on the target datavolume")
err = utils.WaitForDataVolumePhase(f.CdiClient, targetNs.Name, cdiv1.Succeeded, targetPvc.Name)
By("Verify the content")
Expect(f.VerifyTargetPVCContentMD5(targetNs, targetPvc, filePath, expectedMD5)).To(BeTrue())
if utils.DefaultStorageCSI {
// CSI storage class, it should respect fsGroup
By("Checking that disk image group is qemu")
Expect(f.GetDiskGroup(targetNs, targetPvc)).To(Equal(sourcePvcDiskGroup))
}
}
func cloneOfAnnoExistenceTest(f *framework.Framework, targetNamespaceName string) {
// Create targetPvc
By(fmt.Sprintf("Creating target pvc: %s/target-pvc", targetNamespaceName))
_, err := utils.CreatePVCFromDefinition(f.K8sClient, targetNamespaceName, utils.NewPVCDefinition(
"target-pvc",
"1G",
map[string]string{
controller.AnnCloneRequest: f.Namespace.Name + "/" + sourcePVCName,
controller.AnnCloneOf: "true",
controller.AnnPodPhase: "Succeeded",
},
nil))
Expect(err).ToNot(HaveOccurred())
By("Checking no cloning pods were created")
matchString := fmt.Sprintf("{\"PVC\": \"%s/target-pvc\", \"isUpload\": false, \"isCloneTarget\": true, \"podSucceededFromPVC\": true, \"deletionTimeStamp set?\": false}", f.Namespace.Name)
Eventually(func() bool {
log, err := RunKubectlCommand(f, "logs", f.ControllerPod.Name, "-n", f.CdiInstallNs)
Expect(err).NotTo(HaveOccurred())
return strings.Contains(log, matchString)
}, controllerSkipPVCCompleteTimeout, assertionPollInterval).Should(BeTrue())
Expect(err).ToNot(HaveOccurred())
By("Checking logs explicitly skips PVC")
Eventually(func() bool {
log, err := RunKubectlCommand(f, "logs", f.ControllerPod.Name, "-n", f.CdiInstallNs)
Expect(err).NotTo(HaveOccurred())
return strings.Contains(log, fmt.Sprintf("{\"PVC\": \"%s/%s\", \"checkPVC(AnnCloneRequest)\": true, \"NOT has annotation(AnnCloneOf)\": false, \"has finalizer?\": false}", targetNamespaceName, "target-pvc"))
}, controllerSkipPVCCompleteTimeout, assertionPollInterval).Should(BeTrue())
Expect(err).ToNot(HaveOccurred())
}
func cleanDv(f *framework.Framework, dv *cdiv1.DataVolume) {
if dv != nil {
err := utils.DeleteDataVolume(f.CdiClient, f.Namespace.Name, dv.Name)
Expect(err).ToNot(HaveOccurred())
}
}