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

Users don't want 👽 resources in clusters, and we should also be able to tell if were part of a broader installation. Note: - Operator created resources were handled in https://github.com/kubevirt/controller-lifecycle-operator-sdk/pull/18 as these labels will be common to all resources deployed by the HCO. - Now that the controller is guaranteed to have the labels, we can set env vars that reference the label values (fieldRef) to spare calling GET on the CR in the controllers. (thanks mhenriks). Signed-off-by: Alex Kalenyuk <akalenyu@redhat.com>
260 lines
7.1 KiB
Go
260 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/pkg/apis/core/v1beta1"
|
|
"kubevirt.io/containerized-data-importer/pkg/common"
|
|
cdicontroller "kubevirt.io/containerized-data-importer/pkg/controller"
|
|
"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 := cdicontroller.GetPodsUsingPVCs(h.reconciler.Client, dv.Namespace, sets.NewString(cdicontroller.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": cdicontroller.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, cdicontroller.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[cdicontroller.AnnPopulatedFor]
|
|
if ok {
|
|
delete(pvc.Annotations, cdicontroller.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[cdicontroller.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[cdicontroller.AnnPopulatedFor] = dvName
|
|
|
|
return h.reconciler.updateResource(ot, pvc)
|
|
}
|