mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-05-01 12:55:46 +00:00
Merge branch 'release-1.18' into xj014661/1.18/ephemeral_storage_config
This commit is contained in:
1
changelogs/unreleased/9594-Lyndon-Li
Normal file
1
changelogs/unreleased/9594-Lyndon-Li
Normal file
@@ -0,0 +1 @@
|
||||
Fix issue #9343, include PV topology to data mover pod affinities
|
||||
1
changelogs/unreleased/9597-blackpiglet
Normal file
1
changelogs/unreleased/9597-blackpiglet
Normal file
@@ -0,0 +1 @@
|
||||
If BIA return updateObj with SkipFromBackupAnnotation, treat it as skip the resource from backup.
|
||||
@@ -102,6 +102,15 @@ const (
|
||||
// even if the resource contains a matching selector label.
|
||||
ExcludeFromBackupLabel = "velero.io/exclude-from-backup"
|
||||
|
||||
// SkipFromBackupAnnotation is the annotation used by internal BackupItemActions
|
||||
// to indicate that a resource should be skipped from backup,
|
||||
// even if it doesn't have the ExcludeFromBackupLabel.
|
||||
// This is used in cases where we want to skip backup of a resource based on some logic in a plugin.
|
||||
//
|
||||
// Notice: SkipFromBackupAnnotation's priority is higher than MustIncludeAdditionalItemAnnotation.
|
||||
// If SkipFromBackupAnnotation is set, the resource will be skipped even if MustIncludeAdditionalItemAnnotation is set.
|
||||
SkipFromBackupAnnotation = "velero.io/skip-from-backup"
|
||||
|
||||
// defaultVGSLabelKey is the default label key used to group PVCs under a VolumeGroupSnapshot
|
||||
DefaultVGSLabelKey = "velero.io/volume-group"
|
||||
|
||||
|
||||
@@ -98,6 +98,14 @@ func (m *backedUpItemsMap) AddItem(key itemKey) {
|
||||
m.totalItems[key] = struct{}{}
|
||||
}
|
||||
|
||||
func (m *backedUpItemsMap) DeleteItem(key itemKey) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
delete(m.backedUpItems, key)
|
||||
delete(m.totalItems, key)
|
||||
}
|
||||
|
||||
func (m *backedUpItemsMap) AddItemToTotal(key itemKey) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@@ -244,6 +244,14 @@ func (ib *itemBackupper) backupItemInternal(logger logrus.FieldLogger, obj runti
|
||||
return false, itemFiles, kubeerrs.NewAggregate(backupErrs)
|
||||
}
|
||||
|
||||
// If err is nil and updatedObj is nil, it means the item is skipped by plugin action,
|
||||
// we should return here to avoid backing up the item, and avoid potential NPE in the following code.
|
||||
if updatedObj == nil {
|
||||
log.Infof("Remove item from the backup's backupItems list and totalItems list because it's skipped by plugin action.")
|
||||
ib.backupRequest.BackedUpItems.DeleteItem(key)
|
||||
return false, itemFiles, nil
|
||||
}
|
||||
|
||||
itemFiles = append(itemFiles, additionalItemFiles...)
|
||||
obj = updatedObj
|
||||
if metadata, err = meta.Accessor(obj); err != nil {
|
||||
@@ -398,6 +406,13 @@ func (ib *itemBackupper) executeActions(
|
||||
}
|
||||
|
||||
u := &unstructured.Unstructured{Object: updatedItem.UnstructuredContent()}
|
||||
|
||||
if _, ok := u.GetAnnotations()[velerov1api.SkipFromBackupAnnotation]; ok {
|
||||
log.Infof("Resource (groupResource=%s, namespace=%s, name=%s) is skipped from backup by action %s.",
|
||||
groupResource.String(), namespace, name, actionName)
|
||||
return nil, itemFiles, nil
|
||||
}
|
||||
|
||||
if actionName == csiBIAPluginName {
|
||||
if additionalItemIdentifiers == nil && u.GetAnnotations()[velerov1api.SkippedNoCSIPVAnnotation] == "true" {
|
||||
// snapshot was skipped by CSI plugin
|
||||
|
||||
@@ -124,6 +124,15 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1api.O
|
||||
"owner": ownerObject.Name,
|
||||
})
|
||||
|
||||
volumeTopology, err := kube.GetVolumeTopology(ctx, e.kubeClient.CoreV1(), e.kubeClient.StorageV1(), csiExposeParam.SourcePVName, csiExposeParam.StorageClass)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error getting volume topology for PV %s, storage class %s", csiExposeParam.SourcePVName, csiExposeParam.StorageClass)
|
||||
}
|
||||
|
||||
if volumeTopology != nil {
|
||||
curLog.Infof("Using volume topology %v", volumeTopology)
|
||||
}
|
||||
|
||||
curLog.Info("Exposing CSI snapshot")
|
||||
|
||||
volumeSnapshot, err := csi.WaitVolumeSnapshotReady(ctx, e.csiSnapshotClient, csiExposeParam.SnapshotName, csiExposeParam.SourceNamespace, csiExposeParam.ExposeTimeout, curLog)
|
||||
@@ -254,6 +263,7 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1api.O
|
||||
csiExposeParam.NodeOS,
|
||||
csiExposeParam.PriorityClassName,
|
||||
intoleratableNodes,
|
||||
volumeTopology,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error to create backup pod")
|
||||
@@ -588,6 +598,7 @@ func (e *csiSnapshotExposer) createBackupPod(
|
||||
nodeOS string,
|
||||
priorityClassName string,
|
||||
intoleratableNodes []string,
|
||||
volumeTopology *corev1api.NodeSelector,
|
||||
) (*corev1api.Pod, error) {
|
||||
podName := ownerObject.Name
|
||||
|
||||
@@ -701,7 +712,7 @@ func (e *csiSnapshotExposer) createBackupPod(
|
||||
}
|
||||
|
||||
if affinity != nil {
|
||||
podAffinity = kube.ToSystemAffinity([]*kube.LoadAffinity{affinity})
|
||||
podAffinity = kube.ToSystemAffinity(affinity, volumeTopology)
|
||||
}
|
||||
|
||||
pod := &corev1api.Pod{
|
||||
|
||||
@@ -154,6 +154,7 @@ func TestCreateBackupPodWithPriorityClass(t *testing.T) {
|
||||
kube.NodeOSLinux,
|
||||
tc.expectedPriorityClass,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
require.NoError(t, err, tc.description)
|
||||
@@ -239,6 +240,7 @@ func TestCreateBackupPodWithMissingConfigMap(t *testing.T) {
|
||||
kube.NodeOSLinux,
|
||||
"", // empty priority class since config map is missing
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
// Should succeed even when config map is missing
|
||||
|
||||
@@ -68,6 +68,12 @@ func TestExpose(t *testing.T) {
|
||||
|
||||
var restoreSize int64 = 123456
|
||||
|
||||
scObj := &storagev1api.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-sc",
|
||||
},
|
||||
}
|
||||
|
||||
snapshotClass := "fake-snapshot-class"
|
||||
vsObject := &snapshotv1api.VolumeSnapshot{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -199,6 +205,18 @@ func TestExpose(t *testing.T) {
|
||||
expectedAffinity *corev1api.Affinity
|
||||
expectedPVCAnnotation map[string]string
|
||||
}{
|
||||
{
|
||||
name: "get volume topology fail",
|
||||
ownerBackup: backup,
|
||||
exposeParam: CSISnapshotExposeParam{
|
||||
SnapshotName: "fake-vs",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
err: "error getting volume topology for PV fake-pv, storage class fake-sc: error getting storage class fake-sc: storageclasses.storage.k8s.io \"fake-sc\" not found",
|
||||
},
|
||||
{
|
||||
name: "wait vs ready fail",
|
||||
ownerBackup: backup,
|
||||
@@ -206,6 +224,11 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error wait volume snapshot ready: error to get VolumeSnapshot /fake-vs: volumesnapshots.snapshot.storage.k8s.io \"fake-vs\" not found",
|
||||
},
|
||||
@@ -217,10 +240,15 @@ func TestExpose(t *testing.T) {
|
||||
SourceNamespace: "fake-ns",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to get volume snapshot content: error getting volume snapshot content from API: volumesnapshotcontents.snapshot.storage.k8s.io \"fake-vsc\" not found",
|
||||
},
|
||||
{
|
||||
@@ -231,6 +259,8 @@ func TestExpose(t *testing.T) {
|
||||
SourceNamespace: "fake-ns",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -245,6 +275,9 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to delete volume snapshot: error to delete volume snapshot: fake-delete-error",
|
||||
},
|
||||
{
|
||||
@@ -255,6 +288,8 @@ func TestExpose(t *testing.T) {
|
||||
SourceNamespace: "fake-ns",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -269,6 +304,9 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to delete volume snapshot content: error to delete volume snapshot content: fake-delete-error",
|
||||
},
|
||||
{
|
||||
@@ -279,6 +317,8 @@ func TestExpose(t *testing.T) {
|
||||
SourceNamespace: "fake-ns",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -293,6 +333,9 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to create backup volume snapshot: fake-create-error",
|
||||
},
|
||||
{
|
||||
@@ -303,6 +346,8 @@ func TestExpose(t *testing.T) {
|
||||
SourceNamespace: "fake-ns",
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -317,6 +362,9 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to create backup volume snapshot content: fake-create-error",
|
||||
},
|
||||
{
|
||||
@@ -326,11 +374,16 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
AccessMode: "fake-mode",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
vscObj,
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to create backup pvc: unsupported access mode fake-mode",
|
||||
},
|
||||
{
|
||||
@@ -342,6 +395,8 @@ func TestExpose(t *testing.T) {
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
AccessMode: AccessModeFileSystem,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -356,6 +411,9 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObj,
|
||||
},
|
||||
err: "error to create backup pvc: error to create pvc: fake-create-error",
|
||||
},
|
||||
{
|
||||
@@ -367,6 +425,8 @@ func TestExpose(t *testing.T) {
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -374,6 +434,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
kubeReactors: []reactor{
|
||||
{
|
||||
@@ -395,6 +456,8 @@ func TestExpose(t *testing.T) {
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -402,6 +465,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -413,6 +477,8 @@ func TestExpose(t *testing.T) {
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObject,
|
||||
@@ -420,6 +486,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -432,6 +499,8 @@ func TestExpose(t *testing.T) {
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
VolumeSize: *resource.NewQuantity(567890, ""),
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
},
|
||||
snapshotClientObj: []runtime.Object{
|
||||
vsObjectWithoutRestoreSize,
|
||||
@@ -439,6 +508,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedVolumeSize: resource.NewQuantity(567890, ""),
|
||||
},
|
||||
@@ -449,6 +519,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -465,6 +536,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedReadOnlyPVC: true,
|
||||
},
|
||||
@@ -475,6 +547,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -491,6 +564,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedReadOnlyPVC: true,
|
||||
expectedBackupPVCStorageClass: "fake-sc-read-only",
|
||||
@@ -502,6 +576,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -517,6 +592,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedBackupPVCStorageClass: "fake-sc-read-only",
|
||||
},
|
||||
@@ -527,6 +603,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -551,6 +628,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedAffinity: &corev1api.Affinity{
|
||||
NodeAffinity: &corev1api.NodeAffinity{
|
||||
@@ -577,6 +655,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -606,6 +685,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedBackupPVCStorageClass: "fake-sc-read-only",
|
||||
expectedAffinity: &corev1api.Affinity{
|
||||
@@ -633,6 +713,7 @@ func TestExpose(t *testing.T) {
|
||||
SnapshotName: "fake-vs",
|
||||
SourceNamespace: "fake-ns",
|
||||
StorageClass: "fake-sc",
|
||||
SourcePVName: "fake-pv",
|
||||
AccessMode: AccessModeFileSystem,
|
||||
OperationTimeout: time.Millisecond,
|
||||
ExposeTimeout: time.Millisecond,
|
||||
@@ -649,6 +730,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedBackupPVCStorageClass: "fake-sc-read-only",
|
||||
expectedAffinity: nil,
|
||||
@@ -677,6 +759,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
kubeReactors: []reactor{
|
||||
{
|
||||
@@ -714,6 +797,7 @@ func TestExpose(t *testing.T) {
|
||||
},
|
||||
kubeClientObj: []runtime.Object{
|
||||
daemonSet,
|
||||
scObj,
|
||||
},
|
||||
expectedAffinity: nil,
|
||||
expectedPVCAnnotation: map[string]string{util.VSphereCNSFastCloneAnno: "true"},
|
||||
@@ -744,6 +828,7 @@ func TestExpose(t *testing.T) {
|
||||
daemonSet,
|
||||
volumeAttachement1,
|
||||
volumeAttachement2,
|
||||
scObj,
|
||||
},
|
||||
expectedAffinity: &corev1api.Affinity{
|
||||
NodeAffinity: &corev1api.NodeAffinity{
|
||||
|
||||
@@ -498,7 +498,7 @@ func (e *genericRestoreExposer) createRestorePod(
|
||||
e.log.Infof("No selected node for restore pod. Try to get affinity from the node-agent config.")
|
||||
|
||||
if affinity != nil {
|
||||
podAffinity = kube.ToSystemAffinity([]*kube.LoadAffinity{affinity})
|
||||
podAffinity = kube.ToSystemAffinity(affinity, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -689,8 +689,7 @@ func buildJob(
|
||||
}
|
||||
|
||||
if config != nil && len(config.LoadAffinities) > 0 {
|
||||
// Maintenance job only takes the first loadAffinity.
|
||||
affinity := kube.ToSystemAffinity([]*kube.LoadAffinity{config.LoadAffinities[0]})
|
||||
affinity := kube.ToSystemAffinity(config.LoadAffinities[0], nil)
|
||||
job.Spec.Template.Spec.Affinity = affinity
|
||||
}
|
||||
|
||||
|
||||
@@ -232,14 +232,9 @@ func CollectPodLogs(ctx context.Context, podGetter corev1client.CoreV1Interface,
|
||||
return nil
|
||||
}
|
||||
|
||||
func ToSystemAffinity(loadAffinities []*LoadAffinity) *corev1api.Affinity {
|
||||
if len(loadAffinities) == 0 {
|
||||
return nil
|
||||
}
|
||||
nodeSelectorTermList := make([]corev1api.NodeSelectorTerm, 0)
|
||||
|
||||
for _, loadAffinity := range loadAffinities {
|
||||
requirements := []corev1api.NodeSelectorRequirement{}
|
||||
func ToSystemAffinity(loadAffinity *LoadAffinity, volumeTopology *corev1api.NodeSelector) *corev1api.Affinity {
|
||||
requirements := []corev1api.NodeSelectorRequirement{}
|
||||
if loadAffinity != nil {
|
||||
for k, v := range loadAffinity.NodeSelector.MatchLabels {
|
||||
requirements = append(requirements, corev1api.NodeSelectorRequirement{
|
||||
Key: k,
|
||||
@@ -255,25 +250,25 @@ func ToSystemAffinity(loadAffinities []*LoadAffinity) *corev1api.Affinity {
|
||||
Operator: corev1api.NodeSelectorOperator(exp.Operator),
|
||||
})
|
||||
}
|
||||
|
||||
nodeSelectorTermList = append(
|
||||
nodeSelectorTermList,
|
||||
corev1api.NodeSelectorTerm{
|
||||
MatchExpressions: requirements,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if len(nodeSelectorTermList) > 0 {
|
||||
result := new(corev1api.Affinity)
|
||||
result.NodeAffinity = new(corev1api.NodeAffinity)
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = new(corev1api.NodeSelector)
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = nodeSelectorTermList
|
||||
result := new(corev1api.Affinity)
|
||||
result.NodeAffinity = new(corev1api.NodeAffinity)
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution = new(corev1api.NodeSelector)
|
||||
|
||||
return result
|
||||
if volumeTopology != nil {
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, volumeTopology.NodeSelectorTerms...)
|
||||
} else if len(requirements) > 0 {
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = make([]corev1api.NodeSelectorTerm, 1)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
for i := range result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
|
||||
result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[i].MatchExpressions = append(result.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[i].MatchExpressions, requirements...)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func DiagnosePod(pod *corev1api.Pod, events *corev1api.EventList) string {
|
||||
|
||||
@@ -747,24 +747,23 @@ func TestCollectPodLogs(t *testing.T) {
|
||||
func TestToSystemAffinity(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
loadAffinities []*LoadAffinity
|
||||
loadAffinity *LoadAffinity
|
||||
volumeTopology *corev1api.NodeSelector
|
||||
expected *corev1api.Affinity
|
||||
}{
|
||||
{
|
||||
name: "loadAffinity is nil",
|
||||
},
|
||||
{
|
||||
name: "loadAffinity is empty",
|
||||
loadAffinities: []*LoadAffinity{},
|
||||
name: "loadAffinity is empty",
|
||||
loadAffinity: &LoadAffinity{},
|
||||
},
|
||||
{
|
||||
name: "with match label",
|
||||
loadAffinities: []*LoadAffinity{
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-1": "value-1",
|
||||
},
|
||||
loadAffinity: &LoadAffinity{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-1": "value-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -788,23 +787,21 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "with match expression",
|
||||
loadAffinities: []*LoadAffinity{
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-2": "value-2",
|
||||
loadAffinity: &LoadAffinity{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-2": "value-2",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -838,19 +835,49 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple load affinities",
|
||||
loadAffinities: []*LoadAffinity{
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-1": "value-1",
|
||||
name: "with olume topology",
|
||||
volumeTopology: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-5",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-6",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-2": "value-2",
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-7",
|
||||
Values: []string{"value-7-1", "value-7-2", "value-7-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-8",
|
||||
Values: []string{"value-8-1", "value-8-2", "value-8-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchFields: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-9",
|
||||
Values: []string{"value-9-1", "value-9-2", "value-9-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-a",
|
||||
Values: []string{"value-a-1", "value-a-2", "value-a-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -862,10 +889,177 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-1",
|
||||
Values: []string{"value-1"},
|
||||
Key: "key-5",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-6",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-7",
|
||||
Values: []string{"value-7-1", "value-7-2", "value-7-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-8",
|
||||
Values: []string{"value-8-1", "value-8-2", "value-8-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchFields: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-9",
|
||||
Values: []string{"value-9-1", "value-9-2", "value-9-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-a",
|
||||
Values: []string{"value-a-1", "value-a-2", "value-a-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with match expression and volume topology",
|
||||
loadAffinity: &LoadAffinity{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-2": "value-2",
|
||||
},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: metav1.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
volumeTopology: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-5",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-6",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-7",
|
||||
Values: []string{"value-7-1", "value-7-2", "value-7-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-8",
|
||||
Values: []string{"value-8-1", "value-8-2", "value-8-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchFields: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-9",
|
||||
Values: []string{"value-9-1", "value-9-2", "value-9-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-a",
|
||||
Values: []string{"value-a-1", "value-a-2", "value-a-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &corev1api.Affinity{
|
||||
NodeAffinity: &corev1api.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-5",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-6",
|
||||
Values: []string{"value-5-1", "value-5-2", "value-5-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-2",
|
||||
Values: []string{"value-2"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: corev1api.NodeSelectorOpNotIn,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: corev1api.NodeSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-7",
|
||||
Values: []string{"value-7-1", "value-7-2", "value-7-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-8",
|
||||
Values: []string{"value-8-1", "value-8-2", "value-8-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-2",
|
||||
Values: []string{"value-2"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: corev1api.NodeSelectorOpNotIn,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: corev1api.NodeSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -875,6 +1069,28 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
Values: []string{"value-2"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
{
|
||||
Key: "key-3",
|
||||
Values: []string{"value-3-1", "value-3-2"},
|
||||
Operator: corev1api.NodeSelectorOpNotIn,
|
||||
},
|
||||
{
|
||||
Key: "key-4",
|
||||
Values: []string{"value-4-1", "value-4-2", "value-4-3"},
|
||||
Operator: corev1api.NodeSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
MatchFields: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-9",
|
||||
Values: []string{"value-9-1", "value-9-2", "value-9-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
{
|
||||
Key: "key-a",
|
||||
Values: []string{"value-a-1", "value-a-2", "value-a-3"},
|
||||
Operator: corev1api.NodeSelectorOpGt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -886,7 +1102,7 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
affinity := ToSystemAffinity(test.loadAffinities)
|
||||
affinity := ToSystemAffinity(test.loadAffinity, test.volumeTopology)
|
||||
assert.True(t, reflect.DeepEqual(affinity, test.expected))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -580,3 +580,29 @@ func GetPVAttachedNodes(ctx context.Context, pv string, storageClient storagev1.
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func GetVolumeTopology(ctx context.Context, volumeClient corev1client.CoreV1Interface, storageClient storagev1.StorageV1Interface, pvName string, scName string) (*corev1api.NodeSelector, error) {
|
||||
if pvName == "" || scName == "" {
|
||||
return nil, errors.Errorf("invalid parameter, pv %s, sc %s", pvName, scName)
|
||||
}
|
||||
|
||||
sc, err := storageClient.StorageClasses().Get(ctx, scName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting storage class %s", scName)
|
||||
}
|
||||
|
||||
if sc.VolumeBindingMode == nil || *sc.VolumeBindingMode != storagev1api.VolumeBindingWaitForFirstConsumer {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pv, err := volumeClient.PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting PV %s", pvName)
|
||||
}
|
||||
|
||||
if pv.Spec.NodeAffinity == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return pv.Spec.NodeAffinity.Required, nil
|
||||
}
|
||||
|
||||
@@ -1909,3 +1909,143 @@ func TestGetPVCAttachingNodeOS(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVolumeTopology(t *testing.T) {
|
||||
pvWithoutNodeAffinity := &corev1api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-pv",
|
||||
},
|
||||
}
|
||||
|
||||
pvWithNodeAffinity := &corev1api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-pv",
|
||||
},
|
||||
Spec: corev1api.PersistentVolumeSpec{
|
||||
NodeAffinity: &corev1api.VolumeNodeAffinity{
|
||||
Required: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "fake-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scObjWithoutVolumeBind := &storagev1api.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-storage-class",
|
||||
},
|
||||
}
|
||||
|
||||
volumeBindImmediate := storagev1api.VolumeBindingImmediate
|
||||
scObjWithImeediateBind := &storagev1api.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-storage-class",
|
||||
},
|
||||
VolumeBindingMode: &volumeBindImmediate,
|
||||
}
|
||||
|
||||
volumeBindWffc := storagev1api.VolumeBindingWaitForFirstConsumer
|
||||
scObjWithWffcBind := &storagev1api.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "fake-storage-class",
|
||||
},
|
||||
VolumeBindingMode: &volumeBindWffc,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pvName string
|
||||
scName string
|
||||
kubeClientObj []runtime.Object
|
||||
expectedErr string
|
||||
expected *corev1api.NodeSelector
|
||||
}{
|
||||
{
|
||||
name: "invalid pvName",
|
||||
scName: "fake-storage-class",
|
||||
expectedErr: "invalid parameter, pv , sc fake-storage-class",
|
||||
},
|
||||
{
|
||||
name: "invalid scName",
|
||||
pvName: "fake-pv",
|
||||
expectedErr: "invalid parameter, pv fake-pv, sc ",
|
||||
},
|
||||
{
|
||||
name: "no sc",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
expectedErr: "error getting storage class fake-storage-class: storageclasses.storage.k8s.io \"fake-storage-class\" not found",
|
||||
},
|
||||
{
|
||||
name: "sc without binding mode",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
kubeClientObj: []runtime.Object{scObjWithoutVolumeBind},
|
||||
},
|
||||
{
|
||||
name: "sc without immediate binding mode",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
kubeClientObj: []runtime.Object{scObjWithImeediateBind},
|
||||
},
|
||||
{
|
||||
name: "get pv fail",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
kubeClientObj: []runtime.Object{scObjWithWffcBind},
|
||||
expectedErr: "error getting PV fake-pv: persistentvolumes \"fake-pv\" not found",
|
||||
},
|
||||
{
|
||||
name: "pv with no affinity",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObjWithWffcBind,
|
||||
pvWithoutNodeAffinity,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pv with affinity",
|
||||
pvName: "fake-pv",
|
||||
scName: "fake-storage-class",
|
||||
kubeClientObj: []runtime.Object{
|
||||
scObjWithWffcBind,
|
||||
pvWithNodeAffinity,
|
||||
},
|
||||
expected: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "fake-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fakeKubeClient := fake.NewSimpleClientset(test.kubeClientObj...)
|
||||
|
||||
var kubeClient kubernetes.Interface = fakeKubeClient
|
||||
|
||||
affinity, err := GetVolumeTopology(t.Context(), kubeClient.CoreV1(), kubeClient.StorageV1(), test.pvName, test.scName)
|
||||
|
||||
if test.expectedErr != "" {
|
||||
assert.EqualError(t, err, test.expectedErr)
|
||||
} else {
|
||||
assert.Equal(t, test.expected, affinity)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ func (n *NodeAgentConfigTestCase) Backup() error {
|
||||
Expect(backupPodList.Items[0].Spec.PriorityClassName).To(Equal(n.nodeAgentConfigs.PriorityClassName))
|
||||
|
||||
// In backup, only the second element of LoadAffinity array should be used.
|
||||
expectedAffinity := velerokubeutil.ToSystemAffinity(n.nodeAgentConfigs.LoadAffinity[1:])
|
||||
expectedAffinity := velerokubeutil.ToSystemAffinity(n.nodeAgentConfigs.LoadAffinity[1], nil)
|
||||
|
||||
Expect(backupPodList.Items[0].Spec.Affinity).To(Equal(expectedAffinity))
|
||||
|
||||
@@ -317,7 +317,7 @@ func (n *NodeAgentConfigTestCase) Restore() error {
|
||||
Expect(restorePodList.Items[0].Spec.PriorityClassName).To(Equal(n.nodeAgentConfigs.PriorityClassName))
|
||||
|
||||
// In restore, only the first element of LoadAffinity array should be used.
|
||||
expectedAffinity := velerokubeutil.ToSystemAffinity(n.nodeAgentConfigs.LoadAffinity[:1])
|
||||
expectedAffinity := velerokubeutil.ToSystemAffinity(n.nodeAgentConfigs.LoadAffinity[0], nil)
|
||||
|
||||
Expect(restorePodList.Items[0].Spec.Affinity).To(Equal(expectedAffinity))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user