Skip to content

Commit e4c04d7

Browse files
committed
MTV-4150 | disk transfer completed when importer pod is pending
Resolves: MTV-4150 The Disk Transfer step was being marked as completed when a DataVolume reached Succeeded phase, even if the PVCs were not bound or the importer pod was still pending. This could happen when CSI drivers fail to provision volumes (e.g., nfs.csi.k8s.io issues). This fix adds validation before marking a DataVolume as completed: - Verifies the main PVC is bound - Verifies the prime PVC (if it exists) is bound - Verifies the importer pod (if it exists) is not pending If any of these checks fail, the task is marked as pending instead of completed, preventing Disk Transfer from showing as completed prematurely. Also adds a default case to handle unknown/empty DataVolume phases. Signed-off-by: Gwen Casey <gcasey@redhat.com>
1 parent 77b8d0d commit e4c04d7

1 file changed

Lines changed: 111 additions & 2 deletions

File tree

pkg/controller/plan/migration.go

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,8 +1656,104 @@ func (r *Migration) updateCopyProgress(vm *plan.VMStatus, step *plan.Step) (err
16561656
conditions := dv.Conditions()
16571657
switch dv.Status.Phase {
16581658
case cdi.Succeeded, cdi.Paused:
1659-
completed++
1660-
r.setTaskCompleted(task)
1659+
// Before marking as completed, verify that the PVC is bound and
1660+
// the importer pod (if it exists) is not pending. This prevents
1661+
// marking disk transfer as completed when the importer pod is
1662+
// still waiting for PVC provisioning (e.g., due to CSI driver issues).
1663+
canMarkCompleted := true
1664+
if dv.Status.ClaimName != "" {
1665+
pvc := &core.PersistentVolumeClaim{}
1666+
err = r.Destination.Client.Get(context.TODO(), types.NamespacedName{
1667+
Namespace: r.Plan.Spec.TargetNamespace,
1668+
Name: dv.Status.ClaimName,
1669+
}, pvc)
1670+
if err != nil {
1671+
log.Error(
1672+
err,
1673+
"Could not get PVC for DataVolume to verify completion.",
1674+
"vm",
1675+
vm.String(),
1676+
"dv",
1677+
path.Join(dv.Namespace, dv.Name))
1678+
canMarkCompleted = false
1679+
} else {
1680+
// Check if PVC is bound
1681+
if pvc.Status.Phase != core.ClaimBound {
1682+
log.Info(
1683+
"DataVolume is Succeeded but PVC is not bound, not marking as completed.",
1684+
"vm",
1685+
vm.String(),
1686+
"dv",
1687+
path.Join(dv.Namespace, dv.Name),
1688+
"pvcPhase",
1689+
pvc.Status.Phase)
1690+
canMarkCompleted = false
1691+
} else {
1692+
// Check prime PVC if it exists (importer pod uses prime PVC)
1693+
primePVC := &core.PersistentVolumeClaim{}
1694+
err = r.Destination.Client.Get(context.TODO(), types.NamespacedName{
1695+
Namespace: r.Plan.Spec.TargetNamespace,
1696+
Name: fmt.Sprintf("prime-%s", pvc.UID),
1697+
}, primePVC)
1698+
if err != nil {
1699+
if !k8serr.IsNotFound(err) {
1700+
log.Error(
1701+
err,
1702+
"Could not get prime PVC for DataVolume to verify completion.",
1703+
"vm",
1704+
vm.String(),
1705+
"dv",
1706+
path.Join(dv.Namespace, dv.Name))
1707+
// If prime PVC doesn't exist, that's okay - importer may not be using it
1708+
}
1709+
} else if primePVC.Status.Phase != core.ClaimBound {
1710+
log.Info(
1711+
"DataVolume is Succeeded but prime PVC is not bound, not marking as completed.",
1712+
"vm",
1713+
vm.String(),
1714+
"dv",
1715+
path.Join(dv.Namespace, dv.Name),
1716+
"primePVCPhase",
1717+
primePVC.Status.Phase)
1718+
canMarkCompleted = false
1719+
} else {
1720+
// Check if importer pod exists and is not pending
1721+
importer, found, kErr := r.kubevirt.GetImporterPod(*primePVC)
1722+
if kErr != nil {
1723+
log.Error(
1724+
kErr,
1725+
"Could not get CDI importer pod for DataVolume to verify completion.",
1726+
"vm",
1727+
vm.String(),
1728+
"dv",
1729+
path.Join(dv.Namespace, dv.Name))
1730+
// Don't block completion if we can't check the pod
1731+
} else if found && importer != nil {
1732+
if importer.Status.Phase == core.PodPending {
1733+
log.Info(
1734+
"DataVolume is Succeeded but importer pod is pending, not marking as completed.",
1735+
"vm",
1736+
vm.String(),
1737+
"dv",
1738+
path.Join(dv.Namespace, dv.Name),
1739+
"pod",
1740+
path.Join(importer.Namespace, importer.Name))
1741+
canMarkCompleted = false
1742+
}
1743+
}
1744+
}
1745+
}
1746+
}
1747+
}
1748+
if canMarkCompleted {
1749+
completed++
1750+
r.setTaskCompleted(task)
1751+
} else {
1752+
// Mark as pending instead of completed
1753+
pending++
1754+
task.Phase = api.StepPending
1755+
task.Reason = "DataVolume shows Succeeded but PVC is not bound or importer pod is pending"
1756+
}
16611757
case cdi.Pending, cdi.ImportScheduled:
16621758
pending++
16631759
task.Phase = api.StepPending
@@ -1760,6 +1856,19 @@ func (r *Migration) updateCopyProgress(vm *plan.VMStatus, step *plan.Step) (err
17601856
msg, _ := terminationMessage(importer)
17611857
task.AddError(msg)
17621858
}
1859+
default:
1860+
// Handle unknown/empty phases - treat as pending
1861+
log.Info(
1862+
"DataVolume has unknown or empty phase, treating as pending.",
1863+
"vm",
1864+
vm.String(),
1865+
"dv",
1866+
path.Join(dv.Namespace, dv.Name),
1867+
"phase",
1868+
dv.Status.Phase)
1869+
pending++
1870+
task.Phase = api.StepPending
1871+
task.Reason = fmt.Sprintf("DataVolume phase is unknown or empty: %s", dv.Status.Phase)
17631872
}
17641873
}
17651874
}

0 commit comments

Comments
 (0)