Merge branch 'main' into issue-fix-6695
commit
582be97a63
|
@ -0,0 +1 @@
|
||||||
|
Add VolumeInfo metadata structures.
|
|
@ -35,44 +35,50 @@ The `restoreItem` function can decode the _backup-name_-volumes-info.json file t
|
||||||
## Detailed Design
|
## Detailed Design
|
||||||
|
|
||||||
### The VolumeInfo structure
|
### The VolumeInfo structure
|
||||||
_backup-name_-volumes-info.json file is a structure that contains an array of structure `VolumeInfo` and a VolumeInfo version parameter. The version is used to support multiple VolumeInfo version, and make the backward-compatible support possible. The current version is `1`.
|
_backup-name_-volumes-info.json file is a structure that contains an array of structure `VolumeInfo`.
|
||||||
|
|
||||||
The 1 version of `VolumeInfo` definition is:
|
|
||||||
``` golang
|
``` golang
|
||||||
type VolumeInfoV1 struct {
|
type VolumeInfo struct {
|
||||||
PVCName string // The PVC's name. The format should be <namespace-name>/<PVC-name>
|
PVCName string // The PVC's name.
|
||||||
|
PVCNamespace string // The PVC's namespace.
|
||||||
PVName string // The PV name.
|
PVName string // The PV name.
|
||||||
BackupMethod string // The way the volume data is backed up. The valid value includes `VeleroNativeSnapshot`, `PodVolumeBackup`, `CSISnapshot` and `Skipped`.
|
BackupMethod string // The way the volume data is backed up. The valid value includes `VeleroNativeSnapshot`, `PodVolumeBackup` and `CSISnapshot`.
|
||||||
SnapshotDataMovement bool // Whether the volume's snapshot data is moved to specified storage.
|
SnapshotDataMoved bool // Whether the volume's snapshot data is moved to specified storage.
|
||||||
|
|
||||||
|
Skipped boolean // Whether the Volume is skipped in this backup.
|
||||||
SkippedReason string // The reason for the volume is skipped in the backup.
|
SkippedReason string // The reason for the volume is skipped in the backup.
|
||||||
StartTimestamp *metav1.Time // Snapshot starts timestamp.
|
StartTimestamp *metav1.Time // Snapshot starts timestamp.
|
||||||
|
|
||||||
|
OperationID string // The Async Operation's ID.
|
||||||
|
|
||||||
CSISnapshotInfo CSISnapshotInfo
|
CSISnapshotInfo CSISnapshotInfo
|
||||||
SnapshotDataMoveInfo SnapshotDataMoveInfo
|
SnapshotDataMovementInfo SnapshotDataMovementInfo
|
||||||
NativeSnapshotInfo VeleroNativeSnapshotInfo
|
NativeSnapshotInfo VeleroNativeSnapshotInfo
|
||||||
PVBInfo PodVolumeBackupInfo
|
PVBInfo PodVolumeBackupInfo
|
||||||
|
PVInfo PVInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSISnapshotInfo is used for displaying the CSI snapshot status
|
// CSISnapshotInfo is used for displaying the CSI snapshot status
|
||||||
type CSISnapshotInfo struct {
|
type CSISnapshotInfo struct {
|
||||||
SnapshotHandle string // The actual snapshot ID. It can be the cloud provider's snapshot ID or the file-system uploader's snapshot.
|
SnapshotHandle string // It's the storage provider's snapshot ID for CSI.
|
||||||
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
||||||
|
|
||||||
Driver string // The name of the CSI driver.
|
Driver string // The name of the CSI driver.
|
||||||
VSCName string // The name of the VolumeSnapshotContent.
|
VSCName string // The name of the VolumeSnapshotContent.
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotDataMoveInfo is used for displaying the snapshot data mover status.
|
// SnapshotDataMovementInfo is used for displaying the snapshot data mover status.
|
||||||
type SnapshotDataMoveInfo struct {
|
type SnapshotDataMovementInfo struct {
|
||||||
DataMover string // The data mover used by the backup. The valid values are `velero` and ``(equals to `velero`).
|
DataMover string // The data mover used by the backup. The valid values are `velero` and ``(equals to `velero`).
|
||||||
UploaderType string // The type of the uploader that uploads the snapshot data. The valid values are `kopia` and `restic`. It's useful for file-system backup and snapshot data mover.
|
UploaderType string // The type of the uploader that uploads the snapshot data. The valid values are `kopia` and `restic`. It's useful for file-system backup and snapshot data mover.
|
||||||
RetainedSnapshot string // The name or ID of the snapshot associated object(SAO).
|
RetainedSnapshot string // The name or ID of the snapshot associated object(SAO). SAO is used to support local snapshots for the snapshot data mover, e.g. it could be a VolumeSnapshot for CSI snapshot data moign/pv_backup_info.
|
||||||
|
SnapshotHandle string // It's the filesystem repository's snapshot ID.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VeleroNativeSnapshotInfo is used for displaying the Velero native snapshot status.
|
// VeleroNativeSnapshotInfo is used for displaying the Velero native snapshot status.
|
||||||
type VeleroNativeSnapshotInfo struct {
|
type VeleroNativeSnapshotInfo struct {
|
||||||
SnapshotHandle string // The actual snapshot ID. It can be the cloud provider's snapshot ID or the file-system uploader's snapshot.
|
SnapshotHandle string // It's the storage provider's snapshot ID for the Velero-native snapshot.
|
||||||
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
||||||
|
|
||||||
VolumeType string // The cloud provider snapshot volume type.
|
VolumeType string // The cloud provider snapshot volume type.
|
||||||
|
@ -82,33 +88,20 @@ type VeleroNativeSnapshotInfo struct {
|
||||||
|
|
||||||
// PodVolumeBackupInfo is used for displaying the PodVolumeBackup snapshot status.
|
// PodVolumeBackupInfo is used for displaying the PodVolumeBackup snapshot status.
|
||||||
type PodVolumeBackupInfo struct {
|
type PodVolumeBackupInfo struct {
|
||||||
SnapshotHandle string // The actual snapshot ID. It can be the cloud provider's snapshot ID or the file-system uploader's snapshot.
|
SnapshotHandle string // It's the file-system uploader's snapshot ID for PodVolumeBackup.
|
||||||
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
Size int64 // The snapshot corresponding volume size. Some of the volume backup methods cannot retrieve the data by current design, for example, the Velero native snapshot.
|
||||||
|
|
||||||
UploaderType string // The type of the uploader that uploads the data. The valid values are `kopia` and `restic`. It's useful for file-system backup and snapshot data mover.
|
UploaderType string // The type of the uploader that uploads the data. The valid values are `kopia` and `restic`. It's useful for file-system backup and snapshot data mover.
|
||||||
VolumeName string // The PVC's corresponding volume name used by Pod
|
VolumeName string // The PVC's corresponding volume name used by Pod: https://github.com/kubernetes/kubernetes/blob/e4b74dd12fa8cb63c174091d5536a10b8ec19d34/pkg/apis/core/types.go#L48
|
||||||
PodName string // The Pod name mounting this PVC. The format should be <namespace-name>/<pod-name>.
|
PodName string // The Pod name mounting this PVC. The format should be <namespace-name>/<pod-name>.
|
||||||
}
|
NodeName string // The PVB-taken k8s node's name.
|
||||||
```
|
|
||||||
|
|
||||||
To make Velero support multiple versions of VolumeInfo, Velero needs to create a structure to include the version and the versioned volume information together. The information is persisted in the metadata file during backup creation. When reading the VolumeInfo metadata file, Velero reads the version information first by un-marshalling the metadata file by structure `VolumeInfoVersion`, then decide to use which version of the VolumeInfos according to the version value.
|
|
||||||
|
|
||||||
If there is need to introduce a non compatible change to the VolumeInfo, a new version of `VolumeInfos` and `VolumeInfo` are needed. For example, version 2 is created, then `VolumeInfosV2` and `VolumeInfoV2` structures are needed.
|
|
||||||
|
|
||||||
Only when there a non-backward-compatible change introduced in the `VolumeInfo` structure, the `version`'s version will be incremented.
|
|
||||||
|
|
||||||
``` golang
|
|
||||||
type VolumeInfoVersion struct {
|
|
||||||
Version string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VolumeInfosV2 struct {
|
// PVInfo is used to store some PV information modified after creation.
|
||||||
Infos []VolumeInfoV2
|
// Those information are lost after PV recreation.
|
||||||
Version string // VolumeInfo structure's version information.
|
type PVInfo struct {
|
||||||
}
|
ReclaimPolicy string // ReclaimPolicy of PV. It could be different from the referenced StorageClass.
|
||||||
|
Labels map[string]string // The PV's labels should be kept after recreation.
|
||||||
type VolumeInfoV2 struct {
|
|
||||||
...
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -164,11 +157,12 @@ After introducing the VolumeInfo array, the following logic will be added.
|
||||||
...
|
...
|
||||||
case CSISnapshot:
|
case CSISnapshot:
|
||||||
...
|
...
|
||||||
case Skipped:
|
|
||||||
// Check whether the Velero server should restore the PV depending on the DeletionPolicy setting.
|
|
||||||
default:
|
default:
|
||||||
// Need to check whether the volume is backed up by the SnapshotDataMover.
|
// Need to check whether the volume is backed up by the SnapshotDataMover.
|
||||||
if volumeInfo.SnapshotDataMovement:
|
if volumeInfo.SnapshotDataMovement:
|
||||||
|
|
||||||
|
// Check whether the Velero server should restore the PV depending on the DeletionPolicy setting.
|
||||||
|
if volumeInfo.Skipped:
|
||||||
```
|
```
|
||||||
|
|
||||||
### How the VolumeInfo metadata file is deleted
|
### How the VolumeInfo metadata file is deleted
|
||||||
|
|
|
@ -796,6 +796,7 @@ func persistBackup(backup *pkgbackup.Request,
|
||||||
) []error {
|
) []error {
|
||||||
persistErrs := []error{}
|
persistErrs := []error{}
|
||||||
backupJSON := new(bytes.Buffer)
|
backupJSON := new(bytes.Buffer)
|
||||||
|
volumeInfos := make([]volume.VolumeInfo, 0)
|
||||||
|
|
||||||
if err := encode.To(backup.Backup, "json", backupJSON); err != nil {
|
if err := encode.To(backup.Backup, "json", backupJSON); err != nil {
|
||||||
persistErrs = append(persistErrs, errors.Wrap(err, "error encoding backup"))
|
persistErrs = append(persistErrs, errors.Wrap(err, "error encoding backup"))
|
||||||
|
@ -842,6 +843,11 @@ func persistBackup(backup *pkgbackup.Request,
|
||||||
persistErrs = append(persistErrs, errs...)
|
persistErrs = append(persistErrs, errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
volumeInfoJSON, errs := encode.ToJSONGzip(volumeInfos, "backup volumes information")
|
||||||
|
if errs != nil {
|
||||||
|
persistErrs = append(persistErrs, errs...)
|
||||||
|
}
|
||||||
|
|
||||||
if len(persistErrs) > 0 {
|
if len(persistErrs) > 0 {
|
||||||
// Don't upload the JSON files or backup tarball if encoding to json fails.
|
// Don't upload the JSON files or backup tarball if encoding to json fails.
|
||||||
backupJSON = nil
|
backupJSON = nil
|
||||||
|
@ -853,6 +859,7 @@ func persistBackup(backup *pkgbackup.Request,
|
||||||
csiSnapshotContentsJSON = nil
|
csiSnapshotContentsJSON = nil
|
||||||
csiSnapshotClassesJSON = nil
|
csiSnapshotClassesJSON = nil
|
||||||
backupResult = nil
|
backupResult = nil
|
||||||
|
volumeInfoJSON = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
backupInfo := persistence.BackupInfo{
|
backupInfo := persistence.BackupInfo{
|
||||||
|
@ -868,6 +875,7 @@ func persistBackup(backup *pkgbackup.Request,
|
||||||
CSIVolumeSnapshots: csiSnapshotJSON,
|
CSIVolumeSnapshots: csiSnapshotJSON,
|
||||||
CSIVolumeSnapshotContents: csiSnapshotContentsJSON,
|
CSIVolumeSnapshotContents: csiSnapshotContentsJSON,
|
||||||
CSIVolumeSnapshotClasses: csiSnapshotClassesJSON,
|
CSIVolumeSnapshotClasses: csiSnapshotClassesJSON,
|
||||||
|
BackupVolumeInfo: volumeInfoJSON,
|
||||||
}
|
}
|
||||||
if err := backupStore.PutBackup(backupInfo); err != nil {
|
if err := backupStore.PutBackup(backupInfo); err != nil {
|
||||||
persistErrs = append(persistErrs, err)
|
persistErrs = append(persistErrs, err)
|
||||||
|
|
|
@ -50,7 +50,8 @@ type BackupInfo struct {
|
||||||
BackupResourceList,
|
BackupResourceList,
|
||||||
CSIVolumeSnapshots,
|
CSIVolumeSnapshots,
|
||||||
CSIVolumeSnapshotContents,
|
CSIVolumeSnapshotContents,
|
||||||
CSIVolumeSnapshotClasses io.Reader
|
CSIVolumeSnapshotClasses,
|
||||||
|
BackupVolumeInfo io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackupStore defines operations for creating, retrieving, and deleting
|
// BackupStore defines operations for creating, retrieving, and deleting
|
||||||
|
@ -270,6 +271,7 @@ func (s *objectBackupStore) PutBackup(info BackupInfo) error {
|
||||||
s.layout.getCSIVolumeSnapshotContentsKey(info.Name): info.CSIVolumeSnapshotContents,
|
s.layout.getCSIVolumeSnapshotContentsKey(info.Name): info.CSIVolumeSnapshotContents,
|
||||||
s.layout.getCSIVolumeSnapshotClassesKey(info.Name): info.CSIVolumeSnapshotClasses,
|
s.layout.getCSIVolumeSnapshotClassesKey(info.Name): info.CSIVolumeSnapshotClasses,
|
||||||
s.layout.getBackupResultsKey(info.Name): info.BackupResults,
|
s.layout.getBackupResultsKey(info.Name): info.BackupResults,
|
||||||
|
s.layout.getBackupVolumeInfoKey(info.Name): info.BackupVolumeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, reader := range backupObjs {
|
for key, reader := range backupObjs {
|
||||||
|
@ -491,6 +493,25 @@ func (s *objectBackupStore) GetPodVolumeBackups(name string) ([]*velerov1api.Pod
|
||||||
return podVolumeBackups, nil
|
return podVolumeBackups, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *objectBackupStore) GetBackupVolumeInfos(name string) (*volume.VolumeInfos, error) {
|
||||||
|
var volumeInfos *volume.VolumeInfos
|
||||||
|
|
||||||
|
res, err := tryGet(s.objectStore, s.bucket, s.layout.getBackupVolumeInfoKey(name))
|
||||||
|
if err != nil {
|
||||||
|
return volumeInfos, err
|
||||||
|
}
|
||||||
|
if res == nil {
|
||||||
|
return volumeInfos, nil
|
||||||
|
}
|
||||||
|
defer res.Close()
|
||||||
|
|
||||||
|
if err := decode(res, &volumeInfos); err != nil {
|
||||||
|
return volumeInfos, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumeInfos, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *objectBackupStore) GetBackupContents(name string) (io.ReadCloser, error) {
|
func (s *objectBackupStore) GetBackupContents(name string) (io.ReadCloser, error) {
|
||||||
return s.objectStore.GetObject(s.bucket, s.layout.getBackupContentsKey(name))
|
return s.objectStore.GetObject(s.bucket, s.layout.getBackupContentsKey(name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,6 +227,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots io.Reader
|
snapshots io.Reader
|
||||||
backupItemOperations io.Reader
|
backupItemOperations io.Reader
|
||||||
resourceList io.Reader
|
resourceList io.Reader
|
||||||
|
backupVolumeInfo io.Reader
|
||||||
expectedErr string
|
expectedErr string
|
||||||
expectedKeys []string
|
expectedKeys []string
|
||||||
}{
|
}{
|
||||||
|
@ -239,6 +240,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
expectedKeys: []string{
|
expectedKeys: []string{
|
||||||
"backups/backup-1/velero-backup.json",
|
"backups/backup-1/velero-backup.json",
|
||||||
|
@ -248,6 +250,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
"backups/backup-1/backup-1-itemoperations.json.gz",
|
"backups/backup-1/backup-1-itemoperations.json.gz",
|
||||||
"backups/backup-1/backup-1-resource-list.json.gz",
|
"backups/backup-1/backup-1-resource-list.json.gz",
|
||||||
|
"backups/backup-1/backup-1-volumeinfos.json.gz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -260,6 +263,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
expectedKeys: []string{
|
expectedKeys: []string{
|
||||||
"prefix-1/backups/backup-1/velero-backup.json",
|
"prefix-1/backups/backup-1/velero-backup.json",
|
||||||
|
@ -269,6 +273,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
|
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
"prefix-1/backups/backup-1/backup-1-itemoperations.json.gz",
|
"prefix-1/backups/backup-1/backup-1-itemoperations.json.gz",
|
||||||
"prefix-1/backups/backup-1/backup-1-resource-list.json.gz",
|
"prefix-1/backups/backup-1/backup-1-resource-list.json.gz",
|
||||||
|
"prefix-1/backups/backup-1/backup-1-volumeinfos.json.gz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -280,6 +285,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
expectedErr: "error readers return errors",
|
expectedErr: "error readers return errors",
|
||||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||||
},
|
},
|
||||||
|
@ -291,6 +297,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
expectedErr: "error readers return errors",
|
expectedErr: "error readers return errors",
|
||||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||||
},
|
},
|
||||||
|
@ -303,6 +310,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
expectedKeys: []string{
|
expectedKeys: []string{
|
||||||
"backups/backup-1/velero-backup.json",
|
"backups/backup-1/velero-backup.json",
|
||||||
|
@ -311,23 +319,26 @@ func TestPutBackup(t *testing.T) {
|
||||||
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
"backups/backup-1/backup-1-itemoperations.json.gz",
|
"backups/backup-1/backup-1-itemoperations.json.gz",
|
||||||
"backups/backup-1/backup-1-resource-list.json.gz",
|
"backups/backup-1/backup-1-resource-list.json.gz",
|
||||||
|
"backups/backup-1/backup-1-volumeinfos.json.gz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "data should be uploaded even when metadata is nil",
|
name: "data should be uploaded even when metadata is nil",
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
contents: newStringReadSeeker("contents"),
|
contents: newStringReadSeeker("contents"),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||||
snapshots: newStringReadSeeker("snapshots"),
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
resourceList: newStringReadSeeker("resourceList"),
|
resourceList: newStringReadSeeker("resourceList"),
|
||||||
expectedErr: "",
|
backupVolumeInfo: newStringReadSeeker("backupVolumeInfo"),
|
||||||
|
expectedErr: "",
|
||||||
expectedKeys: []string{
|
expectedKeys: []string{
|
||||||
"backups/backup-1/backup-1.tar.gz",
|
"backups/backup-1/backup-1.tar.gz",
|
||||||
"backups/backup-1/backup-1-logs.gz",
|
"backups/backup-1/backup-1-logs.gz",
|
||||||
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
||||||
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
"backups/backup-1/backup-1-resource-list.json.gz",
|
"backups/backup-1/backup-1-resource-list.json.gz",
|
||||||
|
"backups/backup-1/backup-1-volumeinfos.json.gz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -345,6 +356,7 @@ func TestPutBackup(t *testing.T) {
|
||||||
VolumeSnapshots: tc.snapshots,
|
VolumeSnapshots: tc.snapshots,
|
||||||
BackupItemOperations: tc.backupItemOperations,
|
BackupItemOperations: tc.backupItemOperations,
|
||||||
BackupResourceList: tc.resourceList,
|
BackupResourceList: tc.resourceList,
|
||||||
|
BackupVolumeInfo: tc.backupVolumeInfo,
|
||||||
}
|
}
|
||||||
err := harness.PutBackup(backupInfo)
|
err := harness.PutBackup(backupInfo)
|
||||||
|
|
||||||
|
@ -1045,6 +1057,90 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetBackupVolumeInfos(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
volumeInfo *volume.VolumeInfos
|
||||||
|
volumeInfoStr string
|
||||||
|
expectedErr string
|
||||||
|
expectedResult []volume.VolumeInfo
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "No VolumeInfos, expect no error.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid VolumeInfo, should pass.",
|
||||||
|
volumeInfo: &volume.VolumeInfos{
|
||||||
|
VolumeInfos: []volume.VolumeInfo{
|
||||||
|
{
|
||||||
|
PVCName: "pvcName",
|
||||||
|
PVName: "pvName",
|
||||||
|
Skipped: true,
|
||||||
|
SnapshotDataMoved: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResult: []volume.VolumeInfo{
|
||||||
|
{
|
||||||
|
PVCName: "pvcName",
|
||||||
|
PVName: "pvName",
|
||||||
|
Skipped: true,
|
||||||
|
SnapshotDataMoved: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid VolumeInfo string, should also pass.",
|
||||||
|
volumeInfoStr: `{"volumeInfos": [{"abc": "123", "def": "456", "pvcName": "pvcName"}]}`,
|
||||||
|
expectedResult: []volume.VolumeInfo{
|
||||||
|
{
|
||||||
|
PVCName: "pvcName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
harness := newObjectBackupStoreTestHarness("test-bucket", "")
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if tc.volumeInfo != nil {
|
||||||
|
obj := new(bytes.Buffer)
|
||||||
|
gzw := gzip.NewWriter(obj)
|
||||||
|
|
||||||
|
require.NoError(t, json.NewEncoder(gzw).Encode(tc.volumeInfo))
|
||||||
|
require.NoError(t, gzw.Close())
|
||||||
|
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-volumeinfos.json.gz", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.volumeInfoStr != "" {
|
||||||
|
obj := new(bytes.Buffer)
|
||||||
|
gzw := gzip.NewWriter(obj)
|
||||||
|
_, err := gzw.Write([]byte(tc.volumeInfoStr))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.NoError(t, gzw.Close())
|
||||||
|
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-volumeinfos.json.gz", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := harness.GetBackupVolumeInfos("test-backup")
|
||||||
|
if tc.expectedErr != "" {
|
||||||
|
require.Equal(t, tc.expectedErr, err.Error())
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tc.expectedResult) > 0 {
|
||||||
|
require.Equal(t, tc.expectedResult, result.VolumeInfos)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func encodeToBytes(obj runtime.Object) []byte {
|
func encodeToBytes(obj runtime.Object) []byte {
|
||||||
res, err := encode.Encode(obj, "json")
|
res, err := encode.Encode(obj, "json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue