mirror of
https://github.com/kubevirt/containerized-data-importer.git
synced 2025-06-03 06:30:22 +00:00

* Allow ImmediateBind annotation when using populators In case of using PVC with populators if the PVC has this annotation we prevent from waiting for it to be schedueled and we proceed with the process. When using datavolumes with populators in case the dv has the annotation it will be passed to the PVC. we prevent from being in pendingPopulation in case the created pvc has the annotaion. Plus when having honorWaitForFirstConsumer feature gate disabled we will put on the target PVC the immediateBind annotation. Now we allow to use populators when having the annotation the the feature gate disabled. Signed-off-by: Shelly Kagan <skagan@redhat.com> * Add functional tests to population using PVCs Signed-off-by: Shelly Kagan <skagan@redhat.com> * Support immediate binding with clone datavolume Signed-off-by: Shelly Kagan <skagan@redhat.com> * Pass allowed annotations from target pvc to pvc prime This annotations are used for the import/upload/clone pods to define netork configurations. Signed-off-by: Shelly Kagan <skagan@redhat.com> --------- Signed-off-by: Shelly Kagan <skagan@redhat.com> Co-authored-by: Shelly Kagan <skagan@redhat.com>
277 lines
12 KiB
Go
277 lines
12 KiB
Go
package tests_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
|
|
"kubevirt.io/containerized-data-importer/pkg/controller"
|
|
cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
|
|
dvc "kubevirt.io/containerized-data-importer/pkg/controller/datavolume"
|
|
"kubevirt.io/containerized-data-importer/tests/framework"
|
|
"kubevirt.io/containerized-data-importer/tests/utils"
|
|
)
|
|
|
|
var _ = Describe("DataSource", func() {
|
|
const (
|
|
ds1Name = "ds1"
|
|
ds2Name = "ds2"
|
|
pvc1Name = "pvc1"
|
|
pvc2Name = "pvc2"
|
|
snap1Name = "snap1"
|
|
snap2Name = "snap2"
|
|
)
|
|
|
|
f := framework.NewFramework("datasource-func-test")
|
|
|
|
newDataSource := func(dsName string) *cdiv1.DataSource {
|
|
return &cdiv1.DataSource{
|
|
TypeMeta: metav1.TypeMeta{APIVersion: cdiv1.SchemeGroupVersion.String()},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: dsName,
|
|
Namespace: f.Namespace.Name,
|
|
},
|
|
}
|
|
}
|
|
|
|
waitForReadyCondition := func(ds *cdiv1.DataSource, status corev1.ConditionStatus, reason string) *cdiv1.DataSource {
|
|
By(fmt.Sprintf("wait for DataSource %s ready condition: %s, %s", ds.Name, status, reason))
|
|
Eventually(func() bool {
|
|
var err error
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Get(context.TODO(), ds.Name, metav1.GetOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
cond := controller.FindDataSourceConditionByType(ds, cdiv1.DataSourceReady)
|
|
if cond != nil {
|
|
By(fmt.Sprintf("condition state: %s, %s", cond.Status, cond.Reason))
|
|
}
|
|
return cond != nil && cond.Status == status && cond.Reason == reason
|
|
}, timeout, pollingInterval).Should(BeTrue())
|
|
return ds
|
|
}
|
|
|
|
testURL := func() string { return fmt.Sprintf(utils.TinyCoreQcow2URL, f.CdiInstallNs) }
|
|
createDv := func(pvcName, url string) {
|
|
By(fmt.Sprintf("creating DataVolume %s %s", pvcName, url))
|
|
dv := utils.NewDataVolumeWithHTTPImport(pvcName, "1Gi", url)
|
|
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)
|
|
}
|
|
|
|
It("[test_id:8041]status conditions should be updated on pvc create/update/delete", func() {
|
|
By("Create DataSource with no source PVC")
|
|
ds := newDataSource(ds1Name)
|
|
ds, err := f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Create(context.TODO(), ds, metav1.CreateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NoSource")
|
|
|
|
By("Update DataSource source PVC to nonexisting one")
|
|
ds.Spec.Source.PVC = &cdiv1.DataVolumeSourcePVC{Namespace: f.Namespace.Name, Name: pvc1Name}
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Update(context.TODO(), ds, metav1.UpdateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NotFound")
|
|
|
|
By("Create clone DV with SourceRef pointing the DataSource")
|
|
dv := utils.NewDataVolumeWithSourceRef("clone-dv", "1Gi", ds.Namespace, ds.Name)
|
|
dv.Annotations[cc.AnnImmediateBinding] = "true"
|
|
Expect(dv).ToNot(BeNil())
|
|
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
By("Verify DV conditions")
|
|
utils.WaitForConditions(f, dv.Name, dv.Namespace, time.Minute, pollingInterval,
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeBound, Status: corev1.ConditionUnknown, Message: "The source pvc pvc1 doesn't exist", Reason: dvc.CloneWithoutSource},
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeReady, Status: corev1.ConditionFalse, Reason: dvc.CloneWithoutSource},
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeRunning, Status: corev1.ConditionFalse})
|
|
f.ExpectEvent(dv.Namespace).Should(ContainSubstring(dvc.CloneWithoutSource))
|
|
|
|
By("Create import DV so the missing DataSource source PVC will be ready")
|
|
createDv(pvc1Name, testURL())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionTrue, "Ready")
|
|
|
|
By("Wait for the clone DV success")
|
|
err = utils.WaitForDataVolumePhase(f, dv.Namespace, cdiv1.Succeeded, dv.Name)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
deleteDvPvc(f, pvc1Name)
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NotFound")
|
|
|
|
ds.Spec.Source.PVC = nil
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Update(context.TODO(), ds, metav1.UpdateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
_ = waitForReadyCondition(ds, corev1.ConditionFalse, "NoSource")
|
|
})
|
|
|
|
createDs := func(dsName, pvcName string) *cdiv1.DataSource {
|
|
By(fmt.Sprintf("creating DataSource %s -> %s", dsName, pvcName))
|
|
ds := newDataSource(dsName)
|
|
ds.Spec.Source.PVC = &cdiv1.DataVolumeSourcePVC{Namespace: f.Namespace.Name, Name: pvcName}
|
|
ds, err := f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Create(context.TODO(), ds, metav1.CreateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
return waitForReadyCondition(ds, corev1.ConditionTrue, "Ready")
|
|
}
|
|
|
|
updateDsPvc := func(ds *cdiv1.DataSource, pvcName string) {
|
|
By(fmt.Sprintf("updating DataSource %s -> %s", ds.Name, pvcName))
|
|
ds.Spec.Source.PVC = &cdiv1.DataVolumeSourcePVC{Namespace: "", Name: pvcName}
|
|
_, err := f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Update(context.TODO(), ds, metav1.UpdateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
}
|
|
|
|
It("[test_id:8067]status conditions should be updated when several DataSources refer the same pvc", func() {
|
|
createDv(pvc1Name, testURL())
|
|
ds1 := createDs(ds1Name, pvc1Name)
|
|
ds2 := createDs(ds2Name, pvc1Name)
|
|
|
|
ds1 = waitForReadyCondition(ds1, corev1.ConditionTrue, "Ready")
|
|
ds2 = waitForReadyCondition(ds2, corev1.ConditionTrue, "Ready")
|
|
|
|
deleteDvPvc(f, pvc1Name)
|
|
ds1 = waitForReadyCondition(ds1, corev1.ConditionFalse, "NotFound")
|
|
ds2 = waitForReadyCondition(ds2, corev1.ConditionFalse, "NotFound")
|
|
|
|
createDv(pvc2Name, testURL()+"bad")
|
|
updateDsPvc(ds1, pvc2Name)
|
|
updateDsPvc(ds2, pvc2Name)
|
|
ds1 = waitForReadyCondition(ds1, corev1.ConditionFalse, "ImportInProgress")
|
|
ds2 = waitForReadyCondition(ds2, corev1.ConditionFalse, "ImportInProgress")
|
|
|
|
deleteDvPvc(f, pvc2Name)
|
|
_ = waitForReadyCondition(ds1, corev1.ConditionFalse, "NotFound")
|
|
_ = waitForReadyCondition(ds2, corev1.ConditionFalse, "NotFound")
|
|
})
|
|
|
|
It("status conditions timestamp should be updated when DataSource referred pvc is updated, although condition status does not change", func() {
|
|
createDv(pvc1Name, testURL())
|
|
ds := createDs(ds1Name, pvc1Name)
|
|
ds = waitForReadyCondition(ds, corev1.ConditionTrue, "Ready")
|
|
cond := controller.FindDataSourceConditionByType(ds, cdiv1.DataSourceReady)
|
|
Expect(cond).ToNot(BeNil())
|
|
ts := cond.LastTransitionTime
|
|
|
|
createDv(pvc2Name, testURL())
|
|
err := utils.WaitForDataVolumePhase(f, f.Namespace.Name, cdiv1.Succeeded, pvc2Name)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
updateDsPvc(ds, pvc2Name)
|
|
|
|
Eventually(func() metav1.Time {
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Get(context.TODO(), ds.Name, metav1.GetOptions{})
|
|
Expect(ds.Spec.Source.PVC.Name).To(Equal(pvc2Name))
|
|
cond = controller.FindDataSourceConditionByType(ds, cdiv1.DataSourceReady)
|
|
Expect(cond).ToNot(BeNil())
|
|
Expect(cond.Status).To(Equal(corev1.ConditionTrue))
|
|
return cond.LastTransitionTime
|
|
}, 60*time.Second, pollingInterval).ShouldNot(Equal(ts))
|
|
})
|
|
|
|
Context("snapshot source", func() {
|
|
createSnap := func(name string) *snapshotv1.VolumeSnapshot {
|
|
pvcDef := utils.NewPVCDefinition("snap-source-pvc", "1Gi", nil, nil)
|
|
pvcDef.Namespace = f.Namespace.Name
|
|
pvc, err := f.K8sClient.CoreV1().PersistentVolumeClaims(f.Namespace.Name).Create(context.TODO(), pvcDef, metav1.CreateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
f.ForceBindIfWaitForFirstConsumer(pvc)
|
|
|
|
snapClass := f.GetSnapshotClass()
|
|
snapshot := utils.NewVolumeSnapshot(name, pvc.Namespace, pvc.Name, &snapClass.Name)
|
|
err = f.CrClient.Create(context.TODO(), snapshot)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
return snapshot
|
|
}
|
|
|
|
createSnapDs := func(dsName, snapName string) *cdiv1.DataSource {
|
|
By(fmt.Sprintf("creating DataSource %s -> %s", dsName, snapName))
|
|
ds := newDataSource(dsName)
|
|
ds.Spec.Source.Snapshot = &cdiv1.DataVolumeSourceSnapshot{Namespace: f.Namespace.Name, Name: snapName}
|
|
ds, err := f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Create(context.TODO(), ds, metav1.CreateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
return waitForReadyCondition(ds, corev1.ConditionTrue, "Ready")
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
if !f.IsSnapshotStorageClassAvailable() {
|
|
Skip("Clone from volumesnapshot does not work without snapshot capable storage")
|
|
}
|
|
})
|
|
|
|
It("[test_id:9762] status conditions should be updated on snapshot create/update/delete", func() {
|
|
By("Create DataSource with no source")
|
|
ds := newDataSource(ds1Name)
|
|
ds, err := f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Create(context.TODO(), ds, metav1.CreateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NoSource")
|
|
|
|
By("Update DataSource source snapshot to nonexisting one")
|
|
ds.Spec.Source.Snapshot = &cdiv1.DataVolumeSourceSnapshot{Namespace: f.Namespace.Name, Name: snap1Name}
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Update(context.TODO(), ds, metav1.UpdateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NotFound")
|
|
|
|
By("Create clone DV with SourceRef pointing the DataSource")
|
|
dv := utils.NewDataVolumeWithSourceRef("clone-dv", "1Gi", ds.Namespace, ds.Name)
|
|
dv.Annotations[cc.AnnImmediateBinding] = "true"
|
|
Expect(dv).ToNot(BeNil())
|
|
dv, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, dv)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
pvc, err := utils.WaitForPVC(f.K8sClient, dv.Namespace, dv.Name)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
if pvc.Spec.DataSourceRef == nil || pvc.Spec.DataSourceRef.Kind != cdiv1.VolumeCloneSourceRef {
|
|
By("Verify DV conditions")
|
|
utils.WaitForConditions(f, dv.Name, dv.Namespace, time.Minute, pollingInterval,
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeBound, Status: corev1.ConditionUnknown, Message: "The source snapshot snap1 doesn't exist", Reason: dvc.CloneWithoutSource},
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeReady, Status: corev1.ConditionFalse, Reason: dvc.CloneWithoutSource},
|
|
&cdiv1.DataVolumeCondition{Type: cdiv1.DataVolumeRunning, Status: corev1.ConditionFalse})
|
|
}
|
|
f.ExpectEvent(dv.Namespace).Should(ContainSubstring(dvc.CloneWithoutSource))
|
|
|
|
By("Create snapshot so the DataSource will be ready")
|
|
snapshot := createSnap(snap1Name)
|
|
ds = waitForReadyCondition(ds, corev1.ConditionTrue, "Ready")
|
|
|
|
By("Wait for the clone DV success")
|
|
err = utils.WaitForDataVolumePhase(f, dv.Namespace, cdiv1.Succeeded, dv.Name)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = f.CrClient.Delete(context.TODO(), snapshot)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
ds = waitForReadyCondition(ds, corev1.ConditionFalse, "NotFound")
|
|
|
|
ds.Spec.Source.Snapshot = nil
|
|
ds, err = f.CdiClient.CdiV1beta1().DataSources(ds.Namespace).Update(context.TODO(), ds, metav1.UpdateOptions{})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
_ = waitForReadyCondition(ds, corev1.ConditionFalse, "NoSource")
|
|
})
|
|
|
|
It("[test_id:9763] status conditions should be updated when several DataSources refer the same snapshot", func() {
|
|
snapshot := createSnap(snap1Name)
|
|
ds1 := createSnapDs(ds1Name, snap1Name)
|
|
ds2 := createSnapDs(ds2Name, snap1Name)
|
|
|
|
ds1 = waitForReadyCondition(ds1, corev1.ConditionTrue, "Ready")
|
|
ds2 = waitForReadyCondition(ds2, corev1.ConditionTrue, "Ready")
|
|
|
|
err := f.CrClient.Delete(context.TODO(), snapshot)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
_ = waitForReadyCondition(ds1, corev1.ConditionFalse, "NotFound")
|
|
_ = waitForReadyCondition(ds2, corev1.ConditionFalse, "NotFound")
|
|
})
|
|
})
|
|
})
|
|
|
|
func deleteDvPvc(f *framework.Framework, pvcName string) {
|
|
utils.CleanupDvPvc(f.K8sClient, f.CdiClient, f.Namespace.Name, pvcName)
|
|
}
|