Merge branch 'main' into save-source-size-to-backup

This commit is contained in:
Lyndon-Li
2026-09-09 15:50:06 +08:00
45 changed files with 983 additions and 186 deletions
+1
View File
@@ -136,6 +136,7 @@ func (p *pvcBackupItemAction) getVolumeHelperWithCache(backup *velerov1api.Backu
p.crClient,
p.log,
p.pvcPodCache,
nil,
)
if err != nil {
return nil, errors.Wrap(err, "failed to create VolumeHelper")
+3
View File
@@ -485,6 +485,8 @@ func (kb *kubernetesBackupper) BackupWithResolvers(
return err
}
pvcMustInclusionTracker := NewPVCMustInclusionTracker(backupRequest.MustIncludeAdditionalItemPVCs)
volumeHelperImpl, err := volumehelper.NewVolumeHelperImplWithNamespaces(
backupRequest.ResPolicies,
backupRequest.Spec.SnapshotVolumes,
@@ -493,6 +495,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(
boolptr.IsSetToTrue(backupRequest.Spec.DefaultVolumesToFsBackup),
!backupRequest.ResourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumeClaims.String()),
namespaces,
pvcMustInclusionTracker,
)
if err != nil {
log.WithError(err).Error("Failed to build PVC-to-Pod cache for volume policy lookups")
+4 -4
View File
@@ -5666,7 +5666,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
PVCName: "pvc-1",
PVCNamespace: "ns-1",
CompletionTimestamp: &metav1.Time{},
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
},
},
@@ -5677,7 +5677,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
PVCNamespace: "ns-1",
CompletionTimestamp: &now,
Result: volume.VolumeResultFailed,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
RetainedSnapshot: "vs-1",
SnapshotHandle: "snapshot-id",
@@ -5706,7 +5706,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
PVCName: "pvc-1",
PVCNamespace: "ns-1",
CompletionTimestamp: &metav1.Time{},
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
},
},
@@ -5717,7 +5717,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
PVCNamespace: "ns-1",
CompletionTimestamp: &now,
Result: volume.VolumeResultSucceeded,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
RetainedSnapshot: "vs-1",
SnapshotHandle: "snapshot-id",
+21
View File
@@ -245,6 +245,7 @@ func (ib *itemBackupper) backupItemInternal(logger logrus.FieldLogger, obj runti
// where it's been backed up from another pod), since we don't need >1 backup per PVC.
for _, volume := range pod.Spec.Volumes {
shouldDoFSBackup, err := ib.volumeHelperImpl.ShouldPerformFSBackup(volume, *pod)
if err != nil {
backupErrs = append(backupErrs, errors.WithStack(err))
}
@@ -480,6 +481,26 @@ func (ib *itemBackupper) executeActions(
delete(u.GetAnnotations(), velerov1api.MustIncludeAdditionalItemAnnotation)
obj = u
// If the BIA specifies that additional items must be included, we track any PVCs returned as additional items.
// This tracking is necessary because the FSB (File System Backup) evaluation for a Pod
// happens before its PVCs are processed. By tracking these explicitly included PVCs here,
// the FSB logic can correctly determine that the PVC will be backed up and therefore
// a PodVolumeBackup should be created.
// We track this unconditionally when mustInclude is true, because fine-grained backup filters
// might exclude a PVC even if it's globally included, but mustInclude overrides those filters.
if mustInclude && ib.backupRequest.MustIncludeAdditionalItemPVCs != nil {
for _, additionalItem := range additionalItemIdentifiers {
if additionalItem.GroupResource == kuberesource.PersistentVolumeClaims {
key := itemKey{
resource: additionalItem.GroupResource.String(),
namespace: additionalItem.Namespace,
name: additionalItem.Name,
}
ib.backupRequest.MustIncludeAdditionalItemPVCs.AddItem(key)
}
}
}
// If async plugin started async operation, add it to the ItemOperations list
// ignore during finalize phase
if operationID != "" {
+50
View File
@@ -0,0 +1,50 @@
/*
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package backup
import (
"github.com/vmware-tanzu/velero/pkg/kuberesource"
vhutil "github.com/vmware-tanzu/velero/pkg/util/volumehelper"
)
// pvcMustInclusionTracker provides read-only checks for whether a PVC is included
// in the backup as BIA's additionalItems through annotation
// backup.velero.io/must-include-additional-items.
type pvcMustInclusionTracker struct {
mustInclude *backedUpItemsMap
}
func NewPVCMustInclusionTracker(mustInclude *backedUpItemsMap) vhutil.PVCMustInclusionTracker {
return &pvcMustInclusionTracker{
mustInclude: mustInclude,
}
}
func (p *pvcMustInclusionTracker) IsPVCIncluded(namespace, pvcName string) bool {
pvcKey := itemKey{
resource: kuberesource.PersistentVolumeClaims.String(),
namespace: namespace,
name: pvcName,
}
// 1. If the PVC was explicitly forced into the backup by a BIA, it will be backed up.
if p.mustInclude != nil && p.mustInclude.Has(pvcKey) {
return true
}
return false
}
@@ -0,0 +1,47 @@
/*
Copyright the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package backup
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/vmware-tanzu/velero/pkg/kuberesource"
)
func TestPVCMustInclusionTracker_IsPVCIncluded(t *testing.T) {
mustIncludeMap := NewBackedUpItemsMap()
tracker := NewPVCMustInclusionTracker(mustIncludeMap)
pvcKey1 := itemKey{
resource: kuberesource.PersistentVolumeClaims.String(),
namespace: "ns-1",
name: "pvc-1",
}
// Initially neither PVC is included
assert.False(t, tracker.IsPVCIncluded("ns-1", "pvc-1"))
// Add pvc-1 to mustInclude map
mustIncludeMap.AddItem(pvcKey1)
assert.True(t, tracker.IsPVCIncluded("ns-1", "pvc-1"))
// Check a PVC not in any map
assert.False(t, tracker.IsPVCIncluded("ns-1", "pvc-2"))
}
+10 -5
View File
@@ -83,11 +83,16 @@ type Request struct {
VolumeSnapshots SynchronizedVSList
PodVolumeBackups []*velerov1api.PodVolumeBackup
BackedUpItems *backedUpItemsMap
itemOperationsList *[]*itemoperation.BackupOperation
ResPolicies *resourcepolicies.Policies
SkippedPVTracker *skipPVTracker
VolumesInformation volume.BackupVolumesInformation
WorkerPool *ItemBlockWorkerPool
// MustIncludeAdditionalItemPVCs keeps track of PVCs that are returned as additionalItems
// by a BackupItemAction plugin with the must-include annotation. This is specifically
// used to ensure PodVolumeBackups (FSB) are created for these PVCs even when PVCs are
// excluded by global or fine-grained backup resource filters.
MustIncludeAdditionalItemPVCs *backedUpItemsMap
itemOperationsList *[]*itemoperation.BackupOperation
ResPolicies *resourcepolicies.Policies
SkippedPVTracker *skipPVTracker
VolumesInformation volume.BackupVolumesInformation
WorkerPool *ItemBlockWorkerPool
// ClusterScopedFilterMap holds resolved global filters for cluster-scoped resources.
// Key is the resolved group-resource string.
+6
View File
@@ -61,6 +61,12 @@ func (d *DataDownloadBuilder) Phase(phase velerov2alpha1api.DataDownloadPhase) *
return d
}
// RestoreType sets the DataDownload's RestoreType.
func (d *DataDownloadBuilder) RestoreType(restoreType string) *DataDownloadBuilder {
d.object.Spec.RestoreType = restoreType
return d
}
// SnapshotID sets the DataDownload's SnapshotID.
func (d *DataDownloadBuilder) SnapshotID(id string) *DataDownloadBuilder {
d.object.Spec.SnapshotID = id
+8
View File
@@ -249,6 +249,10 @@ func DescribeBackupSpec(d *Describer, spec velerov1api.BackupSpec) {
}
d.Printf("Data Mover:\t%s\n", s)
if string(spec.BackupType) != "" {
d.Printf("Backup Type:\t%s\n", spec.BackupType)
}
d.Println()
d.Printf("TTL:\t%s\n", spec.TTL.Duration)
@@ -746,6 +750,10 @@ func describeDataMovement(d *Describer, details bool, info *volume.BackupVolumeI
if info.SnapshotDataMovementInfo.IncrementalSize != nil {
d.Printf("\t\t\t\tIncremental data Size (bytes): %d\n", *info.SnapshotDataMovementInfo.IncrementalSize)
}
if info.SnapshotDataMovementInfo.ParentSnapshot != "" {
d.Printf("\t\t\t\tParent Snapshot: %s\n", info.SnapshotDataMovementInfo.ParentSnapshot)
}
d.Printf("\t\t\t\tResult: %s\n", info.Result)
} else {
d.Printf("\t\t\tData Movement: %s\n", "included, specify --details for more information")
+8 -3
View File
@@ -108,6 +108,7 @@ func TestDescribeBackupSpec(t *testing.T) {
TTL(72 * time.Hour).
CSISnapshotTimeout(10 * time.Minute).
DataMover("mover").
BackupType(velerov1api.BackupTypeFull).
Hooks(velerov1api.BackupHooks{
Resources: []velerov1api.BackupResourceHookSpec{
{
@@ -156,6 +157,7 @@ Storage Location: backup-location
Velero-Native Snapshot PVs: auto
Snapshot Move Data: auto
Data Mover: mover
Backup Type: Full
TTL: 72h0m0s
@@ -575,7 +577,7 @@ func TestCSISnapshots(t *testing.T) {
PVCNamespace: "pvc-ns-3",
PVCName: "pvc-3",
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-3",
@@ -597,7 +599,7 @@ func TestCSISnapshots(t *testing.T) {
PVCName: "pvc-4",
SnapshotDataMoved: true,
Result: volume.VolumeResultSucceeded,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-4",
@@ -625,13 +627,15 @@ func TestCSISnapshots(t *testing.T) {
PVCName: "pvc-5",
Result: volume.VolumeResultFailed,
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
BackupType: velerov1api.BackupTypeIncremental,
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-5",
OperationID: "fake-operation-5",
Size: 100,
IncrementalSize: ptr.To(int64(50)),
Phase: velerov2alpha1.DataUploadPhaseFailed,
ParentSnapshot: "fake-parent-snapshot",
},
},
},
@@ -644,6 +648,7 @@ func TestCSISnapshots(t *testing.T) {
Uploader Type: fake-uploader
Moved data Size (bytes): 100
Incremental data Size (bytes): 50
Parent Snapshot: fake-parent-snapshot
Result: failed
`,
},
@@ -136,6 +136,9 @@ func DescribeBackupSpecInSF(d *StructuredDescriber, spec velerov1api.BackupSpec)
s = spec.DataMover
}
backupSpecInfo["dataMover"] = s
if string(spec.BackupType) != "" {
backupSpecInfo["backupType"] = spec.BackupType
}
// describe TTL
backupSpecInfo["TTL"] = spec.TTL.Duration.String()
@@ -475,6 +478,9 @@ func describeDataMovementInSF(details bool, info *volume.BackupVolumeInfo, snaps
if info.SnapshotDataMovementInfo.IncrementalSize != nil {
dataMovement["incrementalSize"] = *info.SnapshotDataMovementInfo.IncrementalSize
}
if info.SnapshotDataMovementInfo.ParentSnapshot != "" {
dataMovement["parentSnapshot"] = info.SnapshotDataMovementInfo.ParentSnapshot
}
snapshotDetail["dataMovement"] = dataMovement
} else {
@@ -24,9 +24,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/util/results"
)
@@ -45,6 +47,7 @@ func TestDescribeBackupInSF(t *testing.T) {
TTL(72 * time.Hour).
CSISnapshotTimeout(10 * time.Minute).
DataMover("mover").
BackupType(velerov1api.BackupTypeFull).
Hooks(velerov1api.BackupHooks{
Resources: []velerov1api.BackupResourceHookSpec{
{
@@ -87,6 +90,7 @@ func TestDescribeBackupInSF(t *testing.T) {
"clusterScoped": "auto",
},
"dataMover": "mover",
"backupType": velerov1api.BackupTypeFull,
"labelSelector": emptyDisplay,
"storageLocation": "backup-location",
"veleroNativeSnapshotPVs": "auto",
@@ -517,7 +521,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
PVCNamespace: "pvc-ns-3",
PVCName: "pvc-3",
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-3",
@@ -542,7 +546,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
PVCName: "pvc-4",
SnapshotDataMoved: true,
Result: volume.VolumeResultSucceeded,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-4",
@@ -573,10 +577,15 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
Result: volume.VolumeResultFailed,
PVCName: "pvc-4",
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-4",
OperationID: "fake-operation-4",
BackupType: velerov1api.BackupTypeIncremental,
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
UploaderType: "fake-uploader",
SnapshotHandle: "fake-repo-id-4",
OperationID: "fake-operation-4",
Size: 100,
IncrementalSize: ptr.To(int64(50)),
Phase: velerov2alpha1.DataUploadPhaseFailed,
ParentSnapshot: "fake-parent-snapshot",
},
},
},
@@ -585,10 +594,13 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
"csiSnapshots": map[string]any{
"pvc-ns-4/pvc-4": map[string]any{
"dataMovement": map[string]any{
"operationID": "fake-operation-4",
"dataMover": "velero",
"uploaderType": "fake-uploader",
"result": "failed",
"operationID": "fake-operation-4",
"dataMover": "velero",
"uploaderType": "fake-uploader",
"size": int64(100),
"incrementalSize": int64(50),
"result": "failed",
"parentSnapshot": "fake-parent-snapshot",
},
},
},
+14
View File
@@ -209,6 +209,11 @@ func DescribeRestore(
s = string(restore.Spec.ExistingResourcePolicy)
}
d.Printf("Existing Resource Policy: \t%s\n", s)
s = emptyDisplay
if restore.Spec.ExistingVolumeDataPolicy != "" {
s = string(restore.Spec.ExistingVolumeDataPolicy)
}
d.Printf("Existing Volume Data Policy: \t%s\n", s)
d.Printf("ItemOperationTimeout:\t%s\n", restore.Spec.ItemOperationTimeout.Duration)
d.Println()
@@ -468,6 +473,15 @@ func describeCSISnapshotsRestores(d *Describer, restoreVolInfo []volume.RestoreV
d.Printf("\t\t\tOperation ID: %s\n", info.SnapshotDataMovementInfo.OperationID)
d.Printf("\t\t\tData Mover: %s\n", info.SnapshotDataMovementInfo.DataMover)
d.Printf("\t\t\tUploader Type: %s\n", info.SnapshotDataMovementInfo.UploaderType)
if info.SnapshotDataMovementInfo.RestoreType != "" {
d.Printf("\t\t\tRestore Type: %s\n", info.SnapshotDataMovementInfo.RestoreType)
}
if info.SnapshotDataMovementInfo.Size > 0 {
d.Printf("\t\t\tRestored data Size (bytes): %d\n", info.SnapshotDataMovementInfo.Size)
}
if info.SnapshotDataMovementInfo.IncrementalSize != nil {
d.Printf("\t\t\tIncremental data Size (bytes): %d\n", *info.SnapshotDataMovementInfo.IncrementalSize)
}
} else {
d.Printf("\t\tData Movement: specify --details for more information\n")
}
+27 -6
View File
@@ -2,6 +2,7 @@ package output
import (
"bytes"
"context"
"fmt"
"testing"
"text/tabwriter"
@@ -10,11 +11,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
"github.com/vmware-tanzu/velero/pkg/util/results"
)
@@ -309,11 +312,13 @@ CSI Snapshot Restores:
PVName: "pv-3",
RestoreMethod: volume.CSISnapshot,
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
Size: 1234,
SnapshotDataMovementInfo: &volume.RestoreSnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
Size: 1234,
IncrementalSize: ptr.To(int64(500)),
RestoreType: "Incremental",
},
},
},
@@ -325,6 +330,9 @@ CSI Snapshot Restores:
Operation ID: op-3
Data Mover: velero
Uploader Type: kopia
Restore Type: Incremental
Restored data Size (bytes): 1234
Incremental data Size (bytes): 500
`,
},
{
@@ -336,7 +344,7 @@ CSI Snapshot Restores:
PVName: "pv-3",
RestoreMethod: volume.CSISnapshot,
SnapshotDataMoved: true,
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.RestoreSnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
@@ -415,3 +423,16 @@ func TestDescribeResourceModifier(t *testing.T) {
fmt.Println(d.buf.String())
require.Equal(t, expectOutput, d.buf.String())
}
func TestDescribeRestore(t *testing.T) {
kbClient := velerotest.NewFakeControllerRuntimeClient(t)
restore := builder.ForRestore("velero", "test-restore").
Backup("test-backup").
ExistingResourcePolicy(string(velerov1api.ResourcePolicyTypeUpdate)).
ExistingVolumeDataPolicy(string(velerov1api.VolumeDataPolicyTypeFull)).
Result()
out := DescribeRestore(context.Background(), kbClient, restore, nil, false, false, "")
assert.Contains(t, out, "Existing Resource Policy: update")
assert.Contains(t, out, "Existing Volume Data Policy: full")
}
@@ -196,6 +196,13 @@ func describeRestoreSpecInSF(d *StructuredDescriber, spec velerov1api.RestoreSpe
specInfo["existingResourcePolicy"] = emptyDisplay
}
// existing volume data policy
if spec.ExistingVolumeDataPolicy != "" {
specInfo["existingVolumeDataPolicy"] = string(spec.ExistingVolumeDataPolicy)
} else {
specInfo["existingVolumeDataPolicy"] = emptyDisplay
}
specInfo["itemOperationTimeout"] = spec.ItemOperationTimeout.Duration.String()
specInfo["preserveNodePorts"] = BoolPointerString(spec.PreserveNodePorts, "false", "true", "auto")
@@ -360,12 +367,22 @@ func describeCSISnapshotsRestoresInSF(d *StructuredDescriber, restoreVolInfo []v
}
continue
}
dmInfo := map[string]any{
"operationID": info.SnapshotDataMovementInfo.OperationID,
"dataMover": info.SnapshotDataMovementInfo.DataMover,
"uploaderType": info.SnapshotDataMovementInfo.UploaderType,
}
if info.SnapshotDataMovementInfo.RestoreType != "" {
dmInfo["restoreType"] = info.SnapshotDataMovementInfo.RestoreType
}
if info.SnapshotDataMovementInfo.Size > 0 {
dmInfo["size"] = info.SnapshotDataMovementInfo.Size
}
if info.SnapshotDataMovementInfo.IncrementalSize != nil {
dmInfo["incrementalSize"] = *info.SnapshotDataMovementInfo.IncrementalSize
}
csiRestores[key] = map[string]any{
"dataMovement": map[string]any{
"operationID": info.SnapshotDataMovementInfo.OperationID,
"dataMover": info.SnapshotDataMovementInfo.DataMover,
"uploaderType": info.SnapshotDataMovementInfo.UploaderType,
},
"dataMovement": dmInfo,
}
} else {
csiRestores[key] = map[string]any{
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
"github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
@@ -156,25 +157,27 @@ func TestDescribeRestoreSpecInSF(t *testing.T) {
"excluded": emptyDisplay,
"clusterScoped": "auto",
},
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": emptyDisplay,
"existingVolumeDataPolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
},
},
},
{
name: "included namespaces wildcard treated as all",
spec: velerov1api.RestoreSpec{
BackupName: "backup-2",
IncludedNamespaces: []string{"*"},
ExcludedNamespaces: []string{"kube-system"},
IncludedResources: []string{"pods", "configmaps"},
ExcludedResources: []string{"secrets"},
ExistingResourcePolicy: velerov1api.ResourcePolicyTypeUpdate,
BackupName: "backup-2",
IncludedNamespaces: []string{"*"},
ExcludedNamespaces: []string{"kube-system"},
IncludedResources: []string{"pods", "configmaps"},
ExcludedResources: []string{"secrets"},
ExistingResourcePolicy: velerov1api.ResourcePolicyTypeUpdate,
ExistingVolumeDataPolicy: velerov1api.VolumeDataPolicyTypeFull,
},
expect: map[string]any{
"spec": map[string]any{
@@ -188,13 +191,14 @@ func TestDescribeRestoreSpecInSF(t *testing.T) {
"excluded": "secrets",
"clusterScoped": "auto",
},
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": string(velerov1api.ResourcePolicyTypeUpdate),
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": string(velerov1api.ResourcePolicyTypeUpdate),
"existingVolumeDataPolicy": string(velerov1api.VolumeDataPolicyTypeFull),
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
},
},
},
@@ -223,13 +227,14 @@ func TestDescribeRestoreSpecInSF(t *testing.T) {
"excluded": emptyDisplay,
"clusterScoped": "auto",
},
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
"namespaceMappings": emptyDisplay,
"labelSelector": emptyDisplay,
"orLabelSelectors": emptyDisplay,
"restorePVs": "auto",
"existingResourcePolicy": emptyDisplay,
"existingVolumeDataPolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "auto",
"resourceModifier": map[string]any{
"type": "ConfigMap",
"name": "my-modifier",
@@ -272,13 +277,14 @@ func TestDescribeRestoreSpecInSF(t *testing.T) {
"excluded": emptyDisplay,
"clusterScoped": "included",
},
"namespaceMappings": map[string]string{"ns-a": "ns-a-new"},
"labelSelector": "app=nginx",
"orLabelSelectors": "env=prod or env=stage",
"restorePVs": "true",
"existingResourcePolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "false",
"namespaceMappings": map[string]string{"ns-a": "ns-a-new"},
"labelSelector": "app=nginx",
"orLabelSelectors": "env=prod or env=stage",
"restorePVs": "true",
"existingResourcePolicy": emptyDisplay,
"existingVolumeDataPolicy": emptyDisplay,
"itemOperationTimeout": "0s",
"preserveNodePorts": "false",
"resourcePolicy": map[string]any{
"type": "configmap",
"name": "volume-policy",
@@ -467,10 +473,13 @@ func TestDescribeRestoreCSISnapshotsInSF_NoData(t *testing.T) {
SnapshotDataMoved: true,
PVCName: "pvc-3",
PVCNamespace: "ns-3",
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
SnapshotDataMovementInfo: &volume.RestoreSnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
Size: 1234,
IncrementalSize: ptr.To(int64(500)),
RestoreType: "Incremental",
},
},
},
@@ -479,9 +488,12 @@ func TestDescribeRestoreCSISnapshotsInSF_NoData(t *testing.T) {
"csiSnapshotRestores": map[string]any{
"ns-3/pvc-3": map[string]any{
"dataMovement": map[string]any{
"operationID": "op-3",
"dataMover": "velero",
"uploaderType": "kopia",
"operationID": "op-3",
"dataMover": "velero",
"uploaderType": "kopia",
"size": int64(1234),
"incrementalSize": int64(500),
"restoreType": "Incremental",
},
},
},
@@ -495,7 +507,7 @@ func TestDescribeRestoreCSISnapshotsInSF_NoData(t *testing.T) {
SnapshotDataMoved: true,
PVCName: "pvc-3",
PVCNamespace: "ns-3",
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.RestoreSnapshotDataMovementInfo{
OperationID: "op-3",
DataMover: "velero",
UploaderType: "kopia",
+5 -4
View File
@@ -395,10 +395,11 @@ func (b *backupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *velerov1api.Backup, logger logrus.FieldLogger) *pkgbackup.Request {
request := &pkgbackup.Request{
Backup: backup.DeepCopy(), // don't modify items in the cache
SkippedPVTracker: pkgbackup.NewSkipPVTracker(),
BackedUpItems: pkgbackup.NewBackedUpItemsMap(),
WorkerPool: pkgbackup.StartItemBlockWorkerPool(ctx, b.itemBlockWorkerCount, logger),
Backup: backup.DeepCopy(), // don't modify items in the cache
SkippedPVTracker: pkgbackup.NewSkipPVTracker(),
BackedUpItems: pkgbackup.NewBackedUpItemsMap(),
MustIncludeAdditionalItemPVCs: pkgbackup.NewBackedUpItemsMap(),
WorkerPool: pkgbackup.StartItemBlockWorkerPool(ctx, b.itemBlockWorkerCount, logger),
}
request.VolumesInformation.Init()
@@ -158,10 +158,11 @@ func (r *backupFinalizerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
backupRequest := &pkgbackup.Request{
Backup: backup,
StorageLocation: location,
SkippedPVTracker: pkgbackup.NewSkipPVTracker(),
BackedUpItems: pkgbackup.NewBackedUpItemsMap(),
Backup: backup,
StorageLocation: location,
SkippedPVTracker: pkgbackup.NewSkipPVTracker(),
BackedUpItems: pkgbackup.NewBackedUpItemsMap(),
MustIncludeAdditionalItemPVCs: pkgbackup.NewBackedUpItemsMap(),
}
var outBackupFile *os.File
if len(operations) > 0 {
+24 -2
View File
@@ -496,6 +496,7 @@ func (e *csiSnapshotExposer) CleanUp(ctx context.Context, ownerObject corev1api.
backupPodName := ownerObject.Name
backupPVCName := ownerObject.Name
backupVSName := ownerObject.Name
backupVSCName := ownerObject.Name
kube.DeletePodIfAny(ctx, e.kubeClient.CoreV1(), backupPodName, ownerObject.Namespace, e.log)
kube.DeletePVAndPVCIfAny(ctx, e.kubeClient.CoreV1(), backupPVCName, ownerObject.Namespace, cleanUpTimeout, e.log)
@@ -507,6 +508,13 @@ func (e *csiSnapshotExposer) CleanUp(ctx context.Context, ownerObject corev1api.
csi.DeleteVolumeSnapshotIfAny(ctx, e.csiSnapshotClient, backupVSName, ownerObject.Namespace, e.log)
csi.DeleteVolumeSnapshotIfAny(ctx, e.csiSnapshotClient, vsName, sourceNamespace, e.log)
// The backup VSC is created by Velero as an internal handle to the source
// snapshot. Deleting the backup VS above only cascades to it when its
// deletion policy is Delete, so remove it explicitly to avoid leaking the
// object under a Retain policy. Deleting a Retain VSC drops only the API
// object and leaves the underlying snapshot intact.
csi.DeleteVolumeSnapshotContentIfAny(ctx, e.csiSnapshotClient, backupVSCName, e.log)
}
func getVolumeModeByAccessMode(accessMode string, dataMover string) (corev1api.PersistentVolumeMode, error) {
@@ -571,7 +579,21 @@ func (e *csiSnapshotExposer) createBackupVSC(ctx context.Context, ownerObject co
Source: snapshotv1api.VolumeSnapshotContentSource{
SnapshotHandle: snapshotVSC.Status.SnapshotHandle,
},
DeletionPolicy: snapshotv1api.VolumeSnapshotContentDelete,
// The backup VSC is statically provisioned against the same
// snapshot handle as the source VSC, so both objects refer to one
// physical snapshot. Inherit the source's deletion policy instead
// of forcing Delete, otherwise a user who configured Retain on the
// VolumeSnapshotClass still loses the snapshot when the backup VSC
// is cleaned up.
//
// For Case 2 storages per the design (design/block-data-mover/block-data-mover.md,
// e.g. Ceph RBD), inheriting Retain is not just an option but a requirement for
// incrementals to work at all: rbd snap diff needs the base and target snapshots
// in the same clone chain, so Delete destroys the base as soon as this backup
// completes. The next incremental's delta query then fails and degrades to an
// allocated-blocks backup (see the CBT tier ladder) or, without that fix, a full
// whole-device transfer.
DeletionPolicy: snapshotVSC.Spec.DeletionPolicy,
Driver: snapshotVSC.Spec.Driver,
VolumeSnapshotClassName: snapshotVSC.Spec.VolumeSnapshotClassName,
},
@@ -736,7 +758,7 @@ func (e *csiSnapshotExposer) createBackupPod(
if csiSnapshotMetadataServiceConfigs != nil {
if csiSnapshotMetadataServiceConfigs.SAName != "" {
args = append(args, fmt.Sprintf("--csi-snapshot-metadata-service-sa=%s", csiSnapshotMetadataServiceConfigs.SAName))
args = append(args, fmt.Sprintf("--cbt-sa-name=%s", csiSnapshotMetadataServiceConfigs.SAName))
}
}
+89
View File
@@ -43,6 +43,7 @@ import (
clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
datamovercli "github.com/vmware-tanzu/velero/pkg/cmd/cli/datamover"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
velerotypes "github.com/vmware-tanzu/velero/pkg/types"
"github.com/vmware-tanzu/velero/pkg/util"
@@ -2537,3 +2538,91 @@ func TestCleanUp_SecretsAndConfigMaps(t *testing.T) {
_, err = fakeKubeClient.CoreV1().Secrets("velero").Get(t.Context(), "other-secret", metav1.GetOptions{})
assert.NoError(t, err, "unrelated secret should not be deleted")
}
// TestBackupPodCBTServiceSAFlagMatchesDatamoverBackupFlags pins the contract between the
// flag createBackupPod emits for the CSI snapshot metadata service's service account and
// the flag NewBackupCommand actually registers to consume it. These previously drifted
// (exposer emitted --csi-snapshot-metadata-service-sa, the datamover backup command only
// registered --cbt-sa-name), so cobra rejected the unknown flag and the data mover pod
// exited immediately whenever a dedicated CBT service account was configured. This test
// fails if either side changes the flag name without the other.
func TestBackupPodCBTServiceSAFlagMatchesDatamoverBackupFlags(t *testing.T) {
const saName = "cbt-service-account"
// The exact line in createBackupPod (pkg/exposer/csi_snapshot.go) that builds this arg:
// args = append(args, fmt.Sprintf("--cbt-sa-name=%s", csiSnapshotMetadataServiceConfigs.SAName))
arg := fmt.Sprintf("--cbt-sa-name=%s", saName)
cmd := datamovercli.NewBackupCommand(nil)
err := cmd.ParseFlags([]string{
"--volume-path=/dev/vol",
"--volume-mode=Filesystem",
"--data-upload=du-test",
"--resource-timeout=1m",
arg,
})
require.NoError(t, err, "datamover backup command must accept the flag the exposer emits")
got, err := cmd.Flags().GetString("cbt-sa-name")
require.NoError(t, err)
assert.Equal(t, saName, got)
}
func TestCreateBackupVSCDeletionPolicy(t *testing.T) {
tests := []struct {
name string
sourcePolicy snapshotv1api.DeletionPolicy
expectedPolicy snapshotv1api.DeletionPolicy
}{
{
name: "Delete policy is inherited",
sourcePolicy: snapshotv1api.VolumeSnapshotContentDelete,
expectedPolicy: snapshotv1api.VolumeSnapshotContentDelete,
},
{
// The backup VSC points at the same snapshot handle as the source
// VSC, so forcing Delete here would destroy a snapshot the user
// asked to keep.
name: "Retain policy is inherited",
sourcePolicy: snapshotv1api.VolumeSnapshotContentRetain,
expectedPolicy: snapshotv1api.VolumeSnapshotContentRetain,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
handle := "fake-snapshot-handle"
className := "fake-snapshot-class"
sourceVSC := &snapshotv1api.VolumeSnapshotContent{
ObjectMeta: metav1.ObjectMeta{Name: "source-vsc"},
Spec: snapshotv1api.VolumeSnapshotContentSpec{
DeletionPolicy: test.sourcePolicy,
Driver: "fake-driver",
VolumeSnapshotClassName: &className,
},
Status: &snapshotv1api.VolumeSnapshotContentStatus{
SnapshotHandle: &handle,
},
}
exposer := csiSnapshotExposer{
csiSnapshotClient: snapshotFake.NewSimpleClientset().SnapshotV1(),
log: velerotest.NewLogger(),
}
ownerObject := corev1api.ObjectReference{
Name: "fake-du",
Namespace: "velero",
}
vs := &snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{Name: "fake-du", Namespace: "velero"},
}
backupVSC, err := exposer.createBackupVSC(t.Context(), ownerObject, sourceVSC, vs)
require.NoError(t, err)
assert.Equal(t, test.expectedPolicy, backupVSC.Spec.DeletionPolicy)
assert.Equal(t, handle, *backupVSC.Spec.Source.SnapshotHandle)
})
}
}
@@ -93,6 +93,7 @@ func ShouldPerformSnapshotWithVolumeHelper(
crClient,
boolptr.IsSetToTrue(backup.Spec.DefaultVolumesToFsBackup),
true,
nil,
)
return volumeHelperImpl.ShouldPerformSnapshot(unstructured, groupResource)
@@ -111,6 +112,7 @@ func NewVolumeHelperWithNamespaces(
defaultVolumesToFSBackup bool,
backupExcludePVC bool,
namespaces []string,
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker,
) (vhutil.VolumeHelper, error) {
return volumehelper.NewVolumeHelperImplWithNamespaces(
volumePolicy,
@@ -120,6 +122,7 @@ func NewVolumeHelperWithNamespaces(
defaultVolumesToFSBackup,
backupExcludePVC,
namespaces,
pvcMustInclusionTracker,
)
}
@@ -131,11 +134,13 @@ func NewVolumeHelperWithCache(
client crclient.Client,
logger logrus.FieldLogger,
pvcPodCache *podvolumeutil.PVCPodCache,
pvcMustInclusionTracker vhutil.PVCMustInclusionTracker,
) (vhutil.VolumeHelper, error) {
return volumehelper.NewVolumeHelperImplWithCache(
backup,
client,
logger,
pvcPodCache,
pvcMustInclusionTracker,
)
}
@@ -300,6 +300,7 @@ func TestShouldPerformSnapshotWithNonNilVolumeHelper(t *testing.T) {
false, // defaultVolumesToFSBackup
true, // backupExcludePVC
[]string{"default"},
nil,
)
require.NoError(t, err)
require.NotNil(t, vh)
@@ -712,6 +712,7 @@ func (kr *kopiaRepository) GetSnapshot(ctx context.Context, id udmrepo.ID) (udmr
}
return udmrepo.Snapshot{
ID: udmrepo.ID(snap.ID),
Source: snap.Source.Path,
Description: snap.Description,
StartTime: snap.StartTime.ToTime(),
@@ -751,6 +752,7 @@ func (kr *kopiaRepository) ListSnapshot(ctx context.Context, source string) ([]u
snapshots := []udmrepo.Snapshot{}
for _, snap := range mani {
snapshots = append(snapshots, udmrepo.Snapshot{
ID: udmrepo.ID(snap.ID),
Source: snap.Source.Path,
Description: snap.Description,
StartTime: snap.StartTime.ToTime(),
@@ -1609,6 +1609,7 @@ func TestGetSnapshot(t *testing.T) {
snapshotID: udmrepo.ID("fake-id"),
setRepoMock: true,
expectedSnap: udmrepo.Snapshot{
ID: "fake-id",
Source: "fake-source",
Description: "fake-desc",
StartTime: mockMani.StartTime.ToTime(),
@@ -1805,6 +1806,7 @@ func TestListSnapshot(t *testing.T) {
setRepoMock: true,
expectedSnaps: []udmrepo.Snapshot{
{
ID: "fake-id",
Source: "fake-source",
Description: "fake-desc",
StartTime: mockMani.StartTime.ToTime(),
+1
View File
@@ -98,6 +98,7 @@ type Metadata struct {
}
type Snapshot struct {
ID ID
Source string
Description string
StartTime time.Time
+2 -2
View File
@@ -118,7 +118,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
"pv-1": {
BackupMethod: volume.PodVolumeBackup,
PVName: "pv-1",
PVBInfo: &volume.PodVolumeInfo{
PVBInfo: &volume.PodVolumeBackupInfo{
SnapshotHandle: "testSnapshotHandle",
Size: 100,
NodeName: "testNode",
@@ -173,7 +173,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
CSISnapshotInfo: &volume.CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
},
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: &volume.BackupSnapshotDataMovementInfo{
DataMover: "velero",
},
},
+7 -13
View File
@@ -151,11 +151,6 @@ func snapshotSource(
func getParentBackupInfo(ctx context.Context, rep udmrepo.BackupRepo, forceFull bool, parentSnapshot string, volumeID string, realSource string, snapshotTags map[string]string, log logrus.FieldLogger) parentBackupInfo {
var previous *udmrepo.Snapshot
// parentID names whichever snapshot ended up being the parent. On the discovery
// branch the parentSnapshot parameter is empty by definition, so logging it there
// produces messages that describe a decision without naming the object it was about.
parentID := parentSnapshot
if !forceFull {
if parentSnapshot != "" {
snap, err := rep.GetSnapshot(ctx, udmrepo.ID(parentSnapshot))
@@ -173,8 +168,7 @@ func getParentBackupInfo(ctx context.Context, rep udmrepo.BackupRepo, forceFull
log.WithError(err).Warn("Failed to search previous snapshot, fallback to full backup")
} else {
previous = &snap
parentID = string(snap.RootObject.ID)
log.Infof("Using previous snapshot %s", snap.RootObject.ID)
log.Infof("Using previous snapshot %s", snap.ID)
}
}
} else {
@@ -184,21 +178,21 @@ func getParentBackupInfo(ctx context.Context, rep udmrepo.BackupRepo, forceFull
parentInfo := parentBackupInfo{}
if previous != nil {
if previous.Tags == nil {
log.Warnf("No tag from parent snapshot %s, fallback to full backup", parentID)
log.Warnf("No tag from parent snapshot %s, fallback to full backup", previous.ID)
} else if previous.Tags[uploader.CBTChangeIDTag] == "" {
log.Warnf("No ChangeID tag from parent snapshot %s, fallback to full backup", parentID)
log.Warnf("No ChangeID tag from parent snapshot %s, fallback to full backup", previous.ID)
} else if previous.Tags[uploader.CBTVolumeIDTag] == "" {
log.Warnf("No VolumeID tag from parent snapshot %s, fallback to full backup", parentID)
log.Warnf("No VolumeID tag from parent snapshot %s, fallback to full backup", previous.ID)
} else if previous.Tags[uploader.CBTVolumeIDTag] != volumeID {
log.Warnf("VolumeID %s from parent snapshot %s is not expected as %s, fallback to full backup", previous.Tags[uploader.CBTVolumeIDTag], parentID, volumeID)
log.Warnf("VolumeID %s from parent snapshot %s is not expected as %s, fallback to full backup", previous.Tags[uploader.CBTVolumeIDTag], previous.ID, volumeID)
} else if obj, err := loadObjectFromSnapshot(ctx, rep, previous); err != nil {
log.WithError(err).Warnf("Failed to load object from parent snapshot %s, fallback to full backup", parentID)
log.WithError(err).Warnf("Failed to load object from parent snapshot %s, fallback to full backup", previous.ID)
} else {
parentInfo.parentObject = obj
parentInfo.changeID = previous.Tags[uploader.CBTChangeIDTag]
parentInfo.volumeID = previous.Tags[uploader.CBTVolumeIDTag]
log.Infof("Using parent snapshot %s, start time %v, end time %v, description %s", parentID, previous.StartTime, previous.EndTime, previous.Description)
log.Infof("Using parent snapshot %s, start time %v, end time %v, description %s", previous.ID, previous.StartTime, previous.EndTime, previous.Description)
}
}
+3 -1
View File
@@ -351,6 +351,7 @@ func TestSnapshotSource(t *testing.T) {
func TestGetParentBackupInfoLogsDiscoveredParentID(t *testing.T) {
const volumeID = "vol-123"
const realSource = "/test/source"
const parentSnapID = "snap-parent-42"
const rootObj = "root-obj-42"
snapshotTags := map[string]string{
@@ -364,6 +365,7 @@ func TestGetParentBackupInfoLogsDiscoveredParentID(t *testing.T) {
repo := udmrepomocks.NewBackupRepo(t)
repo.On("ListSnapshot", mock.Anything, realSource).
Return([]udmrepo.Snapshot{{
ID: parentSnapID,
RootObject: udmrepo.ObjectMetadata{ID: rootObj},
Tags: map[string]string{
uploader.CBTChangeIDTag: "cid-abc",
@@ -389,7 +391,7 @@ func TestGetParentBackupInfoLogsDiscoveredParentID(t *testing.T) {
for _, entry := range hook.AllEntries() {
if strings.HasPrefix(entry.Message, "Using parent snapshot ") {
found = true
assert.Contains(t, entry.Message, rootObj,
assert.Contains(t, entry.Message, parentSnapID,
"parent-selection message must name the discovered snapshot, got %q", entry.Message)
}
}
+5 -5
View File
@@ -155,17 +155,17 @@ func (blkup *blockUploader) Restore(snapshot udmrepo.Snapshot, dest destInfo, bi
meta, err := blkup.repoWriter.ReadMetadata(blkup.ctx, snapshot.RootObject.ID)
if err != nil {
return 0, 0, errors.Wrapf(err, "error reading snapshot metadata for %s", snapshot.Description)
return 0, 0, errors.Wrapf(err, "error reading snapshot metadata for %s", snapshot.ID)
}
if len(meta.SubObjects) != 1 {
return 0, 0, errors.Errorf("unexpected number of bdev object (%d) for snapshot %s", len(meta.SubObjects), snapshot.Description)
return 0, 0, errors.Errorf("unexpected number of bdev object (%d) for snapshot %s", len(meta.SubObjects), snapshot.ID)
}
sourceSize, err := getSourceSize(snapshot)
if err != nil {
sourceSize = meta.SubObjects[0].Size
blkup.log.Warnf("Failed to get source size from snapshot %s, use backup size %v", snapshot.Description, sourceSize)
blkup.log.Warnf("Failed to get source size from snapshot %s, use backup size %v", snapshot.ID, sourceSize)
}
if sourceSize > meta.SubObjects[0].Size {
@@ -655,11 +655,11 @@ func loadObjectFromSnapshot(ctx context.Context, rep udmrepo.BackupRepo, snapsho
meta, err := rep.ReadMetadata(ctx, snapshot.RootObject.ID)
if err != nil {
return "", errors.Wrapf(err, "error reading snapshot metadata for %s", snapshot.Description)
return "", errors.Wrap(err, "error reading snapshot metadata")
}
if len(meta.SubObjects) != 1 {
return "", errors.Errorf("unexpected number of bdev object (%d) for snapshot %s", len(meta.SubObjects), snapshot.Description)
return "", errors.Errorf("unexpected number of bdev object (%d)", len(meta.SubObjects))
}
return meta.SubObjects[0].ID, nil
+5
View File
@@ -64,6 +64,11 @@ func GetCBTInfo(ctx context.Context, kubeClient kubernetes.Interface, log logrus
if vsc.Status != nil && vsc.Status.SnapshotHandle != nil {
cbtInfo.ChangeID = *vsc.Status.SnapshotHandle
} else if vsc.Spec.Source.SnapshotHandle != nil {
// The backup VSC is statically provisioned from the source VSC's
// snapshot handle; its status is populated asynchronously and may
// not be set yet, but the handle is already in the spec.
cbtInfo.ChangeID = *vsc.Spec.Source.SnapshotHandle
}
if pv.Spec.CSI != nil && pv.Spec.CSI.VolumeHandle != "" {
@@ -22,6 +22,13 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// PVCMustInclusionTracker provides read-only checks for whether a PVC is included
// in the backup as BIA's additionalItems through annotation
// backup.velero.io/must-include-additional-items.
type PVCMustInclusionTracker interface {
IsPVCIncluded(namespace, pvcName string) bool
}
type VolumeHelper interface {
ShouldPerformSnapshot(obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error)
ShouldPerformFSBackup(volume corev1api.Volume, pod corev1api.Pod) (bool, error)