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

* 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>
379 lines
13 KiB
Go
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,
|
|
}
|
|
}
|