containerized-data-importer/pkg/controller/transfer/pvc_test.go
Michael Henriksen 5195176c16
update to k8s 1.30 libs and controller-runtime 0.18.4 (#3336)
* make deps-update

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* ReourceRequirements -> VolumeResourceRequirements

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* fix calls to controller.Watch()

controller-runtime changed the API!

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* Fix errors with actual openshift/library-go lib

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* make all works now and everything compiles

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* fix "make update-codegen" because generate_groups.sh deprecated

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* run "make generate"

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

* fix transfer unittest because of change to controller-runtime

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>

---------

Signed-off-by: Michael Henriksen <mhenriks@redhat.com>
2024-07-14 20:12:50 +02:00

540 lines
16 KiB
Go

/*
Copyright 2020 The CDI Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package transfer_test
import (
"context"
"encoding/json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1"
)
var _ = Describe("PVC Transfer Tests", func() {
var _ = Describe("PVC Transfer Tests", func() {
It("Should initialize", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferEmpty)
r := createReconciler(xfer)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Finalizers).To(HaveLen(1))
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "Initializing", "")
})
It("Should handle no source", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
r := createReconciler(xfer)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "No source", "")
})
It("Should handle unbound source", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
r := createReconciler(xfer, createUnboundPVC())
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "PVC not bound", "")
})
It("Should handle PVC with finalizer", func() {
f := "snapshot.storage.kubernetes.io/pvc-as-source-protection"
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
pvc := createBoundPVC()
pvc.Finalizers = append(pvc.Finalizers, f)
r := createReconciler(xfer, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "PVC has finalizer: "+f, "")
})
It("Should handle PV not bound", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
pvc := createBoundPVC()
pv := sourcePV()
pv.Spec.ClaimRef = nil
r := createReconciler(xfer, pvc, pv)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "PV not bound", "")
})
It("Should handle pod using PVC", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
pvc := createBoundPVC()
pv := sourcePV()
r := createReconciler(xfer, pvc, pv, createPod(pvc.Name))
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "Pods using PVC", "")
})
It("Should handle another transfer", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
pvc := createBoundPVC()
pvc.Annotations = map[string]string{
"cdi.kubevirt.io/objectTransferName": "baz",
}
r := createReconciler(xfer, sourcePV(), pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferPending))
checkCompleteFalse(xfer, "Source in use by another transfer", "baz")
})
It("Should cancel", func() {
t := metav1.Now()
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
xfer.Finalizers = []string{
"cdi.kubevirt.io/objectTransfer",
}
xfer.DeletionTimestamp = &t
r := createReconciler(xfer)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).To(HaveOccurred())
Expect(errors.IsNotFound(err)).To(BeTrue())
})
It("Should become running", func() {
xfer := pvcTransfer(cdiv1.ObjectTransferPending)
r := createReconciler(xfer, sourcePV(), createBoundPVC())
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
Expect(xfer.Status.Data).To(Equal(pvcTransferRunningWithBoundPVC().Status.Data))
checkCompleteFalse(xfer, "Running", "")
})
It("Should handle missing pv name", func() {
xfer := pvcTransferRunning()
delete(xfer.Status.Data, "pvName")
r := createReconciler(xfer, createBoundPVC())
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferError))
checkCompleteFalse(xfer, "PV name missing", "")
})
It("Should error missing pv", func() {
xfer := pvcTransferRunning()
r := createReconciler(xfer)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).To(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferError))
checkCompleteFalse(xfer, "Error", "persistentvolumes \"source-pv\" not found")
})
It("Should store reclaim", func() {
xfer := pvcTransferRunning()
r := createReconciler(xfer, sourcePV())
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
Expect(xfer.Status.Data["pvReclaim"]).To(Equal("Delete"))
checkCompleteFalse(xfer, "Running", "")
})
It("Should update PV reclaim", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pvc := createBoundPVC()
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", pv.Name, pv)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", pvc.Name, pvc)
Expect(err).To(HaveOccurred())
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(corev1.PersistentVolumeReclaimRetain))
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should delete source pvc", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pvc := createBoundPVC()
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", pv.Name, pv)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, pvc.Namespace, pvc.Name, pvc)
Expect(err).To(HaveOccurred())
Expect(errors.IsNotFound(err)).To(BeTrue())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should update claimref", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
r := createReconciler(xfer, pv)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", pv.Name, pv)
Expect(err).ToNot(HaveOccurred())
Expect(pv.Spec.ClaimRef).ToNot(BeNil())
Expect(&pv.Spec.ClaimRef.Namespace).To(Equal(xfer.Spec.Target.Namespace))
Expect(&pv.Spec.ClaimRef.Name).To(Equal(xfer.Spec.Target.Name))
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should create target", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pv.Spec.ClaimRef = nil
pvc := &corev1.PersistentVolumeClaim{}
r := createReconciler(xfer, pv)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "target-ns", "target-pvc", pvc)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should create target even when a new source appears", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pv.Spec.ClaimRef = nil
newSource := createBoundPVC()
newSource.Spec.VolumeName = "somethingelse"
pvc := &corev1.PersistentVolumeClaim{}
r := createReconciler(xfer, pv, newSource)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "target-ns", "target-pvc", pvc)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should wait for target to be bound", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pv.Spec.ClaimRef = nil
pvc := createUnboundPVC()
pvc.Namespace = "target-ns"
pvc.Name = "target-pvc"
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Waiting for target to be bound", "")
})
It("Should error if PV gets bound to something else", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pv.Spec.ClaimRef.Namespace = "target-ns"
pv.Spec.ClaimRef.Name = "baz"
pvc := createBoundPVC()
pvc.Namespace = "target-ns"
pvc.Name = "target-pvc"
pvc.Spec.VolumeName = pv.Name
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).To(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferError))
checkCompleteFalse(xfer, "PV bound to wrong PVC", "")
})
It("Should update PV retain", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
pv.Spec.ClaimRef.Namespace = "target-ns"
pv.Spec.ClaimRef.Name = "target-pvc"
pvc := createBoundPVC()
pvc.Namespace = "target-ns"
pvc.Name = "target-pvc"
pvc.Spec.VolumeName = pv.Name
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", pv.Name, pv)
Expect(err).ToNot(HaveOccurred())
Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(Equal(corev1.PersistentVolumeReclaimDelete))
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferRunning))
checkCompleteFalse(xfer, "Running", "")
})
It("Should complete transfer", func() {
xfer := pvcTransferRunning()
xfer.Status.Data["pvReclaim"] = "Delete"
pv := sourcePV()
pv.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimDelete
pv.Spec.ClaimRef.Namespace = "target-ns"
pv.Spec.ClaimRef.Name = "target-pvc"
pvc := createBoundPVC()
pvc.Namespace = "target-ns"
pvc.Name = "target-pvc"
pvc.Spec.VolumeName = pv.Name
r := createReconciler(xfer, pv, pvc)
_, err := r.Reconcile(context.TODO(), rr(xfer.Name))
Expect(err).ToNot(HaveOccurred())
err = getResource(r.Client, "", xfer.Name, xfer)
Expect(err).ToNot(HaveOccurred())
Expect(xfer.Status.Phase).To(Equal(cdiv1.ObjectTransferComplete))
checkCompleteTrue(xfer)
})
})
})
func createPod(pvcName string) *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Namespace: "source-ns",
Name: "source-pod",
},
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
{
Name: "vol",
VolumeSource: corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: pvcName,
},
},
},
},
},
}
}
func createUnboundPVC() *corev1.PersistentVolumeClaim {
return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: "source-ns",
Name: "source-pvc",
},
}
}
func createBoundPVC() *corev1.PersistentVolumeClaim {
pvc := createUnboundPVC()
pvc.Spec.VolumeName = "source-pv"
pvc.Status.Phase = corev1.ClaimBound
return pvc
}
func pvcTransfer(phase cdiv1.ObjectTransferPhase) *cdiv1.ObjectTransfer {
targetNamespace := "target-ns"
targetName := "target-pvc"
return &cdiv1.ObjectTransfer{
ObjectMeta: metav1.ObjectMeta{
Name: "pvcTransfer",
UID: types.UID("uid-pvcTransfer"),
},
Spec: cdiv1.ObjectTransferSpec{
Source: cdiv1.TransferSource{
Kind: "PersistentVolumeClaim",
Name: "source-pvc",
Namespace: "source-ns",
},
Target: cdiv1.TransferTarget{
Namespace: &targetNamespace,
Name: &targetName,
},
},
Status: cdiv1.ObjectTransferStatus{
Phase: phase,
},
}
}
func pvcTransferRunning() *cdiv1.ObjectTransfer {
t := pvcTransfer(cdiv1.ObjectTransferRunning)
pvc := createBoundPVC()
pvc.Kind = "PersistentVolumeClaim"
pvc.APIVersion = "v1"
pvc.ResourceVersion = "1000"
pvc.Annotations = map[string]string{
"cdi.kubevirt.io/objectTransferName": "pvcTransfer",
}
pvc.Status = corev1.PersistentVolumeClaimStatus{}
bs, _ := json.Marshal(pvc)
t.Status.Data = map[string]string{
"source": string(bs),
"pvName": "source-pv",
}
return t
}
func pvcTransferRunningWithBoundPVC() *cdiv1.ObjectTransfer {
t := pvcTransfer(cdiv1.ObjectTransferRunning)
pvc := createBoundPVC()
pvc.ResourceVersion = "1000"
pvc.Annotations = map[string]string{
"cdi.kubevirt.io/objectTransferName": "pvcTransfer",
}
pvc.Status = corev1.PersistentVolumeClaimStatus{Phase: corev1.ClaimBound}
bs, _ := json.Marshal(pvc)
t.Status.Data = map[string]string{
"source": string(bs),
"pvName": "source-pv",
}
return t
}
func sourcePV() *corev1.PersistentVolume {
return &corev1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "source-pv",
},
Spec: corev1.PersistentVolumeSpec{
ClaimRef: &corev1.ObjectReference{
Namespace: "source-ns",
Name: "source-pvc",
},
PersistentVolumeReclaimPolicy: corev1.PersistentVolumeReclaimDelete,
},
}
}