UT for pv topology

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
Lyndon-Li
2026-08-27 18:22:47 +08:00
parent 72466dbd52
commit 83e5f9b85a
2 changed files with 182 additions and 41 deletions
+4 -2
View File
@@ -286,7 +286,8 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1ap
selectedNode = ""
restorePV, err := kube.WaitPVCBound(ctx, e.kubeClient.CoreV1(), e.kubeClient.CoreV1(), restorePVC.Name, restorePVC.Namespace, param.ExposeTimeout)
var restorePV *corev1api.PersistentVolume
restorePV, err = kube.WaitPVCBound(ctx, e.kubeClient.CoreV1(), e.kubeClient.CoreV1(), restorePVC.Name, restorePVC.Namespace, param.ExposeTimeout)
if err != nil {
return errors.Wrap(err, "error waiting for restore PVC bound")
}
@@ -309,7 +310,8 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1ap
return errors.Wrapf(err, "error to get volume snapshot %s/%s", param.CSI.Snapshot.VolumeSnapshotNamespace, param.CSI.Snapshot.VolumeSnapshot)
}
vsc, err := csi.GetVSCForVS(ctx, vs, e.ctrlClient)
var vsc *snapshotv1api.VolumeSnapshotContent
vsc, err = csi.GetVSCForVS(ctx, vs, e.ctrlClient)
if err != nil {
return errors.Wrapf(err, "error to get volume snapshot content for volume snapshot %s/%s", vs.Namespace, vs.Name)
}
+178 -39
View File
@@ -188,20 +188,22 @@ func TestRestoreExpose(t *testing.T) {
}
tests := []struct {
name string
kubeClientObj []runtime.Object
ownerRestore *velerov1.Restore
targetPVCName string
targetNamespace string
targetPVName string
kubeReactors []reactor
cacheVolume *CacheConfigs
dataMover string
expectBackupPod bool
expectBackupPVC bool
expectCachePVC bool
expectBackupPV bool
err string
name string
kubeClientObj []runtime.Object
ownerRestore *velerov1.Restore
targetPVCName string
targetNamespace string
targetPVName string
kubeReactors []reactor
cacheVolume *CacheConfigs
dataMover string
expectBackupPod bool
expectBackupPVC bool
expectCachePVC bool
expectBackupPV bool
expectedNodeSelector map[string]string
expectedNodeAffinity *corev1api.NodeAffinity
err string
}{
{
name: "wait target pvc consumed fail",
@@ -339,8 +341,29 @@ func TestRestoreExpose(t *testing.T) {
},
},
},
expectBackupPod: true,
expectBackupPVC: true,
expectBackupPod: true,
expectBackupPVC: true,
expectedNodeSelector: map[string]string{},
expectedNodeAffinity: &corev1api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &corev1api.NodeSelector{
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
{
MatchExpressions: []corev1api.NodeSelectorRequirement{
{
Key: "topology.kubernetes.io/zone",
Operator: corev1api.NodeSelectorOpIn,
Values: []string{"zone-1"},
},
{
Key: "kubernetes.io/os",
Operator: corev1api.NodeSelectorOpNotIn,
Values: []string{"windows"},
},
},
},
},
},
},
},
{
name: "create temporary PV fail",
@@ -559,9 +582,16 @@ func TestRestoreExpose(t *testing.T) {
require.NoError(t, err)
}
_, err = exposer.kubeClient.CoreV1().Pods(ownerObject.Namespace).Get(t.Context(), ownerObject.Name, metav1.GetOptions{})
pod, err := exposer.kubeClient.CoreV1().Pods(ownerObject.Namespace).Get(t.Context(), ownerObject.Name, metav1.GetOptions{})
if test.expectBackupPod {
require.NoError(t, err)
if test.expectedNodeSelector != nil {
assert.Equal(t, test.expectedNodeSelector, pod.Spec.NodeSelector)
}
if test.expectedNodeAffinity != nil {
require.NotNil(t, pod.Spec.Affinity)
assert.Equal(t, test.expectedNodeAffinity, pod.Spec.Affinity.NodeAffinity)
}
} else {
require.True(t, apierrors.IsNotFound(err), "expected IsNotFound, got %v", err)
}
@@ -1608,6 +1638,115 @@ end diagnose restore exposer`,
}
}
func TestValidateSelectedNode(t *testing.T) {
tests := []struct {
name string
node string
dataMover string
kubeClientObj []runtime.Object
expected bool
}{
{
name: "empty node",
node: "",
expected: true,
},
{
name: "node os is linux",
node: "fake-node",
kubeClientObj: []runtime.Object{
&corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-node",
Labels: map[string]string{
corev1api.LabelOSStable: kube.NodeOSLinux,
},
},
},
},
expected: true,
},
{
name: "node os is windows",
node: "fake-node",
kubeClientObj: []runtime.Object{
&corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-node",
Labels: map[string]string{
corev1api.LabelOSStable: kube.NodeOSWindows,
},
},
},
},
expected: true,
},
{
name: "node without os label",
node: "fake-node",
kubeClientObj: []runtime.Object{
&corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-node",
},
},
},
expected: false,
},
{
name: "node not found",
node: "fake-node",
expected: false,
},
{
name: "block data mover with linux node",
node: "fake-node",
dataMover: datamover.DataMoverTypeVeleroBlock,
kubeClientObj: []runtime.Object{
&corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-node",
Labels: map[string]string{
corev1api.LabelOSStable: kube.NodeOSLinux,
},
},
},
},
expected: true,
},
{
name: "block data mover with windows node",
node: "fake-node",
dataMover: datamover.DataMoverTypeVeleroBlock,
kubeClientObj: []runtime.Object{
&corev1api.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-node",
Labels: map[string]string{
corev1api.LabelOSStable: kube.NodeOSWindows,
},
},
},
},
expected: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fakeKubeClient := fake.NewSimpleClientset(test.kubeClientObj...)
exposer := genericRestoreExposer{
kubeClient: fakeKubeClient,
log: velerotest.NewLogger(),
}
actual := exposer.validateSelectedNode(t.Context(), test.node, test.dataMover, exposer.log)
assert.Equal(t, test.expected, actual)
})
}
}
func TestCreateRestorePod(t *testing.T) {
scName := "storage-class-01"
@@ -1744,28 +1883,28 @@ func TestCreateRestorePod(t *testing.T) {
log: velerotest.NewLogger(),
}
pod, err := exposer.createRestorePod(
t.Context(),
corev1api.ObjectReference{
Namespace: velerov1.DefaultNamespace,
Name: "data-download",
},
targetPVCObj,
time.Second*3,
nil,
nil,
nil,
test.selectedNode,
corev1api.ResourceRequirements{},
test.nodeOS,
test.affinity,
"", // priority class name
nil,
"", // volumeSnapshotNamespace
"", // volumeID
nil,
nil, // volumeTopology
)
pod, err := exposer.createRestorePod(
t.Context(),
corev1api.ObjectReference{
Namespace: velerov1.DefaultNamespace,
Name: "data-download",
},
targetPVCObj,
time.Second*3,
nil,
nil,
nil,
test.selectedNode,
corev1api.ResourceRequirements{},
test.nodeOS,
test.affinity,
"", // priority class name
nil,
"", // volumeSnapshotNamespace
"", // volumeID
nil,
nil, // volumeTopology
)
require.NoError(t, err)
if test.expectedPod != nil {