mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
Merge branch 'main' into vgdp-ms-pvr-controller
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/utils/clock"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
@@ -63,6 +65,20 @@ const (
|
||||
backupResyncPeriod = time.Minute
|
||||
)
|
||||
|
||||
var autoExcludeNamespaceScopedResources = []string{
|
||||
// CSI VolumeSnapshot and VolumeSnapshotContent are intermediate resources.
|
||||
// Velero only handle the VS and VSC created during backup,
|
||||
// not during resource collecting.
|
||||
"volumesnapshots.snapshot.storage.k8s.io",
|
||||
}
|
||||
|
||||
var autoExcludeClusterScopedResources = []string{
|
||||
// CSI VolumeSnapshot and VolumeSnapshotContent are intermediate resources.
|
||||
// Velero only handle the VS and VSC created during backup,
|
||||
// not during resource collecting.
|
||||
"volumesnapshotcontents.snapshot.storage.k8s.io",
|
||||
}
|
||||
|
||||
type backupReconciler struct {
|
||||
ctx context.Context
|
||||
logger logrus.FieldLogger
|
||||
@@ -481,19 +497,51 @@ func (b *backupReconciler) prepareBackupRequest(backup *velerov1api.Backup, logg
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, validatedError)
|
||||
}
|
||||
|
||||
// validate the included/excluded resources
|
||||
for _, err := range collections.ValidateIncludesExcludes(request.Spec.IncludedResources, request.Spec.ExcludedResources) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded resource lists: %v", err))
|
||||
}
|
||||
if collections.UseOldResourceFilters(request.Spec) {
|
||||
// validate the included/excluded resources
|
||||
ieErr := collections.ValidateIncludesExcludes(request.Spec.IncludedResources, request.Spec.ExcludedResources)
|
||||
if len(ieErr) > 0 {
|
||||
for _, err := range ieErr {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded resource lists: %v", err))
|
||||
}
|
||||
} else {
|
||||
request.Spec.IncludedResources, request.Spec.ExcludedResources =
|
||||
modifyResourceIncludeExclude(
|
||||
request.Spec.IncludedResources,
|
||||
request.Spec.ExcludedResources,
|
||||
append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// validate the cluster-scoped included/excluded resources
|
||||
clusterErr := collections.ValidateScopedIncludesExcludes(request.Spec.IncludedClusterScopedResources, request.Spec.ExcludedClusterScopedResources)
|
||||
if len(clusterErr) > 0 {
|
||||
for _, err := range clusterErr {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid cluster-scoped included/excluded resource lists: %s", err))
|
||||
}
|
||||
} else {
|
||||
request.Spec.IncludedClusterScopedResources, request.Spec.ExcludedClusterScopedResources =
|
||||
modifyResourceIncludeExclude(
|
||||
request.Spec.IncludedClusterScopedResources,
|
||||
request.Spec.ExcludedClusterScopedResources,
|
||||
autoExcludeClusterScopedResources,
|
||||
)
|
||||
}
|
||||
|
||||
// validate the cluster-scoped included/excluded resources
|
||||
for _, err := range collections.ValidateScopedIncludesExcludes(request.Spec.IncludedClusterScopedResources, request.Spec.ExcludedClusterScopedResources) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid cluster-scoped included/excluded resource lists: %s", err))
|
||||
}
|
||||
|
||||
// validate the namespace-scoped included/excluded resources
|
||||
for _, err := range collections.ValidateScopedIncludesExcludes(request.Spec.IncludedNamespaceScopedResources, request.Spec.ExcludedNamespaceScopedResources) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid namespace-scoped included/excluded resource lists: %s", err))
|
||||
// validate the namespace-scoped included/excluded resources
|
||||
namespaceErr := collections.ValidateScopedIncludesExcludes(request.Spec.IncludedNamespaceScopedResources, request.Spec.ExcludedNamespaceScopedResources)
|
||||
if len(namespaceErr) > 0 {
|
||||
for _, err := range namespaceErr {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid namespace-scoped included/excluded resource lists: %s", err))
|
||||
}
|
||||
} else {
|
||||
request.Spec.IncludedNamespaceScopedResources, request.Spec.ExcludedNamespaceScopedResources =
|
||||
modifyResourceIncludeExclude(
|
||||
request.Spec.IncludedNamespaceScopedResources,
|
||||
request.Spec.ExcludedNamespaceScopedResources,
|
||||
autoExcludeNamespaceScopedResources,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// validate the included/excluded namespaces
|
||||
@@ -932,3 +980,25 @@ func oldAndNewFilterParametersUsedTogether(backupSpec velerov1api.BackupSpec) bo
|
||||
|
||||
return haveOldResourceFilterParameters && haveNewResourceFilterParameters
|
||||
}
|
||||
|
||||
func modifyResourceIncludeExclude(include, exclude, addedExclude []string) (modifiedInclude, modifiedExclude []string) {
|
||||
modifiedInclude = include
|
||||
modifiedExclude = exclude
|
||||
|
||||
excludeStrSet := sets.NewString(exclude...)
|
||||
for _, ex := range addedExclude {
|
||||
if !excludeStrSet.Has(ex) {
|
||||
modifiedExclude = append(modifiedExclude, ex)
|
||||
}
|
||||
}
|
||||
|
||||
for _, exElem := range modifiedExclude {
|
||||
for inIndex, inElem := range modifiedInclude {
|
||||
if inElem == exElem {
|
||||
modifiedInclude = slices.Delete(modifiedInclude, inIndex, inIndex+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedInclude, modifiedExclude
|
||||
}
|
||||
|
||||
@@ -708,6 +708,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -745,6 +746,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: "alt-loc",
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -786,6 +788,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: "read-write",
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -824,6 +827,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -862,6 +866,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -901,6 +906,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -940,6 +946,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -979,6 +986,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1018,6 +1026,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1058,6 +1067,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
@@ -1098,6 +1108,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.True(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
@@ -1138,6 +1149,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.True(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1179,6 +1191,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1220,6 +1233,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1261,6 +1275,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.True(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1303,6 +1318,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.False(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
@@ -1344,6 +1360,105 @@ func TestProcessBackupCompletions(t *testing.T) {
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.True(),
|
||||
ExcludedResources: append(autoExcludeNamespaceScopedResources, autoExcludeClusterScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
CSIVolumeSnapshotsAttempted: 0,
|
||||
CSIVolumeSnapshotsCompleted: 0,
|
||||
},
|
||||
},
|
||||
volumeSnapshot: builder.ForVolumeSnapshot("velero", "testVS").VolumeSnapshotClass("testClass").Status().BoundVolumeSnapshotContentName("testVSC").RestoreSize("10G").SourcePVC("testPVC").ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, "backup-1")).Result(),
|
||||
},
|
||||
{
|
||||
name: "backup with namespace-scoped and cluster-scoped resource filters",
|
||||
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").
|
||||
ExcludedClusterScopedResources("clusterroles").
|
||||
IncludedClusterScopedResources("storageclasses").
|
||||
ExcludedNamespaceScopedResources("secrets").
|
||||
IncludedNamespaceScopedResources("pods").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToFsBackup: false,
|
||||
defaultSnapshotMoveData: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
"velero.io/resource-timeout": "0s",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.True(),
|
||||
IncludedClusterScopedResources: []string{"storageclasses"},
|
||||
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
|
||||
IncludedNamespaceScopedResources: []string{"pods"},
|
||||
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
CSIVolumeSnapshotsAttempted: 0,
|
||||
CSIVolumeSnapshotsCompleted: 0,
|
||||
},
|
||||
},
|
||||
volumeSnapshot: builder.ForVolumeSnapshot("velero", "testVS").VolumeSnapshotClass("testClass").Status().BoundVolumeSnapshotContentName("testVSC").RestoreSize("10G").SourcePVC("testPVC").ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, "backup-1")).Result(),
|
||||
},
|
||||
{
|
||||
name: "backup's include filter overlap with default exclude resources",
|
||||
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").
|
||||
ExcludedClusterScopedResources("clusterroles").
|
||||
IncludedClusterScopedResources("storageclasses", "volumesnapshotcontents.snapshot.storage.k8s.io").
|
||||
ExcludedNamespaceScopedResources("secrets").
|
||||
IncludedNamespaceScopedResources("pods", "volumesnapshots.snapshot.storage.k8s.io").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
defaultVolumesToFsBackup: false,
|
||||
defaultSnapshotMoveData: true,
|
||||
expectedResult: &velerov1api.Backup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Backup",
|
||||
APIVersion: "velero.io/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "backup-1",
|
||||
Annotations: map[string]string{
|
||||
"velero.io/source-cluster-k8s-major-version": "1",
|
||||
"velero.io/source-cluster-k8s-minor-version": "16",
|
||||
"velero.io/source-cluster-k8s-gitversion": "v1.16.4",
|
||||
"velero.io/resource-timeout": "0s",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"velero.io/storage-location": "loc-1",
|
||||
},
|
||||
},
|
||||
Spec: velerov1api.BackupSpec{
|
||||
StorageLocation: defaultBackupLocation.Name,
|
||||
DefaultVolumesToFsBackup: boolptr.False(),
|
||||
SnapshotMoveData: boolptr.True(),
|
||||
IncludedClusterScopedResources: []string{"storageclasses"},
|
||||
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
|
||||
IncludedNamespaceScopedResources: []string{"pods"},
|
||||
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),
|
||||
},
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFinalizing,
|
||||
|
||||
Reference in New Issue
Block a user