Skip to content

Commit

Permalink
Add VolumeInfo metadata structures.
Browse files Browse the repository at this point in the history
Modify design according to comments.
Add PVInfo structure.

Signed-off-by: Xun Jiang <jxun@vmware.com>
  • Loading branch information
Xun Jiang committed Nov 7, 2023
1 parent 6b7ce66 commit d1bdbac
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 8 deletions.
27 changes: 19 additions & 8 deletions design/pv_backup_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,23 @@ The 1 version of `VolumeInfo` definition is:
type VolumeInfoV1 struct {
PVCName string // The PVC's name. The format should be <namespace-name>/<PVC-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.

Skipped boolean // Whether the Volume is skipped in this backup.
SkippedReason string // The reason for the volume is skipped in the backup.
StartTimestamp *metav1.Time // Snapshot starts timestamp.

CSISnapshotInfo CSISnapshotInfo
SnapshotDataMoveInfo SnapshotDataMoveInfo
NativeSnapshotInfo VeleroNativeSnapshotInfo
PVBInfo PodVolumeBackupInfo
PVInfo PVInfo
}

// CSISnapshotInfo is used for displaying the CSI snapshot status
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 // The actual snapshot ID. 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.

Driver string // The name of the CSI driver.
Expand All @@ -72,7 +74,7 @@ type SnapshotDataMoveInfo struct {

// VeleroNativeSnapshotInfo is used for displaying the Velero native snapshot status.
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 // The actual snapshot ID. 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.

VolumeType string // The cloud provider snapshot volume type.
Expand All @@ -82,12 +84,20 @@ type VeleroNativeSnapshotInfo struct {

// PodVolumeBackupInfo is used for displaying the PodVolumeBackup snapshot status.
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 // The actual snapshot ID. 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.

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
PodName string // The Pod name mounting this PVC. The format should be <namespace-name>/<pod-name>.
VolumeName string // The PVC's corresponding volume name used by Pod
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.
}

// PVInfo is used to store some PV information modified after creation.
// Those information are lost after PV recreation.
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.
}
```

Expand Down Expand Up @@ -164,11 +174,12 @@ After introducing the VolumeInfo array, the following logic will be added.
...
case CSISnapshot:
...
case Skipped:
// Check whether the Velero server should restore the PV depending on the DeletionPolicy setting.
default:
// Need to check whether the volume is backed up by the SnapshotDataMover.
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
Expand Down
38 changes: 38 additions & 0 deletions pkg/persistence/object_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package persistence
import (
"compress/gzip"
"encoding/json"
"fmt"
"io"
"strings"
"time"
Expand Down Expand Up @@ -491,6 +492,43 @@ func (s *objectBackupStore) GetPodVolumeBackups(name string) ([]*velerov1api.Pod
return podVolumeBackups, nil
}

func (s *objectBackupStore) GetBackupVolumeInfos(name string) (*volume.VolumeInfos, error) {
var volumeInfos *volume.VolumeInfos
var volumeInfoVersion volume.VolumeInfoVersion

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, &volumeInfoVersion); err != nil {
return volumeInfos, err
}

switch volumeInfoVersion.Version {
case volume.VolumeInfoVersionV1:
volumeInfos = new(volume.VolumeInfos)
volumeInfos.Version = volume.VolumeInfoVersionV1
volumeInfosV1 := new(volume.VolumeInfosV1)
if err := decode(res, volumeInfosV1); err != nil {
fmt.Printf("debug res: %+v\n", res)
fmt.Printf("debug: volumeInfosV1: %+v, err: %s \n", volumeInfosV1, err.Error())
return volumeInfos, err
}

volumeInfos.VolumeInfosV1 = volumeInfosV1.Infos

default:
return volumeInfos, fmt.Errorf("unknown backup VolumeInfo version: %s", volumeInfoVersion.Version)
}

return volumeInfos, nil
}

func (s *objectBackupStore) GetBackupContents(name string) (io.ReadCloser, error) {
return s.objectStore.GetObject(s.bucket, s.layout.getBackupContentsKey(name))
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/persistence/object_store_layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,7 @@ func (l *ObjectStoreLayout) getCSIVolumeSnapshotClassesKey(backup string) string
func (l *ObjectStoreLayout) getBackupResultsKey(backup string) string {
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-results.gz", backup))
}

func (l *ObjectStoreLayout) getBackupVolumeInfoKey(backup string) string {
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-volumeinfos.json.gz", backup))
}
88 changes: 88 additions & 0 deletions pkg/persistence/object_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,94 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
}
}

func TestGetBackupVolumeInfos(t *testing.T) {
tests := []struct {
name string
volumeInfoString string
volumeInfo *volume.VolumeInfosV1
expectedErr string
expectedResult []volume.VolumeInfoV1
}{
{
name: "No VolumeInfos, expect no error.",
},
{
name: "Invalid VolumeInfo, should fail.",
volumeInfoString: "foo",
expectedErr: "unexpected EOF",
},
{
name: "Invalid VolumeInfo version, should fail.",
volumeInfo: &volume.VolumeInfosV1{
Version: "invalid",
},
expectedErr: "unknown backup VolumeInfo version: invalid",
},
{
name: "Valid VolumeInfo version, should pass.",
volumeInfo: &volume.VolumeInfosV1{
Version: "1",
Infos: []*volume.VolumeInfoV1{
{
PVCName: "pvcName",
PVName: "pvName",
Skipped: true,
SnapshotDataMovement: false,
},
},
},
expectedResult: []volume.VolumeInfoV1{
{
PVCName: "pvcName",
PVName: "pvName",
Skipped: true,
SnapshotDataMovement: false,
},
},
},
}

harness := newObjectBackupStoreTestHarness("test-bucket", "")

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if tc.volumeInfoString != "" {
obj := new(bytes.Buffer)
gzw := gzip.NewWriter(obj)

require.NoError(t, json.NewEncoder(gzw).Encode(tc.volumeInfoString))
defer require.NoError(t, gzw.Close())

harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-volumeinfos.json.gz", newStringReadSeeker(tc.volumeInfoString))
}

if tc.volumeInfo != nil {
obj := new(bytes.Buffer)
gzw := gzip.NewWriter(obj)

require.NoError(t, json.NewEncoder(gzw).Encode(tc.volumeInfo))
defer 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.VolumeInfosV1)
}

})
}
}

func encodeToBytes(obj runtime.Object) []byte {
res, err := encode.Encode(obj, "json")
if err != nil {
Expand Down
80 changes: 80 additions & 0 deletions pkg/volume/volume_info_common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package volume

type VolumeInfoVersion struct {
Version string `json:"version"`
}

const (
VolumeInfoVersionV1 string = "1"
)

type VolumeInfos struct {
VolumeInfosV1 []*VolumeInfoV1
Version string
}

// CSISnapshotInfo is used for displaying the CSI snapshot status
type CSISnapshotInfo struct {
// The actual snapshot ID. It's the storage provider's snapshot ID for CSI.
SnapshotHandle string `json:"snapshotHandle"`

// 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 `json:"size"`

// The name of the CSI driver.
Driver string `json:"driver"`

// The name of the VolumeSnapshotContent.
VSCName string `json:"vscName"`
}

// SnapshotDataMoveInfo is used for displaying the snapshot data mover status.
type SnapshotDataMoveInfo struct {
// The data mover used by the backup. The valid values are `velero` and ``(equals to `velero`).
DataMover string `json:"dataMover"`

// 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 `json:"uploaderType"`

// The name or ID of the snapshot associated object(SAO).
RetainedSnapshot string `json:"retainedSnapshot"`
}

// VeleroNativeSnapshotInfo is used for displaying the Velero native snapshot status.
type VeleroNativeSnapshotInfo struct {
// The actual snapshot ID. It's the storage provider's snapshot ID for the Velero-native snapshot.
SnapshotHandle string `json:"snapshotHandle"`

// 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 `json:"size"`

// The cloud provider snapshot volume type.
VolumeType string `json:"volumeType"`

// The cloud provider snapshot volume's availability zones.
VolumeAZ string `json:"volumeAZ"`

// The cloud provider snapshot volume's IOPS.
IOPS string `json:"iops"`
}

// PodVolumeBackupInfo is used for displaying the PodVolumeBackup snapshot status.
type PodVolumeBackupInfo struct {
// The actual snapshot ID. It's the file-system uploader's snapshot ID for PodVolumeBackup.
SnapshotHandle string `json:"snapshotHandle"`

// 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 `json:"size"`

// 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 `json:"uploaderType"`

// The PVC's corresponding volume name used by Pod
VolumeName string `json:"volumeName"`

// The Pod name mounting this PVC. The format should be <namespace-name>/<pod-name>.
PodName string `json:"podName"`

// The PVB-taken k8s node's name.
NodeName string `json:"nodeName"`
}
38 changes: 38 additions & 0 deletions pkg/volume/volume_info_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package volume

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

type VolumeInfosV1 struct {
Infos []*VolumeInfoV1 `json:"infos"`

// VolumeInfo structure's version information.
Version string `json:"version"`
}

type VolumeInfoV1 struct {
// The PVC's name. The format should be <namespace-name>/<PVC-name>
PVCName string `json:"pvcName,omitempty"`

// The PV name.
PVName string `json:"pvName,omitempty"`

// The way the volume data is backed up. The valid value includes `VeleroNativeSnapshot`, `PodVolumeBackup` and `CSISnapshot`.
BackupMethod string `json:"backupMethod,omitempty"`

// Whether the volume's snapshot data is moved to specified storage.
SnapshotDataMovement bool `json:"snapshotDataMovement,omitempty"`

// Whether the Volume is skipped in this backup.
Skipped bool `json:"skipped,omitempty"`

// The reason for the volume is skipped in the backup.
SkippedReason string `json:"skippedReason,omitempty"`

// Snapshot starts timestamp.
StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`

CSISnapshotInfo CSISnapshotInfo `json:"csiSnapshotInfo,omitempty"`
SnapshotDataMoveInfo SnapshotDataMoveInfo `json:"snapshotDataMoveInfo,omitempty"`
NativeSnapshotInfo VeleroNativeSnapshotInfo `json:"nativeSnapshotInfo,omitempty"`
PVBInfo PodVolumeBackupInfo `json:"pvbInfo,omitempty"`
}

0 comments on commit d1bdbac

Please sign in to comment.