mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-03 19:54:00 +00:00
dm controller refactor for cancel
Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
@@ -45,7 +45,6 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
@@ -270,35 +269,30 @@ type fakeSnapshotExposer struct {
|
||||
clock clock.WithTickerAndDelayedExecution
|
||||
ambiguousNodeOS bool
|
||||
peekErr error
|
||||
exposeErr error
|
||||
getErr error
|
||||
getNil bool
|
||||
}
|
||||
|
||||
func (f *fakeSnapshotExposer) Expose(ctx context.Context, ownerObject corev1api.ObjectReference, param any) error {
|
||||
du := velerov2alpha1api.DataUpload{}
|
||||
err := f.kubeClient.Get(ctx, kbclient.ObjectKey{
|
||||
Name: dataUploadName,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
}, &du)
|
||||
if err != nil {
|
||||
return err
|
||||
if f.exposeErr != nil {
|
||||
return f.exposeErr
|
||||
}
|
||||
|
||||
original := du
|
||||
du.Status.Phase = velerov2alpha1api.DataUploadPhasePrepared
|
||||
du.Status.StartTimestamp = &metav1.Time{Time: f.clock.Now()}
|
||||
f.kubeClient.Patch(ctx, &du, kbclient.MergeFrom(&original))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeSnapshotExposer) GetExposed(ctx context.Context, du corev1api.ObjectReference, tm time.Duration, para any) (*exposer.ExposeResult, error) {
|
||||
pod := &corev1api.Pod{}
|
||||
err := f.kubeClient.Get(ctx, kbclient.ObjectKey{
|
||||
Name: dataUploadName,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
}, pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if f.getErr != nil {
|
||||
return nil, f.getErr
|
||||
}
|
||||
|
||||
if f.getNil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pod := &corev1api.Pod{}
|
||||
|
||||
nodeOS := "linux"
|
||||
pNodeOS := &nodeOS
|
||||
if f.ambiguousNodeOS {
|
||||
@@ -346,214 +340,265 @@ func (b *fakeDataUploadFSBR) Close(ctx context.Context) {
|
||||
|
||||
func TestReconcile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
du *velerov2alpha1api.DataUpload
|
||||
pod *corev1api.Pod
|
||||
pvc *corev1api.PersistentVolumeClaim
|
||||
snapshotExposerList map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer
|
||||
dataMgr *datapath.Manager
|
||||
expectedProcessed bool
|
||||
expected *velerov2alpha1api.DataUpload
|
||||
checkFunc func(velerov2alpha1api.DataUpload) bool
|
||||
expectedRequeue ctrl.Result
|
||||
expectedErrMsg string
|
||||
needErrs []bool
|
||||
removeNode bool
|
||||
ambiguousNodeOS bool
|
||||
peekErr error
|
||||
notCreateFSBR bool
|
||||
fsBRInitErr error
|
||||
fsBRStartErr error
|
||||
name string
|
||||
du *velerov2alpha1api.DataUpload
|
||||
notCreateDU bool
|
||||
needDelete bool
|
||||
sportTime *metav1.Time
|
||||
pod *corev1api.Pod
|
||||
pvc *corev1api.PersistentVolumeClaim
|
||||
snapshotExposerList map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer
|
||||
dataMgr *datapath.Manager
|
||||
needCreateFSBR bool
|
||||
needExclusiveUpdateError error
|
||||
expected *velerov2alpha1api.DataUpload
|
||||
expectDeleted bool
|
||||
expectCancelRecord bool
|
||||
needErrs []bool
|
||||
ambiguousNodeOS bool
|
||||
peekErr error
|
||||
exposeErr error
|
||||
getExposeErr error
|
||||
getExposeNil bool
|
||||
fsBRInitErr error
|
||||
fsBRStartErr error
|
||||
expectedErr string
|
||||
expectedResult *ctrl.Result
|
||||
expectDataPath bool
|
||||
}{
|
||||
{
|
||||
name: "Dataupload is not initialized",
|
||||
du: builder.ForDataUpload("unknown-ns", "unknown-name").Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "du not found",
|
||||
du: dataUploadBuilder().Result(),
|
||||
notCreateDU: true,
|
||||
},
|
||||
{
|
||||
name: "Error get Dataupload",
|
||||
du: builder.ForDataUpload(velerov1api.DefaultNamespace, "unknown-name").Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
expectedErrMsg: "getting DataUpload: Get error",
|
||||
needErrs: []bool{true, false, false, false},
|
||||
name: "du not created in velero default namespace",
|
||||
du: builder.ForDataUpload("test-ns", dataUploadName).Result(),
|
||||
},
|
||||
{
|
||||
name: "Unsupported data mover type",
|
||||
du: dataUploadBuilder().DataMover("unknown type").Result(),
|
||||
expected: dataUploadBuilder().Phase("").Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "get du fail",
|
||||
du: dataUploadBuilder().Result(),
|
||||
needErrs: []bool{true, false, false, false},
|
||||
expectedErr: "getting DataUpload: Get error",
|
||||
},
|
||||
{
|
||||
name: "Unknown type of snapshot exposer is not initialized",
|
||||
du: dataUploadBuilder().SnapshotType("unknown type").Result(),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
expectedErrMsg: "unknown type type of snapshot exposer is not exist",
|
||||
name: "du is not for built-in dm",
|
||||
du: dataUploadBuilder().DataMover("other").Result(),
|
||||
},
|
||||
{
|
||||
name: "Dataupload should be accepted",
|
||||
du: dataUploadBuilder().Result(),
|
||||
pod: builder.ForPod("fake-ns", dataUploadName).Volumes(&corev1api.Volume{Name: "test-pvc"}).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "add finalizer to du",
|
||||
du: dataUploadBuilder().Result(),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
},
|
||||
{
|
||||
name: "Dataupload should fail to get PVC information",
|
||||
du: dataUploadBuilder().Result(),
|
||||
pod: builder.ForPod("fake-ns", dataUploadName).Volumes(&corev1api.Volume{Name: "wrong-pvc"}).Result(),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
expectedErrMsg: "failed to get PVC",
|
||||
name: "add finalizer to du failed",
|
||||
du: dataUploadBuilder().Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
expectedErr: "error updating dataupload with error velero/dataupload-1: Update error",
|
||||
},
|
||||
{
|
||||
name: "Dataupload should fail because expected node doesn't exist",
|
||||
du: dataUploadBuilder().Result(),
|
||||
pod: builder.ForPod("fake-ns", dataUploadName).Volumes(&corev1api.Volume{Name: "test-pvc"}).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(),
|
||||
removeNode: true,
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
expectedErrMsg: "no appropriate node to run data upload",
|
||||
name: "du is under deletion",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
needDelete: true,
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Result(),
|
||||
},
|
||||
{
|
||||
name: "Dataupload should be prepared",
|
||||
du: dataUploadBuilder().SnapshotType(fakeSnapshotType).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "du is under deletion but cancel failed",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
needDelete: true,
|
||||
expectedErr: "error updating dataupload with error velero/dataupload-1: Update error",
|
||||
},
|
||||
{
|
||||
name: "Dataupload prepared should be completed",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "du is under deletion and in terminal state",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
sportTime: &metav1.Time{Time: time.Now()},
|
||||
needDelete: true,
|
||||
expectDeleted: true,
|
||||
},
|
||||
{
|
||||
name: "Dataupload should fail if expose returns ambiguous nodeOS",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
ambiguousNodeOS: true,
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
expectedErrMsg: "unsupported ambiguous node OS",
|
||||
name: "du is under deletion and in terminal state, but remove finalizer failed",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
needDelete: true,
|
||||
expectedErr: "error updating dataupload with error velero/dataupload-1: Update error",
|
||||
},
|
||||
{
|
||||
name: "Dataupload with not enabled cancel",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(false).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "delay cancel negative for others",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
sportTime: &metav1.Time{Time: time.Now()},
|
||||
expectCancelRecord: true,
|
||||
},
|
||||
{
|
||||
name: "Dataupload should be cancel",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(true).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceling).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "delay cancel negative for inProgress",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
sportTime: &metav1.Time{Time: time.Now().Add(-time.Minute * 58)},
|
||||
expectCancelRecord: true,
|
||||
},
|
||||
{
|
||||
name: "Dataupload should be cancel with match node",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: func() *velerov2alpha1api.DataUpload {
|
||||
du := dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(true).Result()
|
||||
du.Status.Node = "test-node"
|
||||
return du
|
||||
}(),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
notCreateFSBR: true,
|
||||
name: "delay cancel affirmative for others",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
sportTime: &metav1.Time{Time: time.Now().Add(-time.Minute * 5)},
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
},
|
||||
{
|
||||
name: "Dataupload should not be cancel with mismatch node",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: func() *velerov2alpha1api.DataUpload {
|
||||
du := dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(true).Result()
|
||||
du.Status.Node = "different_node"
|
||||
return du
|
||||
}(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
notCreateFSBR: true,
|
||||
name: "delay cancel affirmative for inProgress",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
sportTime: &metav1.Time{Time: time.Now().Add(-time.Hour)},
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
},
|
||||
{
|
||||
name: "runCancelableDataUpload is concurrent limited",
|
||||
dataMgr: datapath.NewManager(0),
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
expectedRequeue: ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
name: "delay cancel failed",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
sportTime: &metav1.Time{Time: time.Now().Add(-time.Hour)},
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(),
|
||||
expectCancelRecord: true,
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
fsBRInitErr: errors.New("fake-data-path-init-error"),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).SnapshotType(fakeSnapshotType).Result(),
|
||||
expectedErrMsg: "error initializing asyncBR: fake-data-path-init-error",
|
||||
name: "Unknown data upload status",
|
||||
du: dataUploadBuilder().Phase("Unknown").Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
},
|
||||
{
|
||||
name: "Unable to update status to in progress for data download",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
needErrs: []bool{false, false, false, true},
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
expectedRequeue: ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
name: "Unknown type of snapshot exposer is not initialized",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).SnapshotType("unknown type").Result(),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
expectedErr: "unknown type type of snapshot exposer is not exist",
|
||||
},
|
||||
{
|
||||
name: "data path start error",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(),
|
||||
fsBRStartErr: errors.New("fake-data-path-start-error"),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).SnapshotType(fakeSnapshotType).Result(),
|
||||
expectedErrMsg: "error starting async backup for pod dataupload-1, volume dataupload-1: fake-data-path-start-error",
|
||||
name: "new du but accept failed",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
needExclusiveUpdateError: errors.New("exclusive-update-error"),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedErr: "error accepting the data upload dataupload-1: exclusive-update-error",
|
||||
},
|
||||
{
|
||||
name: "prepare timeout",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).SnapshotType(fakeSnapshotType).AcceptedTimestamp(&metav1.Time{Time: time.Now().Add(-time.Minute * 5)}).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Result(),
|
||||
name: "du is cancel on accepted",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Result(),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
},
|
||||
{
|
||||
name: "peek error",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).SnapshotType(fakeSnapshotType).Result(),
|
||||
peekErr: errors.New("fake-peek-error"),
|
||||
expectedProcessed: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
name: "du is accepted but setup expose param failed on getting PVC",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Message("failed to set exposer parameters").Result(),
|
||||
expectedErr: "failed to get PVC fake-ns/test-pvc: persistentvolumeclaims \"test-pvc\" not found",
|
||||
},
|
||||
{
|
||||
name: "Dataupload with enabled cancel",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: func() *velerov2alpha1api.DataUpload {
|
||||
du := dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).SnapshotType(fakeSnapshotType).Result()
|
||||
controllerutil.AddFinalizer(du, DataUploadDownloadFinalizer)
|
||||
du.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
return du
|
||||
}(),
|
||||
checkFunc: func(du velerov2alpha1api.DataUpload) bool {
|
||||
return du.Spec.Cancel
|
||||
},
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(),
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "du expose failed",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).SnapshotType(fakeSnapshotType).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(),
|
||||
exposeErr: errors.New("fake-expose-error"),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Message("error exposing snapshot").Result(),
|
||||
expectedErr: "fake-expose-error",
|
||||
},
|
||||
{
|
||||
name: "Dataupload with remove finalizer and should not be retrieved",
|
||||
pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1api.Volume{Name: "dataupload-1"}).Result(),
|
||||
du: func() *velerov2alpha1api.DataUpload {
|
||||
du := dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).SnapshotType(fakeSnapshotType).Cancel(true).Result()
|
||||
controllerutil.AddFinalizer(du, DataUploadDownloadFinalizer)
|
||||
du.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
return du
|
||||
}(),
|
||||
checkFunc: func(du velerov2alpha1api.DataUpload) bool {
|
||||
return !controllerutil.ContainsFinalizer(&du, DataUploadDownloadFinalizer)
|
||||
},
|
||||
expectedRequeue: ctrl.Result{},
|
||||
name: "du succeeds for accepted",
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).SnapshotType(fakeSnapshotType).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(),
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(),
|
||||
},
|
||||
{
|
||||
name: "prepare timeout on accepted",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Finalizers([]string{DataUploadDownloadFinalizer}).AcceptedTimestamp(&metav1.Time{Time: time.Now().Add(-time.Minute * 30)}).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseFailed).Message("timeout on preparing data upload").Result(),
|
||||
},
|
||||
{
|
||||
name: "peek error on accepted",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
peekErr: errors.New("fake-peak-error"),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceled).Finalizers([]string{DataUploadDownloadFinalizer}).Phase(velerov2alpha1api.DataUploadPhaseCanceled).Message("found a du velero/dataupload-1 with expose error: fake-peak-error. mark it as cancel").Result(),
|
||||
},
|
||||
{
|
||||
name: "cancel on prepared",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Cancel(true).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceled).Finalizers([]string{DataUploadDownloadFinalizer}).Cancel(true).Phase(velerov2alpha1api.DataUploadPhaseCanceled).Result(),
|
||||
},
|
||||
{
|
||||
name: "Failed to get snapshot expose on prepared",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
getExposeErr: errors.New("fake-get-error"),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Message("exposed snapshot is not ready: fake-get-error").Result(),
|
||||
expectedErr: "fake-get-error",
|
||||
},
|
||||
{
|
||||
name: "Get nil restore expose on prepared",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
getExposeNil: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Message("exposed snapshot is not ready").Result(),
|
||||
expectedErr: "no expose result is available for the current node",
|
||||
},
|
||||
{
|
||||
name: "Dataupload should fail if expose returns ambiguous nodeOS",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
ambiguousNodeOS: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedErr: "unsupported ambiguous node OS",
|
||||
},
|
||||
{
|
||||
name: "Error in data path is concurrent limited",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
dataMgr: datapath.NewManager(0),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
fsBRInitErr: errors.New("fake-data-path-init-error"),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Message("error initializing data path").Result(),
|
||||
expectedErr: "error initializing asyncBR: fake-data-path-init-error",
|
||||
},
|
||||
{
|
||||
name: "Unable to update status to in progress for data upload",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path start error",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
fsBRStartErr: errors.New("fake-data-path-start-error"),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).Finalizers([]string{DataUploadDownloadFinalizer}).Message("error starting data path").Result(),
|
||||
expectedErr: "error starting async backup for pod , volume dataupload-1: fake-data-path-start-error",
|
||||
},
|
||||
{
|
||||
name: "Prepare succeeds",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectDataPath: true,
|
||||
},
|
||||
{
|
||||
name: "In progress du is not handled by the current node",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
},
|
||||
{
|
||||
name: "In progress du is not set as cancel",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
},
|
||||
{
|
||||
name: "Cancel data upload in progress with empty FSBR",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceled).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
},
|
||||
{
|
||||
name: "Cancel data upload in progress and patch data upload error",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
needCreateFSBR: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedErr: "error updating dataupload with error velero/dataupload-1: Update error",
|
||||
expectCancelRecord: true,
|
||||
expectDataPath: true,
|
||||
},
|
||||
{
|
||||
name: "Cancel data upload in progress succeeds",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
needCreateFSBR: true,
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceling).Cancel(true).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectDataPath: true,
|
||||
expectCancelRecord: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -561,24 +606,15 @@ func TestReconcile(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
r, err := initDataUploaderReconciler(test.needErrs...)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
r.client.Delete(ctx, test.du, &kbclient.DeleteOptions{})
|
||||
if test.pod != nil {
|
||||
r.client.Delete(ctx, test.pod, &kbclient.DeleteOptions{})
|
||||
}
|
||||
}()
|
||||
ctx := context.Background()
|
||||
if test.du.Namespace == velerov1api.DefaultNamespace {
|
||||
isDeletionTimestampSet := test.du.DeletionTimestamp != nil
|
||||
err = r.client.Create(ctx, test.du)
|
||||
|
||||
if !test.notCreateDU {
|
||||
err = r.client.Create(context.Background(), test.du)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if test.needDelete {
|
||||
err = r.client.Delete(context.Background(), test.du)
|
||||
require.NoError(t, err)
|
||||
// because of the changes introduced by https://github.com/kubernetes-sigs/controller-runtime/commit/7a66d580c0c53504f5b509b45e9300cc18a1cc30
|
||||
// the fake client ignores the DeletionTimestamp when calling the Create(),
|
||||
// so call Delete() here
|
||||
if isDeletionTimestampSet {
|
||||
err = r.client.Delete(ctx, test.du)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
if test.pod != nil {
|
||||
@@ -591,38 +627,41 @@ func TestReconcile(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if test.removeNode {
|
||||
err = r.kubeClient.CoreV1().Nodes().Delete(ctx, "fake-node", metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if test.dataMgr != nil {
|
||||
r.dataPathMgr = test.dataMgr
|
||||
} else {
|
||||
r.dataPathMgr = datapath.NewManager(1)
|
||||
}
|
||||
|
||||
if test.sportTime != nil {
|
||||
r.cancelledDataUpload[test.du.Name] = test.sportTime.Time
|
||||
}
|
||||
|
||||
if test.du.Spec.SnapshotType == fakeSnapshotType {
|
||||
r.snapshotExposerList = map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{fakeSnapshotType: &fakeSnapshotExposer{r.client, r.Clock, test.ambiguousNodeOS, test.peekErr}}
|
||||
r.snapshotExposerList = map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{fakeSnapshotType: &fakeSnapshotExposer{r.client, r.Clock, test.ambiguousNodeOS, test.peekErr, test.exposeErr, test.getExposeErr, test.getExposeNil}}
|
||||
} else if test.du.Spec.SnapshotType == velerov2alpha1api.SnapshotTypeCSI {
|
||||
r.snapshotExposerList = map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{velerov2alpha1api.SnapshotTypeCSI: exposer.NewCSISnapshotExposer(r.kubeClient, r.csiSnapshotClient, velerotest.NewLogger())}
|
||||
}
|
||||
if !test.notCreateFSBR {
|
||||
datapath.MicroServiceBRWatcherCreator = func(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR {
|
||||
return &fakeDataUploadFSBR{
|
||||
du: test.du,
|
||||
kubeClient: r.client,
|
||||
clock: r.Clock,
|
||||
initErr: test.fsBRInitErr,
|
||||
startErr: test.fsBRStartErr,
|
||||
}
|
||||
|
||||
funcExclusiveUpdateDataUpload = exclusiveUpdateDataUpload
|
||||
if test.needExclusiveUpdateError != nil {
|
||||
funcExclusiveUpdateDataUpload = func(context.Context, kbclient.Client, *velerov2alpha1api.DataUpload, func(*velerov2alpha1api.DataUpload)) (bool, error) {
|
||||
return false, test.needExclusiveUpdateError
|
||||
}
|
||||
}
|
||||
|
||||
testCreateFsBR := false
|
||||
if test.du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress && !test.notCreateFSBR {
|
||||
datapath.MicroServiceBRWatcherCreator = func(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR {
|
||||
return &fakeDataUploadFSBR{
|
||||
du: test.du,
|
||||
kubeClient: r.client,
|
||||
clock: r.Clock,
|
||||
initErr: test.fsBRInitErr,
|
||||
startErr: test.fsBRStartErr,
|
||||
}
|
||||
}
|
||||
|
||||
if test.needCreateFSBR {
|
||||
if fsBR := r.dataPathMgr.GetAsyncBR(test.du.Name); fsBR == nil {
|
||||
testCreateFsBR = true
|
||||
_, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, nil, nil, datapath.TaskTypeBackup, test.du.Name, velerov1api.DefaultNamespace, "", "", "", datapath.Callbacks{OnCancelled: r.OnDataUploadCancelled}, false, velerotest.NewLogger())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -635,41 +674,46 @@ func TestReconcile(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, test.expectedRequeue, actualResult)
|
||||
if test.expectedErrMsg == "" {
|
||||
require.NoError(t, err)
|
||||
if test.expectedErr != "" {
|
||||
assert.EqualError(t, err, test.expectedErr)
|
||||
} else {
|
||||
require.ErrorContains(t, err, test.expectedErrMsg)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
du := velerov2alpha1api.DataUpload{}
|
||||
err = r.client.Get(ctx, kbclient.ObjectKey{
|
||||
Name: test.du.Name,
|
||||
Namespace: test.du.Namespace,
|
||||
}, &du)
|
||||
t.Logf("%s: \n %v \n", test.name, du)
|
||||
// Assertions
|
||||
if test.expected == nil {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expected.Status.Phase, du.Status.Phase)
|
||||
if test.expectedResult != nil {
|
||||
assert.Equal(t, test.expectedResult.Requeue, actualResult.Requeue)
|
||||
assert.Equal(t, test.expectedResult.RequeueAfter, actualResult.RequeueAfter)
|
||||
}
|
||||
|
||||
if test.expectedProcessed {
|
||||
assert.False(t, du.Status.CompletionTimestamp.IsZero())
|
||||
if test.expected != nil || test.expectDeleted {
|
||||
du := velerov2alpha1api.DataUpload{}
|
||||
err = r.client.Get(ctx, kbclient.ObjectKey{
|
||||
Name: test.du.Name,
|
||||
Namespace: test.du.Namespace,
|
||||
}, &du)
|
||||
|
||||
if test.expectDeleted {
|
||||
assert.True(t, apierrors.IsNotFound(err))
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expected.Status.Phase, du.Status.Phase)
|
||||
assert.Contains(t, du.Status.Message, test.expected.Status.Message)
|
||||
assert.Equal(t, du.Finalizers, test.expected.Finalizers)
|
||||
assert.Equal(t, du.Spec.Cancel, test.expected.Spec.Cancel)
|
||||
}
|
||||
}
|
||||
|
||||
if !test.expectedProcessed {
|
||||
assert.True(t, du.Status.CompletionTimestamp.IsZero())
|
||||
}
|
||||
|
||||
if test.checkFunc != nil {
|
||||
assert.True(t, test.checkFunc(du))
|
||||
}
|
||||
|
||||
if !testCreateFsBR && du.Status.Phase != velerov2alpha1api.DataUploadPhaseInProgress {
|
||||
if !test.expectDataPath {
|
||||
assert.Nil(t, r.dataPathMgr.GetAsyncBR(test.du.Name))
|
||||
} else {
|
||||
assert.NotNil(t, r.dataPathMgr.GetAsyncBR(test.du.Name))
|
||||
}
|
||||
|
||||
if test.expectCancelRecord {
|
||||
assert.Contains(t, r.cancelledDataUpload, test.du.Name)
|
||||
} else {
|
||||
assert.Empty(t, r.cancelledDataUpload)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -719,7 +763,7 @@ func TestOnDataUploadProgress(t *testing.T) {
|
||||
{
|
||||
name: "failed to patch dataupload",
|
||||
du: dataUploadBuilder().Result(),
|
||||
needErrs: []bool{false, false, false, true},
|
||||
needErrs: []bool{false, false, true, false},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
@@ -992,7 +1036,7 @@ func TestTryCancelDataUpload(t *testing.T) {
|
||||
err = r.client.Create(ctx, test.dd)
|
||||
require.NoError(t, err)
|
||||
|
||||
r.tryCancelAcceptedDataUpload(ctx, test.dd, "")
|
||||
r.tryCancelDataUpload(ctx, test.dd, "")
|
||||
|
||||
if test.expectedErr == "" {
|
||||
assert.NoError(t, err)
|
||||
@@ -1114,33 +1158,8 @@ func TestAttemptDataUploadResume(t *testing.T) {
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "accepted DataUpload in other node",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(),
|
||||
cancelledDataUploads: []string{dataUploadName},
|
||||
acceptedDataUploads: []string{dataUploadName},
|
||||
},
|
||||
{
|
||||
name: "accepted DataUpload in the current node",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).AcceptedByNode("node-1").Result(),
|
||||
cancelledDataUploads: []string{dataUploadName},
|
||||
acceptedDataUploads: []string{dataUploadName},
|
||||
},
|
||||
{
|
||||
name: "accepted DataUpload in the current node but canceled",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).AcceptedByNode("node-1").Cancel(true).Result(),
|
||||
cancelledDataUploads: []string{dataUploadName},
|
||||
acceptedDataUploads: []string{dataUploadName},
|
||||
},
|
||||
{
|
||||
name: "accepted DataUpload in the current node but update error",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).AcceptedByNode("node-1").Result(),
|
||||
needErrs: []bool{false, false, true, false, false, false},
|
||||
acceptedDataUploads: []string{dataUploadName},
|
||||
},
|
||||
{
|
||||
name: "prepared DataUpload",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
prepareddDataUploads: []string{dataUploadName},
|
||||
name: "Other DataUpload",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(),
|
||||
},
|
||||
{
|
||||
name: "InProgress DataUpload, not the current node",
|
||||
|
||||
Reference in New Issue
Block a user