mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-06-10 00:03:10 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 323900adcc | |||
| 317ffd069f | |||
| d6d9e4ee16 |
@@ -1 +0,0 @@
|
||||
Skip VGS cleanup when backup did not use VolumeGroupSnapshots
|
||||
@@ -0,0 +1 @@
|
||||
Make ToSystemAffinity deterministic by sorting MatchLabels keys to avoid spurious affinity spec diffs and restarts
|
||||
@@ -301,10 +301,8 @@ func (ctx *finalizerContext) execute() (results.Result, results.Result) {
|
||||
pdpErrs := ctx.patchDynamicPVWithVolumeInfo()
|
||||
errs.Merge(&pdpErrs)
|
||||
|
||||
if ctx.hasVolumeGroupSnapshotHandles() {
|
||||
vgscWarnings := ctx.cleanupStubVGSC()
|
||||
warnings.Merge(&vgscWarnings)
|
||||
}
|
||||
vgscWarnings := ctx.cleanupStubVGSC()
|
||||
warnings.Merge(&vgscWarnings)
|
||||
|
||||
rehErrs := ctx.WaitRestoreExecHook()
|
||||
errs.Merge(&rehErrs)
|
||||
@@ -451,15 +449,6 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result
|
||||
return errs
|
||||
}
|
||||
|
||||
func (ctx *finalizerContext) hasVolumeGroupSnapshotHandles() bool {
|
||||
for _, vi := range ctx.volumeInfo {
|
||||
if vi.CSISnapshotInfo != nil && vi.CSISnapshotInfo.VolumeGroupSnapshotHandle != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// cleanupStubVGSC deletes stub VolumeGroupSnapshotContent objects that were
|
||||
// created during restore to satisfy CSI controller validation. These stubs are
|
||||
// labeled with velero.io/restore-name for identification.
|
||||
|
||||
@@ -743,83 +743,6 @@ func TestRestoreOperationList(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasVolumeGroupSnapshotHandles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "nil volumeInfo",
|
||||
volumeInfo: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty volumeInfo",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "no CSISnapshotInfo",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{PVCName: "pvc-1", BackupMethod: volume.NativeSnapshot},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "CSISnapshotInfo with empty VolumeGroupSnapshotHandle",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
SnapshotHandle: "snap-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "one volume with VolumeGroupSnapshotHandle",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
SnapshotHandle: "snap-1",
|
||||
VolumeGroupSnapshotHandle: "vgs-handle-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "mixed volumes only one with VolumeGroupSnapshotHandle",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{PVCName: "pvc-1", BackupMethod: volume.NativeSnapshot},
|
||||
{
|
||||
PVCName: "pvc-2",
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
SnapshotHandle: "snap-2",
|
||||
VolumeGroupSnapshotHandle: "vgs-handle-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := &finalizerContext{
|
||||
volumeInfo: tc.volumeInfo,
|
||||
}
|
||||
assert.Equal(t, tc.expected, ctx.hasVolumeGroupSnapshotHandles())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanupStubVGSC(t *testing.T) {
|
||||
snapshotHandle1 := "snap-handle-1"
|
||||
snapshotHandle2 := "snap-handle-2"
|
||||
|
||||
+15
-1
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -236,7 +237,20 @@ func CollectPodLogs(ctx context.Context, podGetter corev1client.CoreV1Interface,
|
||||
func ToSystemAffinity(loadAffinity *LoadAffinity, volumeTopology *corev1api.NodeSelector) *corev1api.Affinity {
|
||||
requirements := []corev1api.NodeSelectorRequirement{}
|
||||
if loadAffinity != nil {
|
||||
for k, v := range loadAffinity.NodeSelector.MatchLabels {
|
||||
// MatchLabels is a map, so its iteration order is not deterministic.
|
||||
// Sort the keys so the generated requirements (and therefore the
|
||||
// resulting affinity) have a stable order. This output may be embedded
|
||||
// into objects that are reconciled continuously (e.g. DaemonSet pod
|
||||
// templates), where an order-only difference would be treated as a spec
|
||||
// change and trigger unnecessary rollouts/restarts.
|
||||
keys := make([]string, 0, len(loadAffinity.NodeSelector.MatchLabels))
|
||||
for k := range loadAffinity.NodeSelector.MatchLabels {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := loadAffinity.NodeSelector.MatchLabels[k]
|
||||
requirements = append(requirements, corev1api.NodeSelectorRequirement{
|
||||
Key: k,
|
||||
Values: []string{v},
|
||||
|
||||
@@ -834,6 +834,45 @@ func TestToSystemAffinity(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with multiple match labels are sorted by key",
|
||||
loadAffinity: &LoadAffinity{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"key-c": "value-c",
|
||||
"key-a": "value-a",
|
||||
"key-b": "value-b",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &corev1api.Affinity{
|
||||
NodeAffinity: &corev1api.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &corev1api.NodeSelector{
|
||||
NodeSelectorTerms: []corev1api.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []corev1api.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "key-a",
|
||||
Values: []string{"value-a"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
{
|
||||
Key: "key-b",
|
||||
Values: []string{"value-b"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
{
|
||||
Key: "key-c",
|
||||
Values: []string{"value-c"},
|
||||
Operator: corev1api.NodeSelectorOpIn,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with olume topology",
|
||||
volumeTopology: &corev1api.NodeSelector{
|
||||
|
||||
Reference in New Issue
Block a user