containerized-data-importer/tests/utils/datavolume.go
Arnon Gilboa addf25b4f9
Support registry import using node docker cache (#1913)
* Support registry import using node docker cache

The new CRI (container runtime interface) importer pod is created with three containers and a shared emptyDir volume:
-Init container: copies static http server binary to empty dir
-Server container: container image container configured to run the http binary and serve up the image file in /data
-Client container: import.sh uses cdi-import to import from server container, and writes "done" file on emptydir
-Server container sees "done" file and exits

Thanks mhenriks for the PoC!

Done:
-added ImportMethod to DataVolumeSourceRegistry (DataVolume.Spec.Source.Registry, DataImportCron.Spec.Source.Registry).
Import method can be "skopeo" (default), or "cri" for container runtime interface based import
-added cdi-containerimage-server & import.sh to the cdi-importer container

ToDo:
-utests and func tests
-doc

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* Add tests, fix CR comments

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* CR fixes

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* Use deployment docker prefix and tag in func tests

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* Add OpenShift ImageStreams import support

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* Add importer pod lookup annotation for image streams

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>

* Add pullMethod and imageStream doc

Signed-off-by: Arnon Gilboa <agilboa@redhat.com>
2021-09-20 22:05:36 +02:00

614 lines
23 KiB
Go

package utils
import (
"context"
"fmt"
"time"
"github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
k8sv1 "k8s.io/api/core/v1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
cdiv1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1"
cdiclientset "kubevirt.io/containerized-data-importer/pkg/client/clientset/versioned"
)
const (
dataVolumePollInterval = 3 * time.Second
dataVolumeCreateTime = 270 * time.Second
dataVolumeDeleteTime = 270 * time.Second
dataVolumePhaseTime = 270 * time.Second
)
const (
// TinyCoreIsoURL provides a test url for the tineyCore iso image
TinyCoreIsoURL = "http://cdi-file-host.%s/tinyCore.iso"
// TinyCoreQcow2URL provides a test url for the tineyCore qcow2 image
TinyCoreQcow2URL = "http://cdi-file-host.%s/tinyCore.qcow2"
//TinyCoreIsoRegistryURL provides a test url for the tinycore.qcow2 image wrapped in docker container
TinyCoreIsoRegistryURL = "docker://cdi-docker-registry-host.%s/tinycoreqcow2"
//TinyCoreIsoRegistryProxyURL provides a test url for the tinycore.qcow2 image wrapped in docker container available through rate-limiting proxy
TinyCoreIsoRegistryProxyURL = "docker://cdi-file-host.%s:83/tinycoreqcow2"
// HTTPSTinyCoreIsoURL provides a test (https) url for the tineyCore iso image
HTTPSTinyCoreIsoURL = "https://cdi-file-host.%s/tinyCore.iso"
// HTTPSTinyCoreQcow2URL provides a test (https) url for the tineyCore qcow2 image
HTTPSTinyCoreQcow2URL = "https://cdi-file-host.%s/tinyCore.qcow2"
// TinyCoreQcow2URLRateLimit provides a test url for the tineyCore qcow2 image via rate-limiting proxy
TinyCoreQcow2URLRateLimit = "http://cdi-file-host.%s:82/tinyCore.qcow2"
// TinyCoreQcow2GzURLRateLimit provides a test url for the tineyCore qcow2.gz image via rate-limiting proxy
TinyCoreQcow2GzURLRateLimit = "http://cdi-file-host.%s:82/tinyCore.qcow2.gz"
// HTTPSTinyCoreVmdkURL provides a test url for the tineyCore qcow2 image
HTTPSTinyCoreVmdkURL = "https://cdi-file-host.%s/tinyCore.vmdk"
// HTTPSTinyCoreVdiURL provides a test url for the tineyCore qcow2 image
HTTPSTinyCoreVdiURL = "https://cdi-file-host.%s/tinyCore.vdi"
// HTTPSTinyCoreVhdURL provides a test url for the tineyCore qcow2 image
HTTPSTinyCoreVhdURL = "https://cdi-file-host.%s/tinyCore.vhd"
// HTTPSTinyCoreVhdxURL provides a test url for the tineyCore qcow2 image
HTTPSTinyCoreVhdxURL = "https://cdi-file-host.%s/tinyCore.vhdx"
// InvalidQcowImagesURL provides a test url for invalid qcow images
InvalidQcowImagesURL = "http://cdi-file-host.%s/invalid_qcow_images/"
// LargeVirtualDiskQcow provides a test url for a cirros image with a large virtual size, in qcow2 format
LargeVirtualDiskQcow = "http://cdi-file-host.%s/cirros-large-virtual-size.qcow2"
// LargeVirtualDiskXz provides a test url for a cirros image with a large virtual size, in RAW format, XZ-compressed
LargeVirtualDiskXz = "http://cdi-file-host.%s/cirros-large-virtual-size.raw.xz"
// LargePhysicalDiskQcow provides a test url for a cirros image with a large physical size, in qcow2 format
LargePhysicalDiskQcow = "http://cdi-file-host.%s/cirros-large-physical-size.qcow2"
// LargePhysicalDiskXz provides a test url for a cirros image with a large physical size, in RAW format, XZ-compressed
LargePhysicalDiskXz = "http://cdi-file-host.%s/cirros-large-physical-size.raw.xz"
// TarArchiveURL provides a test url for a tar achive file
TarArchiveURL = "http://cdi-file-host.%s/archive.tar"
// CirrosURL provides the standard cirros image qcow image
CirrosURL = "http://cdi-file-host.%s/cirros-qcow2.img"
// ImageioURL provides URL of oVirt engine hosting imageio
ImageioURL = "https://imageio.%s:12346/ovirt-engine/api"
// ImageioRootURL provides the base path to fakeovirt, for inventory modifications
ImageioRootURL = "https://imageio.%s:12346"
// ImageioImageURL provides the base path to imageiotest, for test images
ImageioImageURL = "https://imageio.%s:12345"
// VcenterURL provides URL of vCenter/ESX simulator
VcenterURL = "https://vcenter.%s:8989/sdk"
// TrustedRegistryURL provides the base path to trusted registry test url for the tinycore.iso image wrapped in docker container
TrustedRegistryURL = "docker://%s/cdi-func-test-tinycore:%s"
// TrustedRegistryIS provides the base path to trusted registry test fake imagestream for the tinycore.iso image wrapped in docker container
TrustedRegistryIS = "%s/cdi-func-test-tinycore:%s"
// TinyCoreMD5 is the MD5 hash of first 100k bytes of tinyCore image
TinyCoreMD5 = "3710416a680523c7d07538cb1026c60c"
// TinyCoreTarMD5 is the MD5 hash of first 100k bytes of tinyCore tar image
TinyCoreTarMD5 = "aec1a39d753b4b7cc81ee02bc625a342"
// ImageioMD5 is the MD5 hash of first 100k bytes of imageio image
ImageioMD5 = "91150be031835ccfac458744da57d4f6"
// VcenterMD5 is the MD5 hash of first 100k bytes of Vcenter image
VcenterMD5 = "91150be031835ccfac458744da57d4f6"
// BlankMD5 is the MD5 hash of first 100k bytes of blank image
BlankMD5 = "0019d23bef56a136a1891211d7007f6f"
)
// CreateDataVolumeFromDefinition is used by tests to create a testable Data Volume
func CreateDataVolumeFromDefinition(clientSet *cdiclientset.Clientset, namespace string, def *cdiv1.DataVolume) (*cdiv1.DataVolume, error) {
var dataVolume *cdiv1.DataVolume
err := wait.PollImmediate(dataVolumePollInterval, dataVolumeCreateTime, func() (bool, error) {
var err error
dataVolume, err = clientSet.CdiV1beta1().DataVolumes(namespace).Create(context.TODO(), def, metav1.CreateOptions{})
if err == nil || apierrs.IsAlreadyExists(err) {
return true, nil
}
return false, err
})
if err != nil {
return nil, err
}
return dataVolume, nil
}
// DeleteDataVolume deletes the DataVolume with the given name
func DeleteDataVolume(clientSet *cdiclientset.Clientset, namespace, name string) error {
return wait.PollImmediate(dataVolumePollInterval, dataVolumeDeleteTime, func() (bool, error) {
err := clientSet.CdiV1beta1().DataVolumes(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err == nil || apierrs.IsNotFound(err) {
return true, nil
}
return false, err
})
}
// NewCloningDataVolume initializes a DataVolume struct with PVC annotations
func NewCloningDataVolume(dataVolumeName, size string, sourcePvc *k8sv1.PersistentVolumeClaim) *cdiv1.DataVolume {
return NewDataVolumeForImageCloning(dataVolumeName, size, sourcePvc.Namespace, sourcePvc.Name, sourcePvc.Spec.StorageClassName, sourcePvc.Spec.VolumeMode)
}
// NewDataVolumeWithSourceRef initializes a DataVolume struct with DataSource SourceRef
func NewDataVolumeWithSourceRef(dataVolumeName string, size, sourceRefNamespace, sourceRefName string) *cdiv1.DataVolume {
claimSpec := &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceStorage: resource.MustParse(size),
},
},
}
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
SourceRef: &cdiv1.DataVolumeSourceRef{
Kind: cdiv1.DataVolumeDataSource,
Namespace: &sourceRefNamespace,
Name: sourceRefName,
},
PVC: claimSpec,
},
}
}
// NewDataSource initializes a DataSource struct with PVC source
func NewDataSource(dataSourceName, dataSourceNamespace, pvcName, pvcNamespace string) *cdiv1.DataSource {
return &cdiv1.DataSource{
ObjectMeta: metav1.ObjectMeta{
Name: dataSourceName,
Namespace: dataSourceNamespace,
},
Spec: cdiv1.DataSourceSpec{
Source: cdiv1.DataSourceSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Name: pvcName,
Namespace: pvcNamespace,
},
},
},
}
}
// NewDataVolumeWithHTTPImport initializes a DataVolume struct with HTTP annotations
func NewDataVolumeWithHTTPImport(dataVolumeName string, size string, httpURL string) *cdiv1.DataVolume {
claimSpec := &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceStorage: resource.MustParse(size),
},
},
}
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
HTTP: &cdiv1.DataVolumeSourceHTTP{
URL: httpURL,
},
},
PVC: claimSpec,
},
}
}
// NewDataVolumeWithHTTPImportAndStorageSpec initializes a DataVolume struct with HTTP annotations
func NewDataVolumeWithHTTPImportAndStorageSpec(dataVolumeName string, size string, httpURL string) *cdiv1.DataVolume {
storageSpec := &cdiv1.StorageSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceStorage: resource.MustParse(size),
},
},
}
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
HTTP: &cdiv1.DataVolumeSourceHTTP{
URL: httpURL,
},
},
Storage: storageSpec,
},
}
}
// NewDataVolumeWithImageioImport initializes a DataVolume struct with Imageio annotations
func NewDataVolumeWithImageioImport(dataVolumeName string, size string, httpURL string, secret string, configMap string, diskID string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Imageio: &cdiv1.DataVolumeSourceImageIO{
URL: httpURL,
SecretRef: secret,
CertConfigMap: configMap,
DiskID: diskID,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// NewDataVolumeWithImageioWarmImport initializes a DataVolume struct for a multi-stage import from oVirt snapshots
func NewDataVolumeWithImageioWarmImport(dataVolumeName string, size string, httpURL string, secret string, configMap string, diskID string, checkpoints []cdiv1.DataVolumeCheckpoint, finalCheckpoint bool) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Imageio: &cdiv1.DataVolumeSourceImageIO{
URL: httpURL,
SecretRef: secret,
CertConfigMap: configMap,
DiskID: diskID,
},
},
FinalCheckpoint: finalCheckpoint,
Checkpoints: checkpoints,
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// NewDataVolumeWithHTTPImportToBlockPV initializes a DataVolume struct with HTTP annotations to import to block PV
func NewDataVolumeWithHTTPImportToBlockPV(dataVolumeName string, size string, httpURL, storageClassName string) *cdiv1.DataVolume {
volumeMode := corev1.PersistentVolumeMode(corev1.PersistentVolumeBlock)
dataVolume := &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
HTTP: &cdiv1.DataVolumeSourceHTTP{
URL: httpURL,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
VolumeMode: &volumeMode,
StorageClassName: &storageClassName,
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
return dataVolume
}
// NewDataVolumeCloneToBlockPV initializes a DataVolume for block cloning
func NewDataVolumeCloneToBlockPV(dataVolumeName string, size string, srcNamespace, srcName, storageClassName string) *cdiv1.DataVolume {
volumeMode := corev1.PersistentVolumeMode(corev1.PersistentVolumeBlock)
dataVolume := &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Name: srcName,
Namespace: srcNamespace,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
VolumeMode: &volumeMode,
StorageClassName: &storageClassName,
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
return dataVolume
}
// NewDataVolumeForUpload initializes a DataVolume struct with Upload annotations
func NewDataVolumeForUpload(dataVolumeName string, size string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Upload: &cdiv1.DataVolumeSourceUpload{},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// NewDataVolumeForBlankRawImage initializes a DataVolume struct for creating blank raw image
func NewDataVolumeForBlankRawImage(dataVolumeName, size string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Blank: &cdiv1.DataVolumeBlankImage{},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// NewDataVolumeForBlankRawImageBlock initializes a DataVolume struct for creating blank raw image for a block device
func NewDataVolumeForBlankRawImageBlock(dataVolumeName, size string, storageClassName string) *cdiv1.DataVolume {
volumeMode := corev1.PersistentVolumeMode(corev1.PersistentVolumeBlock)
dataVolume := &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Blank: &cdiv1.DataVolumeBlankImage{},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
VolumeMode: &volumeMode,
StorageClassName: &storageClassName,
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
return dataVolume
}
// NewDataVolumeForImageCloning initializes a DataVolume struct for cloning disk image
func NewDataVolumeForImageCloning(dataVolumeName, size, namespace, pvcName string, storageClassName *string, volumeMode *k8sv1.PersistentVolumeMode) *cdiv1.DataVolume {
dv := &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
PVC: &cdiv1.DataVolumeSourcePVC{
Namespace: namespace,
Name: pvcName,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
if volumeMode != nil {
dv.Spec.PVC.VolumeMode = volumeMode
}
if storageClassName != nil {
dv.Spec.PVC.StorageClassName = storageClassName
}
return dv
}
// NewDataVolumeWithRegistryImport initializes a DataVolume struct with registry annotations
func NewDataVolumeWithRegistryImport(dataVolumeName string, size string, registryURL string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
Annotations: map[string]string{},
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
Registry: &cdiv1.DataVolumeSourceRegistry{
URL: &registryURL,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// ModifyDataVolumeWithImportToBlockPV modifies a DataVolume struct for import to a block PV
func ModifyDataVolumeWithImportToBlockPV(dataVolume *cdiv1.DataVolume, storageClassName string) *cdiv1.DataVolume {
volumeMode := corev1.PersistentVolumeMode(corev1.PersistentVolumeBlock)
dataVolume.Spec.PVC.VolumeMode = &volumeMode
dataVolume.Spec.PVC.StorageClassName = &storageClassName
return dataVolume
}
// NewDataVolumeWithVddkImport initializes a DataVolume struct for importing disks from vCenter/ESX
func NewDataVolumeWithVddkImport(dataVolumeName string, size string, backingFile string, secretRef string, thumbprint string, httpURL string, uuid string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
VDDK: &cdiv1.DataVolumeSourceVDDK{
BackingFile: backingFile,
SecretRef: secretRef,
Thumbprint: thumbprint,
URL: httpURL,
UUID: uuid,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// NewDataVolumeWithVddkWarmImport initializes a DataVolume struct for a multi-stage import from vCenter/ESX snapshots
func NewDataVolumeWithVddkWarmImport(dataVolumeName string, size string, backingFile string, secretRef string, thumbprint string, httpURL string, uuid string, currentCheckpoint string, previousCheckpoint string, finalCheckpoint bool) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
VDDK: &cdiv1.DataVolumeSourceVDDK{
BackingFile: backingFile,
SecretRef: secretRef,
Thumbprint: thumbprint,
URL: httpURL,
UUID: uuid,
},
},
FinalCheckpoint: finalCheckpoint,
Checkpoints: []cdiv1.DataVolumeCheckpoint{
{Current: previousCheckpoint, Previous: ""},
{Current: currentCheckpoint, Previous: previousCheckpoint},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// WaitForDataVolumePhase waits for DV's phase to be in a particular phase (Pending, Bound, or Lost)
func WaitForDataVolumePhase(clientSet *cdiclientset.Clientset, namespace string, phase cdiv1.DataVolumePhase, dataVolumeName string) error {
return WaitForDataVolumePhaseWithTimeout(clientSet, namespace, phase, dataVolumeName, dataVolumePhaseTime)
}
// WaitForDataVolumePhaseWithTimeout waits for DV's phase to be in a particular phase (Pending, Bound, or Lost) with a specified timeout
func WaitForDataVolumePhaseWithTimeout(clientSet *cdiclientset.Clientset, namespace string, phase cdiv1.DataVolumePhase, dataVolumeName string, timeout time.Duration) error {
err := wait.PollImmediate(dataVolumePollInterval, timeout, func() (bool, error) {
dataVolume, err := clientSet.CdiV1beta1().DataVolumes(namespace).Get(context.TODO(), dataVolumeName, metav1.GetOptions{})
if err != nil || dataVolume.Status.Phase != phase {
return false, err
}
return true, nil
})
if err != nil {
return fmt.Errorf("DataVolume %s not in phase %s within %v", dataVolumeName, phase, timeout)
}
return nil
}
// NewDataVolumeWithArchiveContent initializes a DataVolume struct with 'archive' ContentType
func NewDataVolumeWithArchiveContent(dataVolumeName string, size string, httpURL string) *cdiv1.DataVolume {
return &cdiv1.DataVolume{
ObjectMeta: metav1.ObjectMeta{
Name: dataVolumeName,
},
Spec: cdiv1.DataVolumeSpec{
Source: &cdiv1.DataVolumeSource{
HTTP: &cdiv1.DataVolumeSourceHTTP{
URL: httpURL,
},
},
ContentType: "archive",
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// PersistentVolumeClaimFromDataVolume creates a PersistentVolumeClaim definition so we can use PersistentVolumeClaim for various operations.
func PersistentVolumeClaimFromDataVolume(datavolume *cdiv1.DataVolume) *corev1.PersistentVolumeClaim {
return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: datavolume.Name,
Namespace: datavolume.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(datavolume, schema.GroupVersionKind{
Group: cdiv1.SchemeGroupVersion.Group,
Version: cdiv1.SchemeGroupVersion.Version,
Kind: "DataVolume",
}),
},
},
Spec: *datavolume.Spec.PVC,
}
}
// GetCloneType returnc the CDI clone type
func GetCloneType(clientSet *cdiclientset.Clientset, dataVolume *cdiv1.DataVolume) string {
var cloneType string
gomega.Eventually(func() bool {
dv, err := clientSet.CdiV1beta1().DataVolumes(dataVolume.Namespace).Get(context.TODO(), dataVolume.Name, metav1.GetOptions{})
gomega.Expect(err).ToNot(gomega.HaveOccurred())
val, ok := dv.Annotations["cdi.kubevirt.io/cloneType"]
if ok {
cloneType = val
}
return ok
}, 90*time.Second, 2*time.Second).Should(gomega.BeTrue())
return cloneType
}