Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-v1.49] Manual fix for clone without source pvc but target pvc already populated #2406

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 26 additions & 18 deletions pkg/controller/datavolume-controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,26 +480,18 @@ func (r *DatavolumeReconciler) Reconcile(_ context.Context, req reconcile.Reques
return reconcile.Result{}, err
}

selectedCloneStrategy, err := r.selectCloneStrategy(datavolume, pvcSpec)
if err != nil {
return reconcile.Result{}, err
}
if selectedCloneStrategy == SmartClone {
r.sccs.StartController()
}

_, dvPrePopulated := datavolume.Annotations[AnnPrePopulated]

if selectedCloneStrategy != NoClone {
return r.reconcileClone(log, datavolume, pvc, pvcSpec, transferName, dvPrePopulated, pvcPopulated, selectedCloneStrategy)
if isClone := datavolume.Spec.Source.PVC != nil; isClone {
return r.reconcileClone(log, datavolume, pvc, pvcSpec, transferName, dvPrePopulated, pvcPopulated)
}

if !dvPrePopulated {
if pvc == nil {
newPvc, err := r.createPvcForDatavolume(log, datavolume, pvcSpec)
if err != nil {
if errQuotaExceeded(err) {
r.updateDataVolumeStatusPhaseWithEvent(cdiv1.Pending, datavolume, nil, selectedCloneStrategy,
r.updateDataVolumeStatusPhaseWithEvent(cdiv1.Pending, datavolume, nil, NoClone,
DataVolumeEvent{
eventType: corev1.EventTypeWarning,
reason: ErrExceededQuota,
Expand Down Expand Up @@ -532,7 +524,7 @@ func (r *DatavolumeReconciler) Reconcile(_ context.Context, req reconcile.Reques

// Finally, we update the status block of the DataVolume resource to reflect the
// current state of the world
return r.reconcileDataVolumeStatus(datavolume, pvc, selectedCloneStrategy)
return r.reconcileDataVolumeStatus(datavolume, pvc, NoClone)
}

func (r *DatavolumeReconciler) reconcileClone(log logr.Logger,
Expand All @@ -541,10 +533,30 @@ func (r *DatavolumeReconciler) reconcileClone(log logr.Logger,
pvcSpec *corev1.PersistentVolumeClaimSpec,
transferName string,
prePopulated bool,
pvcPopulated bool,
selectedCloneStrategy cloneStrategy) (reconcile.Result, error) {
pvcPopulated bool) (reconcile.Result, error) {

_, err := r.findSourcePvc(datavolume)
if err != nil {
if k8serrors.IsNotFound(err) {
brybacki marked this conversation as resolved.
Show resolved Hide resolved
if pvcPopulated {
return r.reconcileDataVolumeStatus(datavolume, pvc, NoClone)
}
r.recorder.Eventf(datavolume, corev1.EventTypeWarning, ErrUnableToClone, "Source pvc %s not found", datavolume.Spec.Source.PVC.Name)
}
return reconcile.Result{}, err
}

selectedCloneStrategy := NoClone
if !prePopulated {
selectedCloneStrategy, err = r.selectCloneStrategy(datavolume, pvcSpec)
if err != nil {
return reconcile.Result{}, err
}

if selectedCloneStrategy == SmartClone {
r.sccs.StartController()
}

if pvc == nil {
if selectedCloneStrategy == SmartClone {
snapshotClassName, _ := r.getSnapshotClassForSmartClone(datavolume, pvcSpec)
Expand Down Expand Up @@ -657,10 +669,6 @@ func (r *DatavolumeReconciler) ensureExtendedToken(pvc *corev1.PersistentVolumeC
}

func (r *DatavolumeReconciler) selectCloneStrategy(datavolume *cdiv1.DataVolume, pvcSpec *corev1.PersistentVolumeClaimSpec) (cloneStrategy, error) {
if datavolume.Spec.Source.PVC == nil {
return NoClone, nil
}

preferredCloneStrategy, err := r.getCloneStrategy(datavolume)
if err != nil {
return NoClone, err
Expand Down
32 changes: 32 additions & 0 deletions pkg/controller/datavolume-controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,38 @@ var _ = Describe("All DataVolume Tests", func() {
Entry("should default to snapshot", nil, nil, cdiv1.CloneStrategySnapshot),
)
})

var _ = Describe("Clone without source", func() {
scName := "testsc"
sc := createStorageClassWithProvisioner(scName, map[string]string{
AnnDefaultStorageClass: "true",
}, map[string]string{}, "csi-plugin")

It("Validate clone already populated without source completes", func() {
dv := newCloneDataVolume("test-dv")
storageProfile := createStorageProfile(scName, nil, filesystemMode)
pvcAnnotations := make(map[string]string)
pvcAnnotations[AnnPopulatedFor] = "test-dv"
pvc := createPvcInStorageClass("test-dv", metav1.NamespaceDefault, &scName, nil, nil, corev1.ClaimBound)
pvc.SetAnnotations(make(map[string]string))
pvc.GetAnnotations()[AnnPopulatedFor] = "test-dv"
reconciler = createDatavolumeReconciler(dv, pvc, storageProfile, sc)

prePopulated := false
pvcPopulated := true
result, err := reconciler.reconcileClone(reconciler.log, dv, pvc, dv.Spec.PVC.DeepCopy(), "", prePopulated, pvcPopulated)
Expect(err).ToNot(HaveOccurred())
err = reconciler.client.Get(context.TODO(), types.NamespacedName{Name: "test-dv", Namespace: metav1.NamespaceDefault}, dv)
Expect(err).ToNot(HaveOccurred())
Expect(dv.Status.ClaimName).To(Equal("test-dv"))
Expect(dv.Status.Phase).To(Equal(cdiv1.Succeeded))
Expect(dv.Annotations[AnnPrePopulated]).To(Equal("test-dv"))
Expect(dv.Annotations[annCloneType]).To(BeEmpty())
Expect(result).To(Equal(reconcile.Result{}))
})

})

var _ = Describe("Get Pod from PVC", func() {
var (
pvc *corev1.PersistentVolumeClaim
Expand Down
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
Expand Down
25 changes: 25 additions & 0 deletions tests/cloner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/rand"
"sigs.k8s.io/controller-runtime/pkg/client"

cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
Expand Down Expand Up @@ -776,6 +777,30 @@ var _ = Describe("all clone tests", func() {
Expect(utils.GetCloneType(f.CdiClient, dataVolume)).To(Equal("csivolumeclone"))
})
})

Context("Clone without a source PVC", func() {
It("Should not clone when PopulatedFor annotation exists", func() {
fsVM := v1.PersistentVolumeMode(v1.PersistentVolumeFilesystem)
targetName := "target" + rand.String(12)

By(fmt.Sprintf("Creating target pvc: %s/%s", f.Namespace.Name, targetName))
targetPvc, err := utils.CreatePVCFromDefinition(f.K8sClient, f.Namespace.Name,
utils.NewPVCDefinition(targetName, "1Gi", map[string]string{controller.AnnPopulatedFor: targetName}, nil))
Expect(err).ToNot(HaveOccurred())
f.ForceBindIfWaitForFirstConsumer(targetPvc)
cloneDV := utils.NewDataVolumeForImageCloningAndStorageSpec(targetName, "1Gi", f.Namespace.Name, "non-existing-source", nil, &fsVM)
_, err = utils.CreateDataVolumeFromDefinition(f.CdiClient, f.Namespace.Name, cloneDV)
Expect(err).ToNot(HaveOccurred())
By("Wait for clone DV Succeeded phase")
err = utils.WaitForDataVolumePhaseWithTimeout(f.CdiClient, f.Namespace.Name, cdiv1.Succeeded, targetName, cloneCompleteTimeout)
Expect(err).ToNot(HaveOccurred())
dv, err := f.CdiClient.CdiV1beta1().DataVolumes(f.Namespace.Name).Get(context.TODO(), targetName, metav1.GetOptions{})
Expect(err).ToNot(HaveOccurred())
_, ok := dv.Annotations["cdi.kubevirt.io/cloneType"]
Expect(ok).To(BeFalse())
})
})

})

var _ = Describe("Validate creating multiple clones of same source Data Volume", func() {
Expand Down