containerized-data-importer/tests/datasource_test.go
kubevirt-bot 8abf94a81f
[release-v1.57] Allow ImmediateBinding annotation when using populators (#2793)
* 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>
2023-07-10 14:23:16 +02:00

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)
}