containerized-data-importer/tests/utils/datavolume.go
Tomasz Barański 536af6b1ad
Replace skopeo (#1322)
* Updated dependencies
Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Replace skopeo with containers API.

This commit removes dependence on skopeo (binary) and uses containers
API. By doing that we're able to opimize the use of storage (scratch)
space, storage I/O and download bandwith.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Fixing rebase - dependencies kerfuffle.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Handling docker-format images as well as OCI.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Fix for missing code-generator module.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Remove regex, image file in registry images are matched by a path
prefix.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>

* Added nginx proxy in front of docker registry for a rate-limited access.

Signed-off-by: Tomasz Baranski <tbaransk@redhat.com>
2020-08-22 05:52:00 +02:00

379 lines
13 KiB
Go

package utils
import (
"context"
"fmt"
"time"
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"
//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 iso image
TinyCoreQcow2URLRateLimit = "http://cdi-file-host.%s:82/tinyCore.qcow2"
// InvalidQcowImagesURL provides a test url for invalid qcow images
InvalidQcowImagesURL = "http://cdi-file-host.%s/invalid_qcow_images/"
// 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"
)
// 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)
}
// NewDataVolumeWithHTTPImport initializes a DataVolume struct with HTTP annotations
func NewDataVolumeWithHTTPImport(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,
},
},
PVC: &k8sv1.PersistentVolumeClaimSpec{
AccessModes: []k8sv1.PersistentVolumeAccessMode{k8sv1.ReadWriteOnce},
Resources: k8sv1.ResourceRequirements{
Requests: k8sv1.ResourceList{
k8sv1.ResourceName(k8sv1.ResourceStorage): resource.MustParse(size),
},
},
},
},
}
}
// 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),
},
},
},
},
}
}
// 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,
},
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,
},
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,
},
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,
},
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),
},
},
},
},
}
}
// 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,
}
}