mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-03 22:57:04 +00:00
Merge branch 'main' of https://github.com/qiuming-best/velero into uploader-restic
This commit is contained in:
@@ -41,7 +41,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
|
||||
@@ -440,7 +439,7 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba
|
||||
return nil
|
||||
}
|
||||
|
||||
snapshots, err := restic.GetSnapshotsInBackup(ctx, backup, r.Client)
|
||||
snapshots, err := getSnapshotsInBackup(ctx, backup, r.Client)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
@@ -491,3 +490,33 @@ func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *vele
|
||||
}
|
||||
return backup, nil
|
||||
}
|
||||
|
||||
// getSnapshotsInBackup returns a list of all restic snapshot ids associated with
|
||||
// a given Velero backup.
|
||||
func getSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]repository.SnapshotIdentifier, error) {
|
||||
podVolumeBackups := &velerov1api.PodVolumeBackupList{}
|
||||
options := &client.ListOptions{
|
||||
LabelSelector: labels.Set(map[string]string{
|
||||
velerov1api.BackupNameLabel: label.GetValidName(backup.Name),
|
||||
}).AsSelector(),
|
||||
}
|
||||
|
||||
err := kbClient.List(ctx, podVolumeBackups, options)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var res []repository.SnapshotIdentifier
|
||||
for _, item := range podVolumeBackups.Items {
|
||||
if item.Status.SnapshotID == "" {
|
||||
continue
|
||||
}
|
||||
res = append(res, repository.SnapshotIdentifier{
|
||||
VolumeNamespace: item.Spec.Pod.Namespace,
|
||||
BackupStorageLocation: backup.Spec.StorageLocation,
|
||||
SnapshotID: item.Status.SnapshotID,
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package controller
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -52,6 +54,7 @@ import (
|
||||
persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
@@ -692,3 +695,172 @@ func TestBackupDeletionControllerReconcile(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSnapshotsInBackup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
podVolumeBackups []velerov1api.PodVolumeBackup
|
||||
expected []repository.SnapshotIdentifier
|
||||
longBackupNameEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "no pod volume backups",
|
||||
podVolumeBackups: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "no pod volume backups with matching label",
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "some pod volume backups with matching label",
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""},
|
||||
},
|
||||
},
|
||||
expected: []repository.SnapshotIdentifier{
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-3",
|
||||
},
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-4",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some pod volume backups with matching label and backup name greater than 63 chars",
|
||||
longBackupNameEnabled: true,
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""},
|
||||
},
|
||||
},
|
||||
expected: []repository.SnapshotIdentifier{
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
clientBuilder = velerotest.NewFakeControllerRuntimeClientBuilder(t)
|
||||
veleroBackup = &velerov1api.Backup{}
|
||||
)
|
||||
|
||||
veleroBackup.Name = "backup-1"
|
||||
|
||||
if test.longBackupNameEnabled {
|
||||
veleroBackup.Name = "the-really-long-backup-name-that-is-much-more-than-63-characters"
|
||||
}
|
||||
clientBuilder.WithLists(&velerov1api.PodVolumeBackupList{
|
||||
Items: test.podVolumeBackups,
|
||||
})
|
||||
|
||||
res, err := getSnapshotsInBackup(context.TODO(), veleroBackup, clientBuilder.Build())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// sort to ensure good compare of slices
|
||||
less := func(snapshots []repository.SnapshotIdentifier) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
if snapshots[i].VolumeNamespace == snapshots[j].VolumeNamespace {
|
||||
return snapshots[i].SnapshotID < snapshots[j].SnapshotID
|
||||
}
|
||||
return snapshots[i].VolumeNamespace < snapshots[j].VolumeNamespace
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sort.Slice(test.expected, less(test.expected))
|
||||
sort.Slice(res, less(res))
|
||||
|
||||
assert.Equal(t, test.expected, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/uploader"
|
||||
@@ -108,7 +109,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
resticInitContainerIndex := getResticInitContainerIndex(pod)
|
||||
if resticInitContainerIndex > 0 {
|
||||
log.Warnf(`Init containers before the %s container may cause issues
|
||||
if they interfere with volumes being restored: %s index %d`, restic.InitContainer, restic.InitContainer, resticInitContainerIndex)
|
||||
if they interfere with volumes being restored: %s index %d`, podvolume.InitContainer, podvolume.InitContainer, resticInitContainerIndex)
|
||||
}
|
||||
|
||||
log.Info("Restore starting")
|
||||
@@ -218,7 +219,7 @@ func isResticInitContainerRunning(pod *corev1api.Pod) bool {
|
||||
func getResticInitContainerIndex(pod *corev1api.Pod) int {
|
||||
// Restic wait container can be anywhere in the list of init containers so locate it.
|
||||
for i, initContainer := range pod.Spec.InitContainers {
|
||||
if initContainer.Name == restic.InitContainer {
|
||||
if initContainer.Name == podvolume.InitContainer {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestShouldProcess(t *testing.T) {
|
||||
NodeName: controllerNode,
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -160,7 +160,7 @@ func TestShouldProcess(t *testing.T) {
|
||||
NodeName: controllerNode,
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -260,7 +260,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Name: "non-restic-init",
|
||||
},
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -291,7 +291,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -323,7 +323,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -357,7 +357,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -422,7 +422,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Name: "non-restic-init",
|
||||
},
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -439,7 +439,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -459,7 +459,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
|
||||
Reference in New Issue
Block a user