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

* touch up zero restoresize snapshot Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * clone populator only supports PVC source now snapshot coming soon Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * more unit tests Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * unit test for clone populator Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * func tests for clone populator Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * move clone populator cleanup function to planner other review comments verifier pod should bount readonly Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * add readonly flag to test executor pods synchronize get hash calls Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * increase linter timeout Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * better/explicit readonly support for test pods Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * check pv for driver info before looking up storageclass as it may not exist Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * addressed review comments Signed-off-by: Michael Henriksen <mhenriks@redhat.com> * chooseStrategy shoud generate more events Signed-off-by: Michael Henriksen <mhenriks@redhat.com> --------- Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
269 lines
7.1 KiB
Go
269 lines
7.1 KiB
Go
package transfer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
|
|
"kubevirt.io/containerized-data-importer/pkg/common"
|
|
cc "kubevirt.io/containerized-data-importer/pkg/controller/common"
|
|
"kubevirt.io/containerized-data-importer/pkg/util"
|
|
)
|
|
|
|
type dataVolumeTransferHandler struct {
|
|
objectTransferHandler
|
|
}
|
|
|
|
func (h *dataVolumeTransferHandler) ReconcilePending(ot *cdiv1.ObjectTransfer) (time.Duration, error) {
|
|
dv := &cdiv1.DataVolume{}
|
|
dvExists, err := h.reconciler.getSourceResource(ot, dv)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
if !dvExists {
|
|
// will reconcile again when dv is created/updated
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "No source", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
if dv.Status.Phase != cdiv1.Succeeded {
|
|
// will reconcile again when dv is updated
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "Source not populated", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
pods, err := cc.GetPodsUsingPVCs(context.TODO(), h.reconciler.Client, dv.Namespace, sets.New(getDataVolumeClaimName(dv)), false)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
if len(pods) > 0 {
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "Pods using DataVolume PVC", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return defaultRequeue, nil
|
|
}
|
|
|
|
dv2 := dv.DeepCopy()
|
|
dv2.Status = cdiv1.DataVolumeStatus{}
|
|
data := map[string]string{
|
|
"pvcName": getDataVolumeClaimName(dv),
|
|
}
|
|
|
|
return 0, h.reconciler.pendingHelper(ot, dv2, data)
|
|
}
|
|
|
|
func (h *dataVolumeTransferHandler) ReconcileRunning(ot *cdiv1.ObjectTransfer) (time.Duration, error) {
|
|
dv := &cdiv1.DataVolume{}
|
|
dvExists, err := h.reconciler.getSourceResource(ot, dv)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if dvExists {
|
|
return h.deleteDataVolume(ot, dv)
|
|
}
|
|
|
|
pvcTransferName := fmt.Sprintf("pvc-transfer-%s", ot.UID)
|
|
|
|
pvcTransfer := &cdiv1.ObjectTransfer{}
|
|
pvcTransferExists, err := h.reconciler.getResource("", pvcTransferName, pvcTransfer)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
target := &cdiv1.DataVolume{}
|
|
targetExists, err := h.reconciler.getTargetResource(ot, target)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
pvcName := ot.Status.Data["pvcName"]
|
|
|
|
if !targetExists && !pvcTransferExists {
|
|
targetNamespace := getTransferTargetNamespace(ot)
|
|
targetName := getTransferTargetName(ot)
|
|
|
|
pvcTransfer = &cdiv1.ObjectTransfer{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: pvcTransferName,
|
|
Labels: map[string]string{
|
|
common.CDILabelKey: common.CDILabelValue,
|
|
common.CDIComponentLabel: "",
|
|
},
|
|
},
|
|
Spec: cdiv1.ObjectTransferSpec{
|
|
Source: cdiv1.TransferSource{
|
|
Kind: "PersistentVolumeClaim",
|
|
Namespace: ot.Spec.Source.Namespace,
|
|
Name: pvcName,
|
|
},
|
|
Target: cdiv1.TransferTarget{
|
|
Namespace: &targetNamespace,
|
|
Name: &targetName,
|
|
},
|
|
ParentName: &ot.Name,
|
|
},
|
|
}
|
|
util.SetRecommendedLabels(pvcTransfer, h.reconciler.InstallerLabels, "cdi-controller")
|
|
|
|
if err := h.reconciler.Client.Create(context.TODO(), pvcTransfer); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
pvcTransferExists = true
|
|
}
|
|
|
|
if pvcTransferExists {
|
|
if pvcTransfer.Status.Phase != cdiv1.ObjectTransferComplete {
|
|
ot.Status.Phase = cdiv1.ObjectTransferRunning
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "PVC transfer in progress", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
pvc := &corev1.PersistentVolumeClaim{}
|
|
pvcExists, err := h.reconciler.getTargetResource(pvcTransfer, pvc)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
if !pvcExists {
|
|
ot.Status.Phase = cdiv1.ObjectTransferError
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "Transferred PVC does not exist", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
if err := h.addPopulatedAnnotation(ot, pvc); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
}
|
|
|
|
if !targetExists {
|
|
target = &cdiv1.DataVolume{}
|
|
if err := h.reconciler.createObjectTransferTarget(ot, target, nil); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
}
|
|
|
|
if target.Status.Phase != cdiv1.Succeeded {
|
|
ot.Status.Phase = cdiv1.ObjectTransferRunning
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "Waiting for target DataVolume", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
if pvcTransferExists && pvcTransfer.DeletionTimestamp == nil {
|
|
if err := h.reconciler.Client.Delete(context.TODO(), pvcTransfer); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
}
|
|
|
|
ot.Status.Phase = cdiv1.ObjectTransferComplete
|
|
ot.Status.Data = nil
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionTrue, "Transfer complete", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
func (h *dataVolumeTransferHandler) deleteDataVolume(ot *cdiv1.ObjectTransfer, dv *cdiv1.DataVolume) (time.Duration, error) {
|
|
pvc := &corev1.PersistentVolumeClaim{}
|
|
pvcExists, err := h.reconciler.getResource(dv.Namespace, getDataVolumeClaimName(dv), pvc)
|
|
if err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
if !pvcExists {
|
|
if err := h.reconciler.setAndUpdateCompleteCondition(ot, corev1.ConditionFalse, "Source DV has no PVC", ""); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return 0, nil
|
|
}
|
|
|
|
idx := -1
|
|
for i, o := range pvc.OwnerReferences {
|
|
if o.Kind == "DataVolume" &&
|
|
o.Name == dv.Name &&
|
|
o.UID == dv.UID {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if idx >= 0 {
|
|
os := pvc.OwnerReferences
|
|
pvc.OwnerReferences = append(os[0:idx], os[idx+1:]...)
|
|
}
|
|
|
|
_, ok := pvc.Annotations[cc.AnnPopulatedFor]
|
|
if ok {
|
|
delete(pvc.Annotations, cc.AnnPopulatedFor)
|
|
}
|
|
|
|
if idx >= 0 || ok {
|
|
if err := h.reconciler.updateResource(ot, pvc); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
return time.Second, h.reconciler.setCompleteConditionRunning(ot)
|
|
}
|
|
|
|
if err := h.reconciler.Client.Delete(context.TODO(), dv); err != nil {
|
|
return 0, h.reconciler.setCompleteConditionError(ot, err)
|
|
}
|
|
|
|
return 0, h.reconciler.setCompleteConditionRunning(ot)
|
|
}
|
|
|
|
func (h *dataVolumeTransferHandler) addPopulatedAnnotation(ot *cdiv1.ObjectTransfer, pvc *corev1.PersistentVolumeClaim) error {
|
|
dvName := getTransferTargetName(ot)
|
|
|
|
if v, ok := pvc.Annotations[cc.AnnPopulatedFor]; ok {
|
|
if v == dvName {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("PVC populated for a different DataVolume")
|
|
}
|
|
|
|
if pvc.Annotations == nil {
|
|
pvc.Annotations = make(map[string]string)
|
|
}
|
|
|
|
pvc.Annotations[cc.AnnPopulatedFor] = dvName
|
|
|
|
return h.reconciler.updateResource(ot, pvc)
|
|
}
|
|
|
|
func getDataVolumeClaimName(dv *cdiv1.DataVolume) string {
|
|
pvcName, ok := dv.Annotations[cc.AnnPopulatedFor]
|
|
if ok {
|
|
return pvcName
|
|
}
|
|
|
|
return dv.Name
|
|
}
|