From 7ec76e9da07935cd68ee9b3fa9d4d513a2f4e199 Mon Sep 17 00:00:00 2001 From: Benny Zlotnik Date: Wed, 28 Feb 2024 12:06:11 +0200 Subject: [PATCH] openstack: Use DataVolume for scratch space Signed-off-by: Benny Zlotnik openstack: fix pod security context Signed-off-by: Benny Zlotnik openstack: switch to DVs Use DVs instead of PVCs to handle WFFC Also, fix permission issues with converter image Signed-off-by: Benny Zlotnik openstack: fix converter tests Signed-off-by: Benny Zlotnik move constants to make them more accessible Signed-off-by: Benny Zlotnik openstack: check inventory status before proceeding We might have a non active status in the inventory for an image, leading to the PVC not being created. Signed-off-by: Benny Zlotnik remove fsGroup and runAsUser Signed-off-by: Benny Zlotnik fix linter issues Signed-off-by: Benny Zlotnik openstack: move inventory check before image removal Signed-off-by: Benny Zlotnik converter: set working security policy Signed-off-by: Benny Zlotnik converter: use generated name for job Signed-off-by: Benny Zlotnik address comments Signed-off-by: Benny Zlotnik --- cmd/image-converter/image-converter.go | 20 +++ pkg/controller/plan/adapter/BUILD.bazel | 4 +- pkg/controller/plan/adapter/base/doc.go | 11 ++ pkg/controller/plan/adapter/converter.go | 169 +++++++++++------- pkg/controller/plan/adapter/converter_test.go | 85 +++++++-- .../plan/adapter/openstack/client.go | 39 ++++ pkg/controller/plan/kubevirt.go | 9 +- 7 files changed, 256 insertions(+), 81 deletions(-) diff --git a/cmd/image-converter/image-converter.go b/cmd/image-converter/image-converter.go index 6bd186646..239d4adb6 100644 --- a/cmd/image-converter/image-converter.go +++ b/cmd/image-converter/image-converter.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "flag" "os/exec" @@ -32,6 +33,8 @@ func convert(srcVolPath, dstVolPath, srcFormat, dstFormat, volumeMode string) er return err } + klog.Info("Copying over source") + // Copy dst over src switch volumeMode { case "Block": @@ -42,8 +45,12 @@ func convert(srcVolPath, dstVolPath, srcFormat, dstFormat, volumeMode string) er case "Filesystem": // Use mv for files as it's faster than qemu-img convert cmd := exec.Command("mv", dstVolPath, srcVolPath) + var stderr bytes.Buffer + cmd.Stderr = &stderr // Capture stderr + klog.Info("Executing command: ", cmd.String()) err := cmd.Run() if err != nil { + klog.Error(stderr.String()) return err } } @@ -68,10 +75,23 @@ func qemuimgConvert(srcVolPath, dstVolPath, srcFormat, dstFormat string) error { return err } + stderr, err := cmd.StderrPipe() + if err != nil { + return err + } + if err := cmd.Start(); err != nil { return err } + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + line := scanner.Text() + klog.Error(line) + } + }() + scanner := bufio.NewScanner(stdout) for scanner.Scan() { line := scanner.Text() diff --git a/pkg/controller/plan/adapter/BUILD.bazel b/pkg/controller/plan/adapter/BUILD.bazel index 9f57d613d..879faea7d 100644 --- a/pkg/controller/plan/adapter/BUILD.bazel +++ b/pkg/controller/plan/adapter/BUILD.bazel @@ -22,9 +22,9 @@ go_library( "//pkg/lib/logging", "//vendor/k8s.io/api/batch/v1:batch", "//vendor/k8s.io/api/core/v1:core", - "//vendor/k8s.io/apimachinery/pkg/api/errors", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", "//vendor/k8s.io/utils/ptr", + "//vendor/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1", "//vendor/sigs.k8s.io/controller-runtime/pkg/client", ], ) @@ -46,6 +46,8 @@ go_test( "//vendor/k8s.io/api/core/v1:core", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:meta", "//vendor/k8s.io/apimachinery/pkg/runtime", + "//vendor/k8s.io/apimachinery/pkg/types", + "//vendor/kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1", "//vendor/sigs.k8s.io/controller-runtime/pkg/client/fake", ], ) diff --git a/pkg/controller/plan/adapter/base/doc.go b/pkg/controller/plan/adapter/base/doc.go index a8bd9b26f..bbeb4c0a2 100644 --- a/pkg/controller/plan/adapter/base/doc.go +++ b/pkg/controller/plan/adapter/base/doc.go @@ -22,6 +22,17 @@ const ( // Set the source format of the PVC for the conversion later AnnSourceFormat = "forklift.konveyor.io/source-format" + + // Set the source PVC of the conversion, used on the DV for filtering + AnnConversionSourcePVC = "forklift.konveyor.io/conversionSourcePVC" + + // CDI + + // Causes the importer pod to be retained after import. + AnnRetainAfterCompletion = "cdi.kubevirt.io/storage.pod.retainAfterCompletion" + + // DV immediate bind to WaitForFirstConsumer storage class + AnnBindImmediate = "cdi.kubevirt.io/storage.bind.immediate.requested" ) var VolumePopulatorNotSupportedError = liberr.New("provider does not support volume populators") diff --git a/pkg/controller/plan/adapter/converter.go b/pkg/controller/plan/adapter/converter.go index e2c977f0d..fa61ce6b6 100644 --- a/pkg/controller/plan/adapter/converter.go +++ b/pkg/controller/plan/adapter/converter.go @@ -4,15 +4,16 @@ import ( "context" "fmt" + planbase "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/base" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" "github.com/konveyor/forklift-controller/pkg/controller/provider/web/base" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "github.com/konveyor/forklift-controller/pkg/lib/logging" batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -46,26 +47,26 @@ func (c *Converter) ConvertPVCs(pvcs []*v1.PersistentVolumeClaim, srcFormat srcF continue } - scratchPVC, err := c.ensureScratchPVC(pvc) + scratchDV, err := c.ensureScratchDV(pvc) if err != nil { return false, err } - switch scratchPVC.Status.Phase { - case v1.ClaimBound: - c.Log.Info("Scratch PVC bound", "pvc", scratchPVC.Name) - case v1.ClaimPending: - c.Log.Info("Scratch PVC pending", "pvc", scratchPVC.Name) + switch scratchDV.Status.Phase { + case cdi.ImportScheduled, cdi.Pending: + c.Log.Info("Scratch DV is not ready", "dv", scratchDV.Name, "status", scratchDV.Status.Phase) return false, nil - case v1.ClaimLost: - c.Log.Info("Scratch PVC lost", "pvc", scratchPVC.Name) - return false, liberr.New("scratch pvc lost") + case cdi.ImportInProgress: + c.Log.Info("Scratch DV import in progress", "dv", scratchDV.Name) + return false, nil + case cdi.Succeeded: + c.Log.Info("Scratch DV is ready", "dv", scratchDV.Name) default: - c.Log.Info("Scratch PVC status is unknown", "pvc", scratchPVC.Name, "status", scratchPVC.Status.Phase) + c.Log.Info("Scratch DV is not ready", "dv", scratchDV.Name, "status", scratchDV.Status.Phase) return false, nil } - convertJob, err := c.ensureJob(pvc, srcFormat(pvc), dstFormat) + convertJob, err := c.ensureJob(pvc, scratchDV, srcFormat(pvc), dstFormat) if err != nil { return false, err } @@ -75,17 +76,12 @@ func (c *Converter) ConvertPVCs(pvcs []*v1.PersistentVolumeClaim, srcFormat srcF switch condition.Type { case batchv1.JobComplete: c.Log.Info("Convert job completed", "pvc", pvc.Name) - - // Delete scrach PVC - err = c.Destination.Client.Delete(context.Background(), scratchPVC, &client.DeleteOptions{}) - if err != nil { - c.Log.Error(err, "Failed to delete scratch PVC", "pvc", scratchPVC.Name) - } - + c.deleteScratchDV(scratchDV) return true, nil case batchv1.JobFailed: if convertJob.Status.Failed >= 3 { + c.deleteScratchDV(scratchDV) return true, liberr.New("convert job failed") } } @@ -99,31 +95,51 @@ func (c *Converter) ConvertPVCs(pvcs []*v1.PersistentVolumeClaim, srcFormat srcF return false, nil } -func (c *Converter) ensureJob(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat string) (*batchv1.Job, error) { - jobName := getJobName(pvc, "convert") - job := &batchv1.Job{} - err := c.Destination.Client.Get(context.Background(), client.ObjectKey{Name: jobName, Namespace: pvc.Namespace}, job) +func (c *Converter) deleteScratchDV(scratchDV *cdi.DataVolume) { + err := c.Destination.Client.Delete(context.Background(), scratchDV) + if err != nil { + c.Log.Error(err, "Failed to delete scratch DV", "DV", scratchDV.Name) + } +} + +func (c *Converter) ensureJob(pvc *v1.PersistentVolumeClaim, dv *cdi.DataVolume, srcFormat, dstFormat string) (*batchv1.Job, error) { + // Find existing job by label + jobList := &batchv1.JobList{} + label := client.MatchingLabels{planbase.AnnConversionSourcePVC: pvc.Name} + err := c.Destination.Client.List(context.Background(), jobList, client.InNamespace(pvc.Namespace), label) + if err != nil { + return nil, err + } + + if len(jobList.Items) == 1 { + c.Log.Info("Found convert job", "job", jobList.Items[0].Name) + return &jobList.Items[0], nil + } else if len(jobList.Items) > 1 { + return nil, liberr.New("multiple convert jobs found for pvc", "pvc", pvc.Name) + } + + // Job doesn't exist, create it + job := createConvertJob(pvc, dv, srcFormat, dstFormat, c.Labels) + c.Log.Info("Creating convert job", "pvc", pvc.Name, "srcFormat", srcFormat, "dstFormat", dstFormat) + err = c.Destination.Client.Create(context.Background(), job) if err != nil { - if k8serr.IsNotFound(err) { - c.Log.Info("Converting PVC format", "pvc", pvc.Name, "srcFormat", srcFormat, "dstFormat", dstFormat) - job := createConvertJob(pvc, srcFormat, dstFormat, c.Labels) - err = c.Destination.Client.Create(context.Background(), job, &client.CreateOptions{}) - if err != nil { - return nil, err - } - } return nil, err } - return job, err + return job, nil } -func createConvertJob(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat string, labels map[string]string) *batchv1.Job { +func createConvertJob(pvc *v1.PersistentVolumeClaim, dv *cdi.DataVolume, srcFormat, dstFormat string, labels map[string]string) *batchv1.Job { + if labels == nil { + labels = make(map[string]string) + } + + labels[planbase.AnnConversionSourcePVC] = pvc.Name return &batchv1.Job{ ObjectMeta: meta.ObjectMeta{ - Name: fmt.Sprintf("convert-%s", pvc.Name), - Namespace: pvc.Namespace, - Labels: labels, + GenerateName: fmt.Sprintf("convert-%s-", pvc.Name), + Namespace: pvc.Namespace, + Labels: labels, }, Spec: batchv1.JobSpec{ BackoffLimit: ptr.To(int32(3)), @@ -131,10 +147,12 @@ func createConvertJob(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat string Template: v1.PodTemplateSpec{ Spec: v1.PodSpec{ SecurityContext: &v1.PodSecurityContext{ + // Since we do not have RunAsUser and FSGroup, the pod will fail in the default namespace + // as it would not be assigned these automatically + RunAsNonRoot: ptr.To(true), SeccompProfile: &v1.SeccompProfile{ Type: v1.SeccompProfileTypeRuntimeDefault, }, - FSGroup: ptr.To(int64(107)), }, RestartPolicy: v1.RestartPolicyNever, Containers: []v1.Container{ @@ -153,7 +171,7 @@ func createConvertJob(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat string Name: "target", VolumeSource: v1.VolumeSource{ PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{ - ClaimName: getScratchPVCName(pvc), + ClaimName: dv.Name, }, }, }, @@ -186,8 +204,6 @@ func makeConversionContainer(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat Image: base.Settings.VirtV2vImageCold, SecurityContext: &v1.SecurityContext{ AllowPrivilegeEscalation: ptr.To(false), - RunAsNonRoot: ptr.To(true), - RunAsUser: ptr.To(int64(107)), Capabilities: &v1.Capabilities{ Drop: []v1.Capability{"ALL"}, }, @@ -230,42 +246,67 @@ func makeConversionContainer(pvc *v1.PersistentVolumeClaim, srcFormat, dstFormat return container } -func (c *Converter) ensureScratchPVC(sourcePVC *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) { - scratchPVC := &v1.PersistentVolumeClaim{} - err := c.Destination.Client.Get(context.Background(), client.ObjectKey{Name: getScratchPVCName(sourcePVC), Namespace: sourcePVC.Namespace}, scratchPVC) +func (c *Converter) ensureScratchDV(sourcePVC *v1.PersistentVolumeClaim) (*cdi.DataVolume, error) { + dvList := &cdi.DataVolumeList{} + label := client.MatchingLabels{planbase.AnnConversionSourcePVC: sourcePVC.Name} + err := c.Destination.Client.List(context.Background(), dvList, client.InNamespace(sourcePVC.Namespace), label) if err != nil { - if k8serr.IsNotFound(err) { - scratchPVC := makeScratchPVC(sourcePVC) - c.Log.Info("Scratch pvc doesn't exist, creating...", "pvc", sourcePVC.Name) - err = c.Destination.Client.Create(context.Background(), scratchPVC, &client.CreateOptions{}) - } return nil, err } - return scratchPVC, nil -} + if len(dvList.Items) == 1 { + c.Log.Info("Found DV", "dv", dvList.Items[0].Name) + return &dvList.Items[0], nil + } else if len(dvList.Items) > 1 { + return nil, liberr.New("multiple scratch DVs found for pvc", "pvc", sourcePVC.Name) + } -func getScratchPVCName(pvc *v1.PersistentVolumeClaim) string { - return fmt.Sprintf("%s-scratch", pvc.Name) + // DV doesn't exist, create it + scratchDV := makeScratchDV(sourcePVC) + c.Log.Info("DV doesn't exist, creating", "dv", scratchDV.Name) + err = c.Destination.Client.Create(context.Background(), scratchDV) + if err != nil { + return nil, err + } + + return scratchDV, nil } -func makeScratchPVC(pvc *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim { +func makeScratchDV(pvc *v1.PersistentVolumeClaim) *cdi.DataVolume { size := pvc.Spec.Resources.Requests[v1.ResourceStorage] - return &v1.PersistentVolumeClaim{ + annotations := make(map[string]string) + annotations[planbase.AnnBindImmediate] = "true" + annotations["migration"] = pvc.Annotations["migration"] + annotations["vmID"] = pvc.Annotations["vmID"] + + labels := pvc.Labels + if labels == nil { + labels = make(map[string]string) + } + + labels[planbase.AnnConversionSourcePVC] = pvc.Name + + return &cdi.DataVolume{ ObjectMeta: meta.ObjectMeta{ - Name: getScratchPVCName(pvc), - Namespace: pvc.Namespace, - Labels: pvc.Labels, + GenerateName: fmt.Sprintf("scratch-dv-%s-", pvc.Name), + Namespace: pvc.Namespace, + Annotations: annotations, + Labels: labels, }, - Spec: v1.PersistentVolumeClaimSpec{ - AccessModes: pvc.Spec.AccessModes, - VolumeMode: pvc.Spec.VolumeMode, - Resources: v1.ResourceRequirements{ - Requests: v1.ResourceList{ - v1.ResourceStorage: size, + Spec: cdi.DataVolumeSpec{ + Source: &cdi.DataVolumeSource{ + Blank: &cdi.DataVolumeBlankImage{}, + }, + Storage: &cdi.StorageSpec{ + VolumeMode: pvc.Spec.VolumeMode, + AccessModes: pvc.Spec.AccessModes, + StorageClassName: pvc.Spec.StorageClassName, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceStorage: size, + }, }, }, - StorageClassName: pvc.Spec.StorageClassName, }, } } diff --git a/pkg/controller/plan/adapter/converter_test.go b/pkg/controller/plan/adapter/converter_test.go index 0aac32386..ee66c50a6 100644 --- a/pkg/controller/plan/adapter/converter_test.go +++ b/pkg/controller/plan/adapter/converter_test.go @@ -1,6 +1,8 @@ package adapter import ( + "context" + "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/base" plancontext "github.com/konveyor/forklift-controller/pkg/controller/plan/context" "github.com/konveyor/forklift-controller/pkg/lib/logging" @@ -8,9 +10,11 @@ import ( . "github.com/onsi/gomega" batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" + cdi "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -39,18 +43,13 @@ var _ = Describe("Converter tests", func() { }, } - scratchPVC := &v1.PersistentVolumeClaim{ - ObjectMeta: meta.ObjectMeta{ - Name: getScratchPVCName(qcow2PVC), - Namespace: pvcNamespace, - }, - } - scratchPVC.Status.Phase = v1.ClaimBound - convertJob := &batchv1.Job{ ObjectMeta: meta.ObjectMeta{ Name: getJobName(qcow2PVC, "convert"), Namespace: pvcNamespace, + Labels: map[string]string{ + base.AnnConversionSourcePVC: pvcName, + }, }, } @@ -59,7 +58,7 @@ var _ = Describe("Converter tests", func() { } It("Should not be ready if job is not ready", func() { - converter = createFakeConverter(qcow2PVC, scratchPVC, convertJob) + converter = createFakeConverter(qcow2PVC, convertJob) ready, err := converter.ConvertPVCs([]*v1.PersistentVolumeClaim{qcow2PVC}, srcFormatFn, "raw") Expect(err).ToNot(HaveOccurred()) Expect(ready).To(BeFalse()) @@ -69,19 +68,85 @@ var _ = Describe("Converter tests", func() { convertJob.Status.Conditions = append(convertJob.Status.Conditions, batchv1.JobCondition{ Type: batchv1.JobComplete, }) - converter = createFakeConverter(qcow2PVC, scratchPVC, convertJob) + + dv := &cdi.DataVolume{ + ObjectMeta: meta.ObjectMeta{ + Name: "test-dv", + Namespace: pvcNamespace, + Labels: map[string]string{ + base.AnnConversionSourcePVC: pvcName, + }, + }, + } + + dv.Status.Phase = cdi.Succeeded + + converter = createFakeConverter(qcow2PVC, convertJob, dv) ready, err := converter.ConvertPVCs([]*v1.PersistentVolumeClaim{qcow2PVC}, srcFormatFn, "raw") Expect(err).ToNot(HaveOccurred()) Expect(ready).To(BeTrue()) }) + + It("Should create job if it does not exist", func() { + converter = createFakeConverter(qcow2PVC) + dv := &cdi.DataVolume{ + ObjectMeta: meta.ObjectMeta{ + Name: "test-dv", + Namespace: pvcNamespace, + }, + } + job, err := converter.ensureJob(qcow2PVC, dv, srcFormatFn(qcow2PVC), "raw") + Expect(err).ToNot(HaveOccurred()) + Expect(job).ToNot(BeNil()) + }) + + It("Should create scratch DV if it does not exist", func() { + converter = createFakeConverter(qcow2PVC) + dv, err := converter.ensureScratchDV(qcow2PVC) + Expect(err).ToNot(HaveOccurred()) + Expect(dv).ToNot(BeNil()) + }) + + It("Should remove scratch DV if the job failed", func() { + dv := &cdi.DataVolume{ + ObjectMeta: meta.ObjectMeta{ + Name: "test-dv", + Namespace: pvcNamespace, + Labels: map[string]string{ + base.AnnConversionSourcePVC: pvcName, + }, + }, + Status: cdi.DataVolumeStatus{ + Phase: cdi.Succeeded, + }, + } + + convertJob.Status.Conditions = append(convertJob.Status.Conditions, batchv1.JobCondition{Status: "False", Type: batchv1.JobFailed}) + convertJob.Status.Failed = 3 + + converter = createFakeConverter(qcow2PVC, convertJob, dv) + + _, err := converter.ConvertPVCs([]*v1.PersistentVolumeClaim{qcow2PVC}, srcFormatFn, "raw") + Expect(err).ToNot(HaveOccurred()) + + // Check if scratch DV is removed + err = converter.Destination.Client.Get(context.TODO(), types.NamespacedName{Name: dv.Name, Namespace: dv.Namespace}, dv) + Expect(err).To(HaveOccurred()) + }) }) }) func createFakeConverter(objects ...runtime.Object) *Converter { + scheme := runtime.NewScheme() + _ = cdi.AddToScheme(scheme) + _ = v1.AddToScheme(scheme) + _ = batchv1.AddToScheme(scheme) + objs := []runtime.Object{} objs = append(objs, objects...) client := fake.NewClientBuilder(). + WithScheme(scheme). WithRuntimeObjects(objs...). Build() diff --git a/pkg/controller/plan/adapter/openstack/client.go b/pkg/controller/plan/adapter/openstack/client.go index 865caf62a..2d03f1939 100644 --- a/pkg/controller/plan/adapter/openstack/client.go +++ b/pkg/controller/plan/adapter/openstack/client.go @@ -192,14 +192,17 @@ func (r *Client) PreTransferActions(vmRef ref.Ref) (ready bool, err error) { if err != nil || !ready { return } + ready, err = r.ensureImagesFromVolumesReady(vm) if err != nil || ready { return } + err = r.ensureSnapshotsFromVolumes(vm) if err != nil { return } + err = r.ensureVolumesFromSnapshots(vm) return } @@ -845,6 +848,14 @@ func (r *Client) ensureImagesFromVolumesReady(vm *libclient.VM) (ready bool, err return case imageReady: originalVolumeID := image.Properties[forkliftPropertyOriginalVolumeID].(string) + ready, err := r.isImageReadyInInventory(vm, &image) + if err != nil { + return false, liberr.Wrap(err) + } + if !ready { + return false, nil + } + go func() { // executing this in a non-blocking mode err := r.cleanup(vm, originalVolumeID) @@ -854,12 +865,40 @@ func (r *Client) ensureImagesFromVolumesReady(vm *libclient.VM) (ready bool, err } }() } + } + ready = true r.Log.Info("all steps finished!", "vm", vm.Name) return } +func (r *Client) isImageReadyInInventory(vm *libclient.VM, image *libclient.Image) (ready bool, err error) { + // Check that the inventory is synchronized with the images + inventoryImage := &model.Image{} + err = r.Context.Source.Inventory.Find(inventoryImage, ref.Ref{ID: image.ID}) + if err != nil { + if errors.As(err, &model.NotFoundError{}) { + err = nil + r.Log.Info("the image does not exist in the inventory, waiting...", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + return + } + err = liberr.Wrap(err) + r.Log.Error(err, "trying to find the image in the inventory", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + return + } + + if inventoryImage.Status != string(ImageStatusActive) { + r.Log.Info("the image is not ready in the inventory, waiting...", + "vm", vm.Name, "image", image.Name, "properties", image.Properties) + return + } + + return true, nil +} + func (r *Client) ensureImageFromVolumeReady(vm *libclient.VM, image *libclient.Image) (ready bool, err error) { switch image.Status { case ImageStatusQueued, ImageStatusUploading, ImageStatusSaving: diff --git a/pkg/controller/plan/kubevirt.go b/pkg/controller/plan/kubevirt.go index af36f8a0b..30c22ae56 100644 --- a/pkg/controller/plan/kubevirt.go +++ b/pkg/controller/plan/kubevirt.go @@ -16,6 +16,7 @@ import ( "time" api "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" + planbase "github.com/konveyor/forklift-controller/pkg/controller/plan/adapter/base" template "github.com/openshift/api/template/v1" "github.com/openshift/library-go/pkg/template/generator" "github.com/openshift/library-go/pkg/template/templateprocessing" @@ -50,8 +51,6 @@ import ( const ( // Transfer network annotation (value=network-attachment-definition name) AnnDefaultNetwork = "v1.multus-cni.io/default-network" - // Causes the importer pod to be retained after import. - AnnRetainAfterCompletion = "cdi.kubevirt.io/storage.pod.retainAfterCompletion" // Contains validations for a Kubevirt VM. Needs to be removed when // creating a VM from a template. AnnKubevirtValidations = "vm.kubevirt.io/validations" @@ -63,8 +62,6 @@ const ( AnnOriginalID = "original-ID" // DV deletion on completion AnnDeleteAfterCompletion = "cdi.kubevirt.io/storage.deleteAfterCompletion" - // DV immediate bind to WaitForFirstConsumer storage class - AnnBindImmediate = "cdi.kubevirt.io/storage.bind.immediate.requested" // Max Length for vm name NameMaxLength = 63 VddkVolumeName = "vddk-vol-mount" @@ -1170,14 +1167,14 @@ func (r *KubeVirt) dataVolumes(vm *plan.VMStatus, secret *core.Secret, configMap annotations := r.vmLabels(vm.Ref) if !r.Plan.Spec.Warm || Settings.RetainPrecopyImporterPods { - annotations[AnnRetainAfterCompletion] = "true" + annotations[planbase.AnnRetainAfterCompletion] = "true" } if r.Plan.Spec.TransferNetwork != nil { annotations[AnnDefaultNetwork] = path.Join( r.Plan.Spec.TransferNetwork.Namespace, r.Plan.Spec.TransferNetwork.Name) } if r.Plan.Spec.Warm || !r.Destination.Provider.IsHost() || r.Plan.IsSourceProviderOCP() { - annotations[AnnBindImmediate] = "true" + annotations[planbase.AnnBindImmediate] = "true" } // Do not delete the DV when the import completes as we check the DV to get the current // disk transfer status.