Move VolumesInformation to an independant package.

Signed-off-by: Xun Jiang <jxun@vmware.com>
pull/7138/head
Xun Jiang 2023-11-29 23:37:38 +08:00
parent ca97248f2a
commit c77bec73bb
23 changed files with 1530 additions and 1457 deletions

View File

@ -0,0 +1,556 @@
/*
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 volume
import (
"context"
"strconv"
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/sirupsen/logrus"
corev1api "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
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/itemoperation"
"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
"github.com/vmware-tanzu/velero/pkg/volume"
)
type VolumeBackupMethod string
const (
NativeSnapshot VolumeBackupMethod = "NativeSnapshot"
PodVolumeBackup VolumeBackupMethod = "PodVolumeBackup"
CSISnapshot VolumeBackupMethod = "CSISnapshot"
)
type VolumeInfo struct {
// The PVC's name.
PVCName string `json:"pvcName,omitempty"`
// The PVC's namespace
PVCNamespace string `json:"pvcNamespace,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 VolumeBackupMethod `json:"backupMethod,omitempty"`
// Whether the volume's snapshot data is moved to specified storage.
SnapshotDataMoved bool `json:"snapshotDataMoved"`
// Whether the local snapshot is preserved after snapshot is moved.
// The local snapshot may be a result of CSI snapshot backup(no data movement)
// or a CSI snapshot data movement plus preserve local snapshot.
PreserveLocalSnapshot bool `json:"preserveLocalSnapshot"`
// Whether the Volume is skipped in this backup.
Skipped bool `json:"skipped"`
// The reason for the volume is skipped in the backup.
SkippedReason string `json:"skippedReason,omitempty"`
// Snapshot starts timestamp.
StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`
// The Async Operation's ID.
OperationID string `json:"operationID,omitempty"`
CSISnapshotInfo CSISnapshotInfo `json:"csiSnapshotInfo,omitempty"`
SnapshotDataMovementInfo SnapshotDataMovementInfo `json:"snapshotDataMovementInfo,omitempty"`
NativeSnapshotInfo NativeSnapshotInfo `json:"nativeSnapshotInfo,omitempty"`
PVBInfo PodVolumeBackupInfo `json:"pvbInfo,omitempty"`
PVInfo PVInfo `json:"pvInfo,omitempty"`
}
// CSISnapshotInfo is used for displaying the CSI snapshot status
type CSISnapshotInfo struct {
// It's the storage provider's snapshot ID for CSI.
SnapshotHandle string `json:"snapshotHandle"`
// The snapshot corresponding volume size.
Size int64 `json:"size"`
// The name of the CSI driver.
Driver string `json:"driver"`
// The name of the VolumeSnapshotContent.
VSCName string `json:"vscName"`
}
// SnapshotDataMovementInfo is used for displaying the snapshot data mover status.
type SnapshotDataMovementInfo 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`.
UploaderType string `json:"uploaderType"`
// 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 movement.
RetainedSnapshot string `json:"retainedSnapshot"`
// It's the filesystem repository's snapshot ID.
SnapshotHandle string `json:"snapshotHandle"`
}
// NativeSnapshotInfo is used for displaying the Velero native snapshot status.
// A Velero Native Snapshot is a cloud storage snapshot taken by the Velero native
// plugins, e.g. velero-plugin-for-aws, velero-plugin-for-gcp, and
// velero-plugin-for-microsoft-azure.
type NativeSnapshotInfo struct {
// It's the storage provider's snapshot ID for the Velero-native snapshot.
SnapshotHandle string `json:"snapshotHandle"`
// 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 {
// It's the file-system uploader's snapshot ID for PodVolumeBackup.
SnapshotHandle string `json:"snapshotHandle"`
// The snapshot corresponding volume size.
Size int64 `json:"size"`
// The type of the uploader that uploads the data. The valid values are `kopia` and `restic`.
UploaderType string `json:"uploaderType"`
// The PVC's corresponding volume name used by Pod
// https://github.com/kubernetes/kubernetes/blob/e4b74dd12fa8cb63c174091d5536a10b8ec19d34/pkg/apis/core/types.go#L48
VolumeName string `json:"volumeName"`
// The Pod name mounting this PVC.
PodName string `json:"podName"`
// The Pod namespace
PodNamespace string `json:"podNamespace"`
// The PVB-taken k8s node's name.
NodeName string `json:"nodeName"`
}
// PVInfo is used to store some PV information modified after creation.
// Those information are lost after PV recreation.
type PVInfo struct {
// ReclaimPolicy of PV. It could be different from the referenced StorageClass.
ReclaimPolicy string `json:"reclaimPolicy"`
// The PV's labels should be kept after recreation.
Labels map[string]string `json:"labels"`
}
// VolumesInformation contains the information needs by generating
// the backup VolumeInfo array.
type VolumesInformation struct {
// A map contains the backup-included PV detail content. The key is PV name.
pvMap map[string]pvcPvInfo
volumeInfos []*VolumeInfo
logger logrus.FieldLogger
crClient kbclient.Client
volumeSnapshots []snapshotv1api.VolumeSnapshot
volumeSnapshotContents []snapshotv1api.VolumeSnapshotContent
volumeSnapshotClasses []snapshotv1api.VolumeSnapshotClass
SkippedPVs map[string]string
NativeSnapshots []*volume.Snapshot
PodVolumeBackups []*velerov1api.PodVolumeBackup
BackupOperations []*itemoperation.BackupOperation
BackupName string
}
type pvcPvInfo struct {
PVCName string
PVCNamespace string
PV corev1api.PersistentVolume
}
func (v *VolumesInformation) Init() {
v.pvMap = make(map[string]pvcPvInfo)
v.volumeInfos = make([]*VolumeInfo, 0)
}
func (v *VolumesInformation) InsertPVMap(pv corev1api.PersistentVolume, pvcName, pvcNamespace string) {
if v.pvMap == nil {
v.Init()
}
v.pvMap[pv.Name] = pvcPvInfo{
PVCName: pvcName,
PVCNamespace: pvcNamespace,
PV: pv,
}
}
func (v *VolumesInformation) Result(
csiVolumeSnapshots []snapshotv1api.VolumeSnapshot,
csiVolumeSnapshotContents []snapshotv1api.VolumeSnapshotContent,
csiVolumesnapshotClasses []snapshotv1api.VolumeSnapshotClass,
crClient kbclient.Client,
logger logrus.FieldLogger,
) []*VolumeInfo {
v.logger = logger
v.crClient = crClient
v.volumeSnapshots = csiVolumeSnapshots
v.volumeSnapshotContents = csiVolumeSnapshotContents
v.volumeSnapshotClasses = csiVolumesnapshotClasses
v.generateVolumeInfoForSkippedPV()
v.generateVolumeInfoForVeleroNativeSnapshot()
v.generateVolumeInfoForCSIVolumeSnapshot()
v.generateVolumeInfoFromPVB()
v.generateVolumeInfoFromDataUpload()
return v.volumeInfos
}
// generateVolumeInfoForSkippedPV generate VolumeInfos for SkippedPV.
func (v *VolumesInformation) generateVolumeInfoForSkippedPV() {
tmpVolumeInfos := make([]*VolumeInfo, 0)
for pvName, skippedReason := range v.SkippedPVs {
if pvcPVInfo := v.retrievePvcPvInfo(pvName, "", ""); pvcPVInfo != nil {
volumeInfo := &VolumeInfo{
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvName,
SnapshotDataMoved: false,
Skipped: true,
SkippedReason: skippedReason,
PVInfo: PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
v.logger.Warnf("Cannot find info for PV %s", pvName)
continue
}
}
v.volumeInfos = append(v.volumeInfos, tmpVolumeInfos...)
}
// generateVolumeInfoForVeleroNativeSnapshot generate VolumeInfos for Velero native snapshot
func (v *VolumesInformation) generateVolumeInfoForVeleroNativeSnapshot() {
tmpVolumeInfos := make([]*VolumeInfo, 0)
for _, nativeSnapshot := range v.NativeSnapshots {
var iops int64
if nativeSnapshot.Spec.VolumeIOPS != nil {
iops = *nativeSnapshot.Spec.VolumeIOPS
}
if pvcPVInfo := v.retrievePvcPvInfo(nativeSnapshot.Spec.PersistentVolumeName, "", ""); pvcPVInfo != nil {
volumeInfo := &VolumeInfo{
BackupMethod: NativeSnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
SnapshotDataMoved: false,
Skipped: false,
NativeSnapshotInfo: NativeSnapshotInfo{
SnapshotHandle: nativeSnapshot.Status.ProviderSnapshotID,
VolumeType: nativeSnapshot.Spec.VolumeType,
VolumeAZ: nativeSnapshot.Spec.VolumeAZ,
IOPS: strconv.FormatInt(iops, 10),
},
PVInfo: PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
v.logger.Warnf("cannot find info for PV %s", nativeSnapshot.Spec.PersistentVolumeName)
continue
}
}
v.volumeInfos = append(v.volumeInfos, tmpVolumeInfos...)
}
// generateVolumeInfoForCSIVolumeSnapshot generate VolumeInfos for CSI VolumeSnapshot
func (v *VolumesInformation) generateVolumeInfoForCSIVolumeSnapshot() {
tmpVolumeInfos := make([]*VolumeInfo, 0)
for _, volumeSnapshot := range v.volumeSnapshots {
var volumeSnapshotClass *snapshotv1api.VolumeSnapshotClass
var volumeSnapshotContent *snapshotv1api.VolumeSnapshotContent
// This is protective logic. The passed-in VS should be all related
// to this backup.
if volumeSnapshot.Labels[velerov1api.BackupNameLabel] != v.BackupName {
continue
}
if volumeSnapshot.Spec.VolumeSnapshotClassName == nil {
v.logger.Warnf("Cannot find VolumeSnapshotClass for VolumeSnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
if volumeSnapshot.Status == nil || volumeSnapshot.Status.BoundVolumeSnapshotContentName == nil {
v.logger.Warnf("Cannot fine VolumeSnapshotContent for VolumeSnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
if volumeSnapshot.Spec.Source.PersistentVolumeClaimName == nil {
v.logger.Warnf("VolumeSnapshot %s/%s doesn't have a source PVC", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
for index := range v.volumeSnapshotClasses {
if *volumeSnapshot.Spec.VolumeSnapshotClassName == v.volumeSnapshotClasses[index].Name {
volumeSnapshotClass = &v.volumeSnapshotClasses[index]
}
}
for index := range v.volumeSnapshotContents {
if *volumeSnapshot.Status.BoundVolumeSnapshotContentName == v.volumeSnapshotContents[index].Name {
volumeSnapshotContent = &v.volumeSnapshotContents[index]
}
}
if volumeSnapshotClass == nil || volumeSnapshotContent == nil {
v.logger.Warnf("fail to get VolumeSnapshotContent or VolumeSnapshotClass for VolumeSnapshot: %s/%s",
volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
var operation itemoperation.BackupOperation
for _, op := range v.BackupOperations {
if op.Spec.ResourceIdentifier.GroupResource.String() == kuberesource.VolumeSnapshots.String() &&
op.Spec.ResourceIdentifier.Name == volumeSnapshot.Name &&
op.Spec.ResourceIdentifier.Namespace == volumeSnapshot.Namespace {
operation = *op
}
}
var size int64
if volumeSnapshot.Status.RestoreSize != nil {
size = volumeSnapshot.Status.RestoreSize.Value()
}
snapshotHandle := ""
if volumeSnapshotContent.Status.SnapshotHandle != nil {
snapshotHandle = *volumeSnapshotContent.Status.SnapshotHandle
}
if pvcPVInfo := v.retrievePvcPvInfo("", *volumeSnapshot.Spec.Source.PersistentVolumeClaimName, volumeSnapshot.Namespace); pvcPVInfo != nil {
volumeInfo := &VolumeInfo{
BackupMethod: CSISnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
Skipped: false,
SnapshotDataMoved: false,
PreserveLocalSnapshot: true,
OperationID: operation.Spec.OperationID,
StartTimestamp: &(volumeSnapshot.CreationTimestamp),
CSISnapshotInfo: CSISnapshotInfo{
VSCName: *volumeSnapshot.Status.BoundVolumeSnapshotContentName,
Size: size,
Driver: volumeSnapshotClass.Driver,
SnapshotHandle: snapshotHandle,
},
PVInfo: PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
v.logger.Warnf("cannot find info for PVC %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Spec.Source.PersistentVolumeClaimName)
continue
}
}
v.volumeInfos = append(v.volumeInfos, tmpVolumeInfos...)
}
// generateVolumeInfoFromPVB generate VolumeInfo for PVB.
func (v *VolumesInformation) generateVolumeInfoFromPVB() {
tmpVolumeInfos := make([]*VolumeInfo, 0)
for _, pvb := range v.PodVolumeBackups {
volumeInfo := &VolumeInfo{
BackupMethod: PodVolumeBackup,
SnapshotDataMoved: false,
Skipped: false,
StartTimestamp: pvb.Status.StartTimestamp,
PVBInfo: PodVolumeBackupInfo{
SnapshotHandle: pvb.Status.SnapshotID,
Size: pvb.Status.Progress.TotalBytes,
UploaderType: pvb.Spec.UploaderType,
VolumeName: pvb.Spec.Volume,
PodName: pvb.Spec.Pod.Name,
PodNamespace: pvb.Spec.Pod.Namespace,
NodeName: pvb.Spec.Node,
},
}
pod := new(corev1api.Pod)
pvcName := ""
err := v.crClient.Get(context.TODO(), kbclient.ObjectKey{Namespace: pvb.Spec.Pod.Namespace, Name: pvb.Spec.Pod.Name}, pod)
if err != nil {
v.logger.WithError(err).Warn("Fail to get pod for PodVolumeBackup: ", pvb.Name)
continue
}
for _, volume := range pod.Spec.Volumes {
if volume.Name == pvb.Spec.Volume && volume.PersistentVolumeClaim != nil {
pvcName = volume.PersistentVolumeClaim.ClaimName
}
}
if pvcName != "" {
if pvcPVInfo := v.retrievePvcPvInfo("", pvcName, pod.Namespace); pvcPVInfo != nil {
volumeInfo.PVCName = pvcPVInfo.PVCName
volumeInfo.PVCNamespace = pvcPVInfo.PVCNamespace
volumeInfo.PVName = pvcPVInfo.PV.Name
volumeInfo.PVInfo = PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
}
} else {
v.logger.Warnf("Cannot find info for PVC %s/%s", pod.Namespace, pvcName)
continue
}
} else {
v.logger.Debug("The PVB %s doesn't have a corresponding PVC", pvb.Name)
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
}
v.volumeInfos = append(v.volumeInfos, tmpVolumeInfos...)
}
// generateVolumeInfoFromDataUpload generate VolumeInfo for DataUpload.
func (v *VolumesInformation) generateVolumeInfoFromDataUpload() {
tmpVolumeInfos := make([]*VolumeInfo, 0)
vsClassList := new(snapshotv1api.VolumeSnapshotClassList)
if err := v.crClient.List(context.TODO(), vsClassList); err != nil {
v.logger.WithError(err).Errorf("cannot list VolumeSnapshotClass %s", err.Error())
return
}
for _, operation := range v.BackupOperations {
if operation.Spec.ResourceIdentifier.GroupResource.String() == kuberesource.PersistentVolumeClaims.String() {
var duIdentifier velero.ResourceIdentifier
for _, identifier := range operation.Spec.PostOperationItems {
if identifier.GroupResource.String() == "datauploads.velero.io" {
duIdentifier = identifier
}
}
if duIdentifier.Empty() {
v.logger.Warnf("cannot find DataUpload for PVC %s/%s backup async operation",
operation.Spec.ResourceIdentifier.Namespace, operation.Spec.ResourceIdentifier.Name)
continue
}
dataUpload := new(velerov2alpha1.DataUpload)
err := v.crClient.Get(
context.TODO(),
kbclient.ObjectKey{
Namespace: duIdentifier.Namespace,
Name: duIdentifier.Name},
dataUpload,
)
if err != nil {
v.logger.Warnf("fail to get DataUpload for operation %s: %s", operation.Spec.OperationID, err.Error())
continue
}
driverUsedByVSClass := ""
for index := range vsClassList.Items {
if vsClassList.Items[index].Name == dataUpload.Spec.CSISnapshot.SnapshotClass {
driverUsedByVSClass = vsClassList.Items[index].Driver
}
}
if pvcPVInfo := v.retrievePvcPvInfo("", operation.Spec.ResourceIdentifier.Name, operation.Spec.ResourceIdentifier.Namespace); pvcPVInfo != nil {
volumeInfo := &VolumeInfo{
BackupMethod: CSISnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
SnapshotDataMoved: true,
Skipped: false,
OperationID: operation.Spec.OperationID,
StartTimestamp: operation.Status.Created,
CSISnapshotInfo: CSISnapshotInfo{
Driver: driverUsedByVSClass,
},
SnapshotDataMovementInfo: SnapshotDataMovementInfo{
DataMover: dataUpload.Spec.DataMover,
UploaderType: "kopia",
},
PVInfo: PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
v.logger.Warnf("Cannot find info for PVC %s/%s", operation.Spec.ResourceIdentifier.Namespace, operation.Spec.ResourceIdentifier.Name)
continue
}
}
}
v.volumeInfos = append(v.volumeInfos, tmpVolumeInfos...)
}
// retrievePvcPvInfo gets the PvcPvInfo from the PVMap.
// support retrieve info by PV's name, or by PVC's name
// and namespace.
func (v *VolumesInformation) retrievePvcPvInfo(pvName, pvcName, pvcNS string) *pvcPvInfo {
if pvName != "" {
if info, ok := v.pvMap[pvName]; ok {
return &info
}
return nil
}
if pvcNS == "" || pvcName == "" {
return nil
}
for _, info := range v.pvMap {
if pvcNS == info.PVCNamespace && pvcName == info.PVCName {
return &info
}
}
return nil
}

View File

@ -0,0 +1,856 @@
/*
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 volume
import (
"context"
"testing"
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
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/itemoperation"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/util/logging"
"github.com/vmware-tanzu/velero/pkg/volume"
)
func TestGenerateVolumeInfoForSkippedPV(t *testing.T) {
tests := []struct {
name string
skippedPVName string
pvMap map[string]pvcPvInfo
expectedVolumeInfos []*VolumeInfo
}{
{
name: "Cannot find info for PV",
skippedPVName: "testPV",
pvMap: map[string]pvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Normal Skipped PV info",
skippedPVName: "testPV",
pvMap: map[string]pvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
Skipped: true,
SkippedReason: "CSI: skipped for PodVolumeBackup",
PVInfo: PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
volumesInfo := VolumesInformation{}
volumesInfo.Init()
if tc.skippedPVName != "" {
volumesInfo.SkippedPVs = map[string]string{
tc.skippedPVName: "CSI: skipped for PodVolumeBackup",
}
}
if tc.pvMap != nil {
for k, v := range tc.pvMap {
volumesInfo.pvMap[k] = v
}
}
volumesInfo.logger = logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumesInfo.generateVolumeInfoForSkippedPV()
require.Equal(t, tc.expectedVolumeInfos, volumesInfo.volumeInfos)
})
}
}
func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) {
tests := []struct {
name string
nativeSnapshot volume.Snapshot
pvMap map[string]pvcPvInfo
expectedVolumeInfos []*VolumeInfo
}{
{
name: "Native snapshot's IPOS pointer is nil",
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: nil,
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Cannot find info for the PV",
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Cannot find PV info in pvMap",
pvMap: map[string]pvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
},
Status: volume.SnapshotStatus{
ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Normal native snapshot",
pvMap: map[string]pvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
},
Status: volume.SnapshotStatus{
ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
},
},
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: NativeSnapshot,
PVInfo: PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
NativeSnapshotInfo: NativeSnapshotInfo{
SnapshotHandle: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
IOPS: "100",
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
volumesInfo := VolumesInformation{}
volumesInfo.Init()
volumesInfo.NativeSnapshots = append(volumesInfo.NativeSnapshots, &tc.nativeSnapshot)
if tc.pvMap != nil {
for k, v := range tc.pvMap {
volumesInfo.pvMap[k] = v
}
}
volumesInfo.logger = logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumesInfo.generateVolumeInfoForVeleroNativeSnapshot()
require.Equal(t, tc.expectedVolumeInfos, volumesInfo.volumeInfos)
})
}
}
func TestGenerateVolumeInfoForCSIVolumeSnapshot(t *testing.T) {
resourceQuantity := resource.MustParse("100Gi")
now := metav1.Now()
tests := []struct {
name string
volumeSnapshot snapshotv1api.VolumeSnapshot
volumeSnapshotContent snapshotv1api.VolumeSnapshotContent
volumeSnapshotClass snapshotv1api.VolumeSnapshotClass
pvMap map[string]pvcPvInfo
operation *itemoperation.BackupOperation
expectedVolumeInfos []*VolumeInfo
}{
{
name: "VS doesn't have VolumeSnapshotClass name",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "VS doesn't have status",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "VS doesn't have PVC",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Cannot find VSC for VS",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Cannot find VolumeInfo for PVC",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
volumeSnapshotClass: *builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
volumeSnapshotContent: *builder.ForVolumeSnapshotContent("testContent").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: stringPtr("testSnapshotHandle")}).Result(),
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Normal VolumeSnapshot case",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
CreationTimestamp: now,
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
RestoreSize: &resourceQuantity,
},
},
volumeSnapshotClass: *builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
volumeSnapshotContent: *builder.ForVolumeSnapshotContent("testContent").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: stringPtr("testSnapshotHandle")}).Result(),
pvMap: map[string]pvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testID",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "snapshot.storage.k8s.io",
Resource: "volumesnapshots",
},
Namespace: "velero",
Name: "testVS",
},
},
},
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: CSISnapshot,
OperationID: "testID",
StartTimestamp: &now,
PreserveLocalSnapshot: true,
CSISnapshotInfo: CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
SnapshotHandle: "testSnapshotHandle",
Size: 107374182400,
VSCName: "testContent",
},
PVInfo: PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
volumesInfo := VolumesInformation{}
volumesInfo.Init()
if tc.pvMap != nil {
for k, v := range tc.pvMap {
volumesInfo.pvMap[k] = v
}
}
if tc.operation != nil {
volumesInfo.BackupOperations = append(volumesInfo.BackupOperations, tc.operation)
}
volumesInfo.volumeSnapshots = []snapshotv1api.VolumeSnapshot{tc.volumeSnapshot}
volumesInfo.volumeSnapshotContents = []snapshotv1api.VolumeSnapshotContent{tc.volumeSnapshotContent}
volumesInfo.volumeSnapshotClasses = []snapshotv1api.VolumeSnapshotClass{tc.volumeSnapshotClass}
volumesInfo.logger = logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumesInfo.generateVolumeInfoForCSIVolumeSnapshot()
require.Equal(t, tc.expectedVolumeInfos, volumesInfo.volumeInfos)
})
}
}
func TestGenerateVolumeInfoFromPVB(t *testing.T) {
tests := []struct {
name string
pvb *velerov1api.PodVolumeBackup
pod *corev1api.Pod
pvMap map[string]pvcPvInfo
expectedVolumeInfos []*VolumeInfo
}{
{
name: "cannot find PVB's pod, should fail",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "PVB doesn't have a related PVC",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
HostPath: &corev1api.HostPathVolumeSource{},
},
},
).Result(),
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "",
PVCNamespace: "",
PVName: "",
BackupMethod: PodVolumeBackup,
PVBInfo: PodVolumeBackupInfo{
PodName: "testPod",
PodNamespace: "velero",
},
},
},
},
{
name: "Backup doesn't have information for PVC",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: "testPVC",
},
},
},
).Result(),
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "PVB's volume has a PVC",
pvMap: map[string]pvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: "testPVC",
},
},
},
).Result(),
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: PodVolumeBackup,
PVBInfo: PodVolumeBackupInfo{
PodName: "testPod",
PodNamespace: "velero",
},
PVInfo: PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
volumesInfo := VolumesInformation{}
volumesInfo.Init()
volumesInfo.crClient = velerotest.NewFakeControllerRuntimeClient(t)
volumesInfo.PodVolumeBackups = append(volumesInfo.PodVolumeBackups, tc.pvb)
if tc.pvMap != nil {
for k, v := range tc.pvMap {
volumesInfo.pvMap[k] = v
}
}
if tc.pod != nil {
require.NoError(t, volumesInfo.crClient.Create(context.TODO(), tc.pod))
}
volumesInfo.logger = logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumesInfo.generateVolumeInfoFromPVB()
require.Equal(t, tc.expectedVolumeInfos, volumesInfo.volumeInfos)
})
}
}
func TestGenerateVolumeInfoFromDataUpload(t *testing.T) {
now := metav1.Now()
tests := []struct {
name string
volumeSnapshotClass *snapshotv1api.VolumeSnapshotClass
dataUpload *velerov2alpha1.DataUpload
operation *itemoperation.BackupOperation
pvMap map[string]pvcPvInfo
expectedVolumeInfos []*VolumeInfo
}{
{
name: "Operation is not for PVC",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "configmaps",
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "Operation doesn't have DataUpload PostItemOperation",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "",
Resource: "configmaps",
},
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "DataUpload cannot be found for operation",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{},
},
{
name: "VolumeSnapshotClass cannot be found for operation",
dataUpload: builder.ForDataUpload("velero", "testDU").DataMover("velero").CSISnapshot(&velerov2alpha1.CSISnapshotSpec{
VolumeSnapshot: "testVS",
}).SnapshotID("testSnapshotHandle").Result(),
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
},
pvMap: map[string]pvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: CSISnapshot,
SnapshotDataMoved: true,
OperationID: "testOperation",
SnapshotDataMovementInfo: SnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "kopia",
},
PVInfo: PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
{
name: "Normal DataUpload case",
dataUpload: builder.ForDataUpload("velero", "testDU").DataMover("velero").CSISnapshot(&velerov2alpha1.CSISnapshotSpec{
VolumeSnapshot: "testVS",
SnapshotClass: "testClass",
}).SnapshotID("testSnapshotHandle").Result(),
volumeSnapshotClass: builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
Status: itemoperation.OperationStatus{
Created: &now,
},
},
pvMap: map[string]pvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []*VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: CSISnapshot,
SnapshotDataMoved: true,
OperationID: "testOperation",
StartTimestamp: &now,
CSISnapshotInfo: CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
},
SnapshotDataMovementInfo: SnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "kopia",
},
PVInfo: PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
volumesInfo := VolumesInformation{}
volumesInfo.Init()
if tc.operation != nil {
volumesInfo.BackupOperations = append(volumesInfo.BackupOperations, tc.operation)
}
if tc.pvMap != nil {
for k, v := range tc.pvMap {
volumesInfo.pvMap[k] = v
}
}
volumesInfo.crClient = velerotest.NewFakeControllerRuntimeClient(t)
if tc.dataUpload != nil {
volumesInfo.crClient.Create(context.TODO(), tc.dataUpload)
}
if tc.volumeSnapshotClass != nil {
volumesInfo.crClient.Create(context.TODO(), tc.volumeSnapshotClass)
}
volumesInfo.logger = logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumesInfo.generateVolumeInfoFromDataUpload()
require.Equal(t, tc.expectedVolumeInfos, volumesInfo.volumeInfos)
})
}
}
func stringPtr(str string) *string {
return &str
}
func int64Ptr(val int) *int64 {
i := int64(val)
return &i
}

View File

@ -72,7 +72,6 @@ func TestBackedUpItemsMatchesTarballContents(t *testing.T) {
Backup: defaultBackup().Result(),
SkippedPVTracker: NewSkipPVTracker(),
}
req.VolumesInformation.InitPVMap()
backupFile := bytes.NewBuffer([]byte{})

View File

@ -706,11 +706,8 @@ func (ib *itemBackupper) addVolumeInfo(obj runtime.Unstructured, log logrus.Fiel
pvcNamespace = pv.Spec.ClaimRef.Namespace
}
ib.backupRequest.VolumesInformation.InsertPVMap(pv.Name, PvcPvInfo{
PVCName: pvcName,
PVCNamespace: pvcNamespace,
PV: *pv,
})
ib.backupRequest.VolumesInformation.InsertPVMap(*pv, pvcName, pvcNamespace)
return nil
}

View File

@ -241,31 +241,16 @@ func TestRandom(t *testing.T) {
func TestAddVolumeInfo(t *testing.T) {
tests := []struct {
name string
pv *corev1api.PersistentVolume
expectedVolumeInfo map[string]PvcPvInfo
name string
pv *corev1api.PersistentVolume
}{
{
name: "PV has ClaimRef",
pv: builder.ForPersistentVolume("testPV").ClaimRef("testNS", "testPVC").Result(),
expectedVolumeInfo: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "testNS",
PV: *builder.ForPersistentVolume("testPV").ClaimRef("testNS", "testPVC").Result(),
},
},
},
{
name: "PV has no ClaimRef",
pv: builder.ForPersistentVolume("testPV").Result(),
expectedVolumeInfo: map[string]PvcPvInfo{
"testPV": {
PVCName: "",
PVCNamespace: "",
PV: *builder.ForPersistentVolume("testPV").Result(),
},
},
},
}
@ -273,7 +258,7 @@ func TestAddVolumeInfo(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
ib := itemBackupper{}
ib.backupRequest = new(Request)
ib.backupRequest.VolumesInformation.InitPVMap()
ib.backupRequest.VolumesInformation.Init()
pvObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pv)
require.NoError(t, err)
@ -281,7 +266,6 @@ func TestAddVolumeInfo(t *testing.T) {
err = ib.addVolumeInfo(&unstructured.Unstructured{Object: pvObj}, logger)
require.NoError(t, err)
//require.Equal(t, tc.expectedVolumeInfo, ib.backupRequest.VolumesInformation.GetPVMap())
})
}
}

View File

@ -17,25 +17,15 @@ limitations under the License.
package backup
import (
"context"
"fmt"
"sort"
"strconv"
corev1api "k8s.io/api/core/v1"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/sirupsen/logrus"
"github.com/vmware-tanzu/velero/internal/hook"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
internalVolume "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/itemoperation"
"github.com/vmware-tanzu/velero/pkg/kuberesource"
"github.com/vmware-tanzu/velero/pkg/plugin/framework"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
"github.com/vmware-tanzu/velero/pkg/util/collections"
"github.com/vmware-tanzu/velero/pkg/volume"
)
@ -63,381 +53,11 @@ type Request struct {
itemOperationsList *[]*itemoperation.BackupOperation
ResPolicies *resourcepolicies.Policies
SkippedPVTracker *skipPVTracker
VolumesInformation VolumesInformation
VolumesInformation internalVolume.VolumesInformation
}
// VolumesInformation contains the information needs by generating
// the backup VolumeInfo array.
type VolumesInformation struct {
// A map contains the backup-included PV detail content. The key is PV name.
pvMap map[string]PvcPvInfo
}
func (v *VolumesInformation) InitPVMap() {
v.pvMap = make(map[string]PvcPvInfo)
}
func (v *VolumesInformation) InsertPVMap(pvName string, pv PvcPvInfo) {
if v.pvMap == nil {
v.InitPVMap()
}
v.pvMap[pvName] = pv
}
func (v *VolumesInformation) GenerateVolumeInfo(backup *Request, csiVolumeSnapshots []snapshotv1api.VolumeSnapshot,
csiVolumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, csiVolumesnapshotClasses []snapshotv1api.VolumeSnapshotClass,
crClient kbclient.Client, logger logrus.FieldLogger) []volume.VolumeInfo {
volumeInfos := make([]volume.VolumeInfo, 0)
skippedVolumeInfos := generateVolumeInfoForSkippedPV(*v, backup, logger)
volumeInfos = append(volumeInfos, skippedVolumeInfos...)
nativeSnapshotVolumeInfos := generateVolumeInfoForVeleroNativeSnapshot(*v, backup, logger)
volumeInfos = append(volumeInfos, nativeSnapshotVolumeInfos...)
csiVolumeInfos := generateVolumeInfoForCSIVolumeSnapshot(*v, backup, csiVolumeSnapshots, csiVolumeSnapshotContents, csiVolumesnapshotClasses, logger)
volumeInfos = append(volumeInfos, csiVolumeInfos...)
pvbVolumeInfos := generateVolumeInfoFromPVB(*v, backup, crClient, logger)
volumeInfos = append(volumeInfos, pvbVolumeInfos...)
dataUploadVolumeInfos := generateVolumeInfoFromDataUpload(*v, backup, crClient, logger)
volumeInfos = append(volumeInfos, dataUploadVolumeInfos...)
return volumeInfos
}
// generateVolumeInfoForSkippedPV generate VolumeInfos for SkippedPV.
func generateVolumeInfoForSkippedPV(info VolumesInformation, backup *Request, logger logrus.FieldLogger) []volume.VolumeInfo {
tmpVolumeInfos := make([]volume.VolumeInfo, 0)
for _, skippedPV := range backup.SkippedPVTracker.Summary() {
if pvcPVInfo := info.retrievePvcPvInfo(skippedPV.Name, "", ""); pvcPVInfo != nil {
volumeInfo := volume.VolumeInfo{
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: skippedPV.Name,
SnapshotDataMoved: false,
Skipped: true,
SkippedReason: skippedPV.SerializeSkipReasons(),
PVInfo: volume.PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
logger.Warnf("Cannot find info for PV %s", skippedPV.Name)
continue
}
}
return tmpVolumeInfos
}
// generateVolumeInfoForVeleroNativeSnapshot generate VolumeInfos for Velero native snapshot
func generateVolumeInfoForVeleroNativeSnapshot(info VolumesInformation, backup *Request, logger logrus.FieldLogger) []volume.VolumeInfo {
tmpVolumeInfos := make([]volume.VolumeInfo, 0)
for _, nativeSnapshot := range backup.VolumeSnapshots {
var iops int64
if nativeSnapshot.Spec.VolumeIOPS != nil {
iops = *nativeSnapshot.Spec.VolumeIOPS
}
if pvcPVInfo := info.retrievePvcPvInfo(nativeSnapshot.Spec.PersistentVolumeName, "", ""); pvcPVInfo != nil {
volumeInfo := volume.VolumeInfo{
BackupMethod: volume.NativeSnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
SnapshotDataMoved: false,
Skipped: false,
NativeSnapshotInfo: volume.NativeSnapshotInfo{
SnapshotHandle: nativeSnapshot.Status.ProviderSnapshotID,
VolumeType: nativeSnapshot.Spec.VolumeType,
VolumeAZ: nativeSnapshot.Spec.VolumeAZ,
IOPS: strconv.FormatInt(iops, 10),
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
logger.Warnf("cannot find info for PV %s", nativeSnapshot.Spec.PersistentVolumeName)
continue
}
}
return tmpVolumeInfos
}
// generateVolumeInfoForCSIVolumeSnapshot generate VolumeInfos for CSI VolumeSnapshot
func generateVolumeInfoForCSIVolumeSnapshot(info VolumesInformation, backup *Request, csiVolumeSnapshots []snapshotv1api.VolumeSnapshot,
csiVolumeSnapshotContents []snapshotv1api.VolumeSnapshotContent, csiVolumesnapshotClasses []snapshotv1api.VolumeSnapshotClass,
logger logrus.FieldLogger) []volume.VolumeInfo {
tmpVolumeInfos := make([]volume.VolumeInfo, 0)
for _, volumeSnapshot := range csiVolumeSnapshots {
var volumeSnapshotClass *snapshotv1api.VolumeSnapshotClass
var volumeSnapshotContent *snapshotv1api.VolumeSnapshotContent
// This is protective logic. The passed-in VS should be all related
// to this backup.
if volumeSnapshot.Labels[velerov1api.BackupNameLabel] != backup.Name {
continue
}
if volumeSnapshot.Spec.VolumeSnapshotClassName == nil {
logger.Warnf("Cannot find VolumeSnapshotClass for VolumeSnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
if volumeSnapshot.Status == nil || volumeSnapshot.Status.BoundVolumeSnapshotContentName == nil {
logger.Warnf("Cannot fine VolumeSnapshotContent for VolumeSnapshot %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
if volumeSnapshot.Spec.Source.PersistentVolumeClaimName == nil {
logger.Warnf("VolumeSnapshot %s/%s doesn't have a source PVC", volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
for index := range csiVolumesnapshotClasses {
if *volumeSnapshot.Spec.VolumeSnapshotClassName == csiVolumesnapshotClasses[index].Name {
volumeSnapshotClass = &csiVolumesnapshotClasses[index]
}
}
for index := range csiVolumeSnapshotContents {
if *volumeSnapshot.Status.BoundVolumeSnapshotContentName == csiVolumeSnapshotContents[index].Name {
volumeSnapshotContent = &csiVolumeSnapshotContents[index]
}
}
if volumeSnapshotClass == nil || volumeSnapshotContent == nil {
logger.Warnf("fail to get VolumeSnapshotContent or VolumeSnapshotClass for VolumeSnapshot: %s/%s",
volumeSnapshot.Namespace, volumeSnapshot.Name)
continue
}
var operation itemoperation.BackupOperation
for _, op := range *backup.GetItemOperationsList() {
if op.Spec.ResourceIdentifier.GroupResource.String() == kuberesource.VolumeSnapshots.String() &&
op.Spec.ResourceIdentifier.Name == volumeSnapshot.Name &&
op.Spec.ResourceIdentifier.Namespace == volumeSnapshot.Namespace {
operation = *op
}
}
var size int64
if volumeSnapshot.Status.RestoreSize != nil {
size = volumeSnapshot.Status.RestoreSize.Value()
}
snapshotHandle := ""
if volumeSnapshotContent.Status.SnapshotHandle != nil {
snapshotHandle = *volumeSnapshotContent.Status.SnapshotHandle
}
if pvcPVInfo := info.retrievePvcPvInfo("", *volumeSnapshot.Spec.Source.PersistentVolumeClaimName, volumeSnapshot.Namespace); pvcPVInfo != nil {
volumeInfo := volume.VolumeInfo{
BackupMethod: volume.CSISnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
Skipped: false,
SnapshotDataMoved: false,
PreserveLocalSnapshot: true,
OperationID: operation.Spec.OperationID,
StartTimestamp: &(volumeSnapshot.CreationTimestamp),
CSISnapshotInfo: volume.CSISnapshotInfo{
VSCName: *volumeSnapshot.Status.BoundVolumeSnapshotContentName,
Size: size,
Driver: volumeSnapshotClass.Driver,
SnapshotHandle: snapshotHandle,
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
logger.Warnf("cannot find info for PVC %s/%s", volumeSnapshot.Namespace, volumeSnapshot.Spec.Source.PersistentVolumeClaimName)
continue
}
}
return tmpVolumeInfos
}
// generateVolumeInfoFromPVB generate VolumeInfo for PVB.
func generateVolumeInfoFromPVB(info VolumesInformation, backup *Request, crClient kbclient.Client, logger logrus.FieldLogger) []volume.VolumeInfo {
tmpVolumeInfos := make([]volume.VolumeInfo, 0)
for _, pvb := range backup.PodVolumeBackups {
volumeInfo := volume.VolumeInfo{
BackupMethod: volume.PodVolumeBackup,
SnapshotDataMoved: false,
Skipped: false,
StartTimestamp: pvb.Status.StartTimestamp,
PVBInfo: volume.PodVolumeBackupInfo{
SnapshotHandle: pvb.Status.SnapshotID,
Size: pvb.Status.Progress.TotalBytes,
UploaderType: pvb.Spec.UploaderType,
VolumeName: pvb.Spec.Volume,
PodName: pvb.Spec.Pod.Name,
PodNamespace: pvb.Spec.Pod.Namespace,
NodeName: pvb.Spec.Node,
},
}
pod := new(corev1api.Pod)
pvcName := ""
err := crClient.Get(context.TODO(), kbclient.ObjectKey{Namespace: pvb.Spec.Pod.Namespace, Name: pvb.Spec.Pod.Name}, pod)
if err != nil {
logger.WithError(err).Warn("Fail to get pod for PodVolumeBackup: ", pvb.Name)
continue
}
for _, volume := range pod.Spec.Volumes {
if volume.Name == pvb.Spec.Volume && volume.PersistentVolumeClaim != nil {
pvcName = volume.PersistentVolumeClaim.ClaimName
}
}
if pvcName != "" {
if pvcPVInfo := info.retrievePvcPvInfo("", pvcName, pod.Namespace); pvcPVInfo != nil {
volumeInfo.PVCName = pvcPVInfo.PVCName
volumeInfo.PVCNamespace = pvcPVInfo.PVCNamespace
volumeInfo.PVName = pvcPVInfo.PV.Name
volumeInfo.PVInfo = volume.PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
}
} else {
logger.Warnf("Cannot find info for PVC %s/%s", pod.Namespace, pvcName)
continue
}
} else {
logger.Debug("The PVB %s doesn't have a corresponding PVC", pvb.Name)
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
}
return tmpVolumeInfos
}
// generateVolumeInfoFromDataUpload generate VolumeInfo for DataUpload.
func generateVolumeInfoFromDataUpload(info VolumesInformation, backup *Request, crClient kbclient.Client, logger logrus.FieldLogger) []volume.VolumeInfo {
tmpVolumeInfos := make([]volume.VolumeInfo, 0)
vsClassList := new(snapshotv1api.VolumeSnapshotClassList)
if err := crClient.List(context.TODO(), vsClassList); err != nil {
logger.WithError(err).Errorf("cannot list VolumeSnapshotClass %s", err.Error())
return tmpVolumeInfos
}
for _, operation := range *backup.GetItemOperationsList() {
if operation.Spec.ResourceIdentifier.GroupResource.String() == kuberesource.PersistentVolumeClaims.String() {
var duIdentifier velero.ResourceIdentifier
for _, identifier := range operation.Spec.PostOperationItems {
if identifier.GroupResource.String() == "datauploads.velero.io" {
duIdentifier = identifier
}
}
if duIdentifier.Empty() {
logger.Warnf("cannot find DataUpload for PVC %s/%s backup async operation",
operation.Spec.ResourceIdentifier.Namespace, operation.Spec.ResourceIdentifier.Name)
continue
}
dataUpload := new(velerov2alpha1.DataUpload)
err := crClient.Get(
context.TODO(),
kbclient.ObjectKey{
Namespace: duIdentifier.Namespace,
Name: duIdentifier.Name},
dataUpload,
)
if err != nil {
logger.Warnf("fail to get DataUpload for operation %s: %s", operation.Spec.OperationID, err.Error())
continue
}
driverUsedByVSClass := ""
for index := range vsClassList.Items {
if vsClassList.Items[index].Name == dataUpload.Spec.CSISnapshot.SnapshotClass {
driverUsedByVSClass = vsClassList.Items[index].Driver
}
}
if pvcPVInfo := info.retrievePvcPvInfo("", operation.Spec.ResourceIdentifier.Name, operation.Spec.ResourceIdentifier.Namespace); pvcPVInfo != nil {
volumeInfo := volume.VolumeInfo{
BackupMethod: volume.CSISnapshot,
PVCName: pvcPVInfo.PVCName,
PVCNamespace: pvcPVInfo.PVCNamespace,
PVName: pvcPVInfo.PV.Name,
SnapshotDataMoved: true,
Skipped: false,
OperationID: operation.Spec.OperationID,
StartTimestamp: operation.Status.Created,
CSISnapshotInfo: volume.CSISnapshotInfo{
Driver: driverUsedByVSClass,
},
SnapshotDataMovementInfo: volume.SnapshotDataMovementInfo{
DataMover: dataUpload.Spec.DataMover,
UploaderType: "kopia",
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy),
Labels: pvcPVInfo.PV.Labels,
},
}
tmpVolumeInfos = append(tmpVolumeInfos, volumeInfo)
} else {
logger.Warnf("Cannot find info for PVC %s/%s", operation.Spec.ResourceIdentifier.Namespace, operation.Spec.ResourceIdentifier.Name)
continue
}
}
}
return tmpVolumeInfos
}
// retrievePvcPvInfo gets the PvcPvInfo from the PVMap.
// support retrieve info by PV's name, or by PVC's name
// and namespace.
func (v *VolumesInformation) retrievePvcPvInfo(pvName, pvcName, pvcNS string) *PvcPvInfo {
if pvName != "" {
if info, ok := v.pvMap[pvName]; ok {
return &info
}
return nil
}
if pvcNS == "" || pvcName == "" {
return nil
}
for _, info := range v.pvMap {
if pvcNS == info.PVCNamespace && pvcName == info.PVCName {
return &info
}
}
return nil
}
type PvcPvInfo struct {
PVCName string
PVCNamespace string
PV corev1api.PersistentVolume
}
// GetItemOperationsList returns ItemOperationsList, initializing it if necessary
func (r *Request) GetItemOperationsList() *[]*itemoperation.BackupOperation {
@ -467,3 +87,17 @@ func (r *Request) BackupResourceList() map[string][]string {
return resources
}
func (r *Request) FillVolumesInformation() {
skippedPVMap := make(map[string]string)
for _, skippedPV := range r.SkippedPVTracker.Summary() {
skippedPVMap[skippedPV.Name] = skippedPV.SerializeSkipReasons()
}
r.VolumesInformation.SkippedPVs = skippedPVMap
r.VolumesInformation.NativeSnapshots = r.VolumeSnapshots
r.VolumesInformation.PodVolumeBackups = r.PodVolumeBackups
r.VolumesInformation.BackupOperations = *r.GetItemOperationsList()
r.VolumesInformation.BackupName = r.Backup.Name
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
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.
@ -17,26 +17,9 @@ limitations under the License.
package backup
import (
"context"
"testing"
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
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/itemoperation"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/util/logging"
"github.com/vmware-tanzu/velero/pkg/volume"
)
func TestRequest_BackupResourceList(t *testing.T) {
@ -97,804 +80,3 @@ func TestRequest_BackupResourceListEntriesSorted(t *testing.T) {
"v1/Pod": {"ns1/pod1", "ns2/pod2"},
}, req.BackupResourceList())
}
func TestGenerateVolumeInfoForSkippedPV(t *testing.T) {
tests := []struct {
name string
skippedPVName string
pvMap map[string]PvcPvInfo
expectedVolumeInfos []volume.VolumeInfo
}{
{
name: "Cannot find info for PV",
skippedPVName: "testPV",
pvMap: map[string]PvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Normal Skipped PV info",
skippedPVName: "testPV",
pvMap: map[string]PvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
Skipped: true,
SkippedReason: "CSI: skipped for PodVolumeBackup;",
PVInfo: volume.PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
request := new(Request)
request.SkippedPVTracker = NewSkipPVTracker()
request.VolumesInformation.InitPVMap()
if tc.skippedPVName != "" {
request.SkippedPVTracker.Track(tc.skippedPVName, "CSI", "skipped for PodVolumeBackup")
}
if tc.pvMap != nil {
for k, v := range tc.pvMap {
request.VolumesInformation.InsertPVMap(k, v)
}
}
logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumeInfos := generateVolumeInfoForSkippedPV(request.VolumesInformation, request, logger)
require.Equal(t, tc.expectedVolumeInfos, volumeInfos)
})
}
}
func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) {
tests := []struct {
name string
nativeSnapshot volume.Snapshot
pvMap map[string]PvcPvInfo
expectedVolumeInfos []volume.VolumeInfo
}{
{
name: "Native snapshot's IPOS pointer is nil",
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: nil,
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Cannot find info for the PV",
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Cannot find PV info in pvMap",
pvMap: map[string]PvcPvInfo{
"velero/testPVC": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
},
Status: volume.SnapshotStatus{
ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Normal native snapshot",
pvMap: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
nativeSnapshot: volume.Snapshot{
Spec: volume.SnapshotSpec{
PersistentVolumeName: "testPV",
VolumeIOPS: int64Ptr(100),
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
},
Status: volume.SnapshotStatus{
ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
},
},
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: volume.NativeSnapshot,
PVInfo: volume.PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
NativeSnapshotInfo: volume.NativeSnapshotInfo{
SnapshotHandle: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c",
VolumeType: "ssd",
VolumeAZ: "us-central1-a",
IOPS: "100",
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
request := new(Request)
request.VolumeSnapshots = append(request.VolumeSnapshots, &tc.nativeSnapshot)
request.VolumesInformation.InitPVMap()
if tc.pvMap != nil {
for k, v := range tc.pvMap {
request.VolumesInformation.InsertPVMap(k, v)
}
}
logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumeInfos := generateVolumeInfoForVeleroNativeSnapshot(request.VolumesInformation, request, logger)
require.Equal(t, tc.expectedVolumeInfos, volumeInfos)
})
}
}
func TestGenerateVolumeInfoForCSIVolumeSnapshot(t *testing.T) {
resourceQuantity := resource.MustParse("100Gi")
now := metav1.Now()
tests := []struct {
name string
volumeSnapshot snapshotv1api.VolumeSnapshot
volumeSnapshotContent snapshotv1api.VolumeSnapshotContent
volumeSnapshotClass snapshotv1api.VolumeSnapshotClass
pvMap map[string]PvcPvInfo
operation *itemoperation.BackupOperation
expectedVolumeInfos []volume.VolumeInfo
}{
{
name: "VS doesn't have VolumeSnapshotClass name",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "VS doesn't have status",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "VS doesn't have PVC",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Cannot find VSC for VS",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Cannot find VolumeInfo for PVC",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
},
},
volumeSnapshotClass: *builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
volumeSnapshotContent: *builder.ForVolumeSnapshotContent("testContent").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: stringPtr("testSnapshotHandle")}).Result(),
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Normal VolumeSnapshot case",
volumeSnapshot: snapshotv1api.VolumeSnapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "testVS",
Namespace: "velero",
CreationTimestamp: now,
},
Spec: snapshotv1api.VolumeSnapshotSpec{
VolumeSnapshotClassName: stringPtr("testClass"),
Source: snapshotv1api.VolumeSnapshotSource{
PersistentVolumeClaimName: stringPtr("testPVC"),
},
},
Status: &snapshotv1api.VolumeSnapshotStatus{
BoundVolumeSnapshotContentName: stringPtr("testContent"),
RestoreSize: &resourceQuantity,
},
},
volumeSnapshotClass: *builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
volumeSnapshotContent: *builder.ForVolumeSnapshotContent("testContent").Status(&snapshotv1api.VolumeSnapshotContentStatus{SnapshotHandle: stringPtr("testSnapshotHandle")}).Result(),
pvMap: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testID",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "snapshot.storage.k8s.io",
Resource: "volumesnapshots",
},
Namespace: "velero",
Name: "testVS",
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: volume.CSISnapshot,
OperationID: "testID",
StartTimestamp: &now,
PreserveLocalSnapshot: true,
CSISnapshotInfo: volume.CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
SnapshotHandle: "testSnapshotHandle",
Size: 107374182400,
VSCName: "testContent",
},
PVInfo: volume.PVInfo{
ReclaimPolicy: "Delete",
Labels: map[string]string{
"a": "b",
},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
request := new(Request)
request.Backup = new(velerov1api.Backup)
request.VolumesInformation.InitPVMap()
if tc.pvMap != nil {
for k, v := range tc.pvMap {
request.VolumesInformation.InsertPVMap(k, v)
}
}
operationList := request.GetItemOperationsList()
if tc.operation != nil {
*operationList = append(*operationList, tc.operation)
}
logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
volumeInfos := generateVolumeInfoForCSIVolumeSnapshot(request.VolumesInformation, request, []snapshotv1api.VolumeSnapshot{tc.volumeSnapshot}, []snapshotv1api.VolumeSnapshotContent{tc.volumeSnapshotContent}, []snapshotv1api.VolumeSnapshotClass{tc.volumeSnapshotClass}, logger)
require.Equal(t, tc.expectedVolumeInfos, volumeInfos)
})
}
}
func TestGenerateVolumeInfoFromPVB(t *testing.T) {
tests := []struct {
name string
pvb *velerov1api.PodVolumeBackup
pod *corev1api.Pod
pvMap map[string]PvcPvInfo
expectedVolumeInfos []volume.VolumeInfo
}{
{
name: "cannot find PVB's pod, should fail",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "PVB doesn't have a related PVC",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
HostPath: &corev1api.HostPathVolumeSource{},
},
},
).Result(),
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "",
PVCNamespace: "",
PVName: "",
BackupMethod: volume.PodVolumeBackup,
PVBInfo: volume.PodVolumeBackupInfo{
PodName: "testPod",
PodNamespace: "velero",
},
},
},
},
{
name: "Backup doesn't have information for PVC",
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: "testPVC",
},
},
},
).Result(),
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "PVB's volume has a PVC",
pvMap: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").Result(),
pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{
Name: "test",
VolumeMounts: []corev1api.VolumeMount{
{
Name: "testVolume",
MountPath: "/data",
},
},
}).Volumes(
&corev1api.Volume{
Name: "",
VolumeSource: corev1api.VolumeSource{
PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{
ClaimName: "testPVC",
},
},
},
).Result(),
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: volume.PodVolumeBackup,
PVBInfo: volume.PodVolumeBackupInfo{
PodName: "testPod",
PodNamespace: "velero",
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
crClient := velerotest.NewFakeControllerRuntimeClient(t)
logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
request := new(Request)
request.PodVolumeBackups = append(request.PodVolumeBackups, tc.pvb)
request.VolumesInformation.InitPVMap()
if tc.pvMap != nil {
for k, v := range tc.pvMap {
request.VolumesInformation.InsertPVMap(k, v)
}
}
if tc.pod != nil {
require.NoError(t, crClient.Create(context.TODO(), tc.pod))
}
volumeInfos := generateVolumeInfoFromPVB(request.VolumesInformation, request, crClient, logger)
require.Equal(t, tc.expectedVolumeInfos, volumeInfos)
})
}
}
func TestGenerateVolumeInfoFromDataUpload(t *testing.T) {
now := metav1.Now()
tests := []struct {
name string
volumeSnapshotClass *snapshotv1api.VolumeSnapshotClass
dataUpload *velerov2alpha1.DataUpload
operation *itemoperation.BackupOperation
pvMap map[string]PvcPvInfo
expectedVolumeInfos []volume.VolumeInfo
}{
{
name: "Operation is not for PVC",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "configmaps",
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "Operation doesn't have DataUpload PostItemOperation",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "",
Resource: "configmaps",
},
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "DataUpload cannot be found for operation",
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{},
},
{
name: "VolumeSnapshotClass cannot be found for operation",
dataUpload: builder.ForDataUpload("velero", "testDU").DataMover("velero").CSISnapshot(&velerov2alpha1.CSISnapshotSpec{
VolumeSnapshot: "testVS",
}).SnapshotID("testSnapshotHandle").Result(),
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
},
pvMap: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: volume.CSISnapshot,
SnapshotDataMoved: true,
OperationID: "testOperation",
SnapshotDataMovementInfo: volume.SnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "kopia",
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
{
name: "Normal DataUpload case",
dataUpload: builder.ForDataUpload("velero", "testDU").DataMover("velero").CSISnapshot(&velerov2alpha1.CSISnapshotSpec{
VolumeSnapshot: "testVS",
SnapshotClass: "testClass",
}).SnapshotID("testSnapshotHandle").Result(),
volumeSnapshotClass: builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(),
operation: &itemoperation.BackupOperation{
Spec: itemoperation.BackupOperationSpec{
OperationID: "testOperation",
ResourceIdentifier: velero.ResourceIdentifier{
GroupResource: schema.GroupResource{
Group: "",
Resource: "persistentvolumeclaims",
},
Namespace: "velero",
Name: "testPVC",
},
PostOperationItems: []velero.ResourceIdentifier{
{
GroupResource: schema.GroupResource{
Group: "velero.io",
Resource: "datauploads",
},
Namespace: "velero",
Name: "testDU",
},
},
},
Status: itemoperation.OperationStatus{
Created: &now,
},
},
pvMap: map[string]PvcPvInfo{
"testPV": {
PVCName: "testPVC",
PVCNamespace: "velero",
PV: corev1api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "testPV",
Labels: map[string]string{"a": "b"},
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
},
},
},
},
expectedVolumeInfos: []volume.VolumeInfo{
{
PVCName: "testPVC",
PVCNamespace: "velero",
PVName: "testPV",
BackupMethod: volume.CSISnapshot,
SnapshotDataMoved: true,
OperationID: "testOperation",
StartTimestamp: &now,
CSISnapshotInfo: volume.CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
},
SnapshotDataMovementInfo: volume.SnapshotDataMovementInfo{
DataMover: "velero",
UploaderType: "kopia",
},
PVInfo: volume.PVInfo{
ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete),
Labels: map[string]string{"a": "b"},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
request := new(Request)
request.VolumesInformation.InitPVMap()
operationList := request.GetItemOperationsList()
if tc.operation != nil {
*operationList = append(*operationList, tc.operation)
}
if tc.pvMap != nil {
for k, v := range tc.pvMap {
request.VolumesInformation.InsertPVMap(k, v)
}
}
logger := logging.DefaultLogger(logrus.DebugLevel, logging.FormatJSON)
crClient := velerotest.NewFakeControllerRuntimeClient(t)
if tc.dataUpload != nil {
crClient.Create(context.TODO(), tc.dataUpload)
}
if tc.volumeSnapshotClass != nil {
crClient.Create(context.TODO(), tc.volumeSnapshotClass)
}
volumeInfos := generateVolumeInfoFromDataUpload(request.VolumesInformation, request, crClient, logger)
require.Equal(t, tc.expectedVolumeInfos, volumeInfos)
})
}
}
func stringPtr(str string) *string {
return &str
}

View File

@ -1,3 +1,19 @@
/*
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 (

View File

@ -123,7 +123,7 @@ func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name strin
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, created.Status.DownloadURL, nil)
httpReq, err := http.NewRequest(http.MethodGet, created.Status.DownloadURL, nil)
if err != nil {
return err
}

View File

@ -40,6 +40,7 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
"github.com/vmware-tanzu/velero/internal/storage"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
"github.com/vmware-tanzu/velero/pkg/discovery"
@ -315,7 +316,7 @@ func (b *backupReconciler) prepareBackupRequest(backup *velerov1api.Backup, logg
Backup: backup.DeepCopy(), // don't modify items in the cache
SkippedPVTracker: pkgbackup.NewSkipPVTracker(),
}
request.VolumesInformation.InitPVMap()
request.VolumesInformation.Init()
// set backup major version - deprecated, use Status.FormatVersion
request.Status.Version = pkgbackup.BackupVersion
@ -586,7 +587,7 @@ func (b *backupReconciler) validateAndGetSnapshotLocations(backup *velerov1api.B
// add credential to config for each location
for _, location := range providerLocations {
err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(location, b.credentialFileStore)
err = internalVolume.UpdateVolumeSnapshotLocationWithCredentialConfig(location, b.credentialFileStore)
if err != nil {
errors = append(errors, fmt.Sprintf("error adding credentials to volume snapshot location named %s: %v", location.Name, err))
continue
@ -842,8 +843,15 @@ func persistBackup(backup *pkgbackup.Request,
persistErrs = append(persistErrs, errs...)
}
volumeInfoJSON, errs := encode.ToJSONGzip(backup.VolumesInformation.GenerateVolumeInfo(backup, csiVolumeSnapshots,
csiVolumeSnapshotContents, csiVolumeSnapshotClasses, crClient, logger), "backup volumes information")
backup.FillVolumesInformation()
volumeInfoJSON, errs := encode.ToJSONGzip(backup.VolumesInformation.Result(
csiVolumeSnapshots,
csiVolumeSnapshotContents,
csiVolumeSnapshotClasses,
crClient,
logger,
), "backup volumes information")
if errs != nil {
persistErrs = append(persistErrs, errs...)
}

View File

@ -22,40 +22,35 @@ import (
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/utils/clock"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/delete"
internalVolume "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/discovery"
"github.com/vmware-tanzu/velero/pkg/label"
"github.com/vmware-tanzu/velero/pkg/metrics"
"github.com/vmware-tanzu/velero/pkg/persistence"
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1"
"github.com/vmware-tanzu/velero/pkg/podvolume"
"github.com/vmware-tanzu/velero/pkg/repository"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
"github.com/vmware-tanzu/velero/pkg/util/kube"
"github.com/vmware-tanzu/velero/pkg/volume"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/podvolume"
)
const (
@ -462,7 +457,7 @@ func (r *backupDeletionReconciler) volumeSnapshottersForVSL(
}
// add credential to config
err := volume.UpdateVolumeSnapshotLocationWithCredentialConfig(vsl, r.credentialStore)
err := internalVolume.UpdateVolumeSnapshotLocationWithCredentialConfig(vsl, r.credentialStore)
if err != nil {
return nil, errors.WithStack(err)
}

View File

@ -44,6 +44,7 @@ import (
"github.com/vmware-tanzu/velero/internal/hook"
"github.com/vmware-tanzu/velero/internal/resourcemodifiers"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
"github.com/vmware-tanzu/velero/pkg/label"
@ -56,7 +57,6 @@ import (
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
"github.com/vmware-tanzu/velero/pkg/util/logging"
"github.com/vmware-tanzu/velero/pkg/util/results"
"github.com/vmware-tanzu/velero/pkg/volume"
)
// nonRestorableResources is an exclusion list for the restoration process. Any resources
@ -521,13 +521,13 @@ func (r *restoreReconciler) runValidatedRestore(restore *api.Restore, info backu
return errors.Wrap(err, "fail to fetch CSI VolumeSnapshots metadata")
}
backupVolumeInfoMap := make(map[string]volume.VolumeInfo)
backupVolumeInfoMap := make(map[string]internalVolume.VolumeInfo)
volumeInfos, err := backupStore.GetBackupVolumeInfos(restore.Spec.BackupName)
if err != nil || volumeInfos == nil {
if err != nil || len(volumeInfos) == 0 {
restoreLog.Infof("Backup %s doesn't have volumeinfos metadata file.", restore.Spec.BackupName)
} else {
for _, volumeInfo := range volumeInfos.VolumeInfos {
backupVolumeInfoMap[volumeInfo.PVName] = volumeInfo
for _, volumeInfo := range volumeInfos {
backupVolumeInfoMap[volumeInfo.PVName] = *volumeInfo
}
}

View File

@ -29,15 +29,15 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
clocktesting "k8s.io/utils/clock/testing"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
corev1 "k8s.io/api/core/v1"
"github.com/vmware-tanzu/velero/internal/resourcemodifiers"
internalVolume "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/metrics"
@ -498,7 +498,7 @@ func TestRestoreReconcile(t *testing.T) {
if test.emptyVolumeInfo == true {
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return(nil, nil)
} else {
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return(&volume.VolumeInfos{}, nil)
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return([]*internalVolume.VolumeInfo{}, nil)
}
volumeSnapshots := []*volume.Snapshot{

View File

@ -21,15 +21,14 @@ import (
io "io"
mock "github.com/stretchr/testify/mock"
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
itemoperation "github.com/vmware-tanzu/velero/pkg/itemoperation"
persistence "github.com/vmware-tanzu/velero/pkg/persistence"
"github.com/vmware-tanzu/velero/pkg/persistence"
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
volume "github.com/vmware-tanzu/velero/pkg/volume"
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
)
// BackupStore is an autogenerated mock type for the BackupStore type
@ -315,15 +314,15 @@ func (_m *BackupStore) GetRestoreItemOperations(name string) ([]*itemoperation.R
}
// GetRestoreItemOperations provides a mock function with given fields: name
func (_m *BackupStore) GetBackupVolumeInfos(name string) (*volume.VolumeInfos, error) {
func (_m *BackupStore) GetBackupVolumeInfos(name string) ([]*internalVolume.VolumeInfo, error) {
ret := _m.Called(name)
var r0 *volume.VolumeInfos
if rf, ok := ret.Get(0).(func(string) *volume.VolumeInfos); ok {
var r0 []*internalVolume.VolumeInfo
if rf, ok := ret.Get(0).(func(string) []*internalVolume.VolumeInfo); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*volume.VolumeInfos)
r0 = ret.Get(0).([]*internalVolume.VolumeInfo)
}
}

View File

@ -31,6 +31,7 @@ import (
kerrors "k8s.io/apimachinery/pkg/util/errors"
"github.com/vmware-tanzu/velero/internal/credentials"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
@ -73,7 +74,7 @@ type BackupStore interface {
GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error)
GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1api.VolumeSnapshotContent, error)
GetCSIVolumeSnapshotClasses(name string) ([]*snapshotv1api.VolumeSnapshotClass, error)
GetBackupVolumeInfos(name string) (*volume.VolumeInfos, error)
GetBackupVolumeInfos(name string) ([]*internalVolume.VolumeInfo, error)
// BackupExists checks if the backup metadata file exists in object storage.
BackupExists(bucket, backupName string) (bool, error)
@ -494,8 +495,8 @@ func (s *objectBackupStore) GetPodVolumeBackups(name string) ([]*velerov1api.Pod
return podVolumeBackups, nil
}
func (s *objectBackupStore) GetBackupVolumeInfos(name string) (*volume.VolumeInfos, error) {
var volumeInfos *volume.VolumeInfos
func (s *objectBackupStore) GetBackupVolumeInfos(name string) ([]*internalVolume.VolumeInfo, error) {
volumeInfos := make([]*internalVolume.VolumeInfo, 0)
res, err := tryGet(s.objectStore, s.bucket, s.layout.getBackupVolumeInfoKey(name))
if err != nil {

View File

@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"github.com/vmware-tanzu/velero/internal/credentials"
internalVolume "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"
@ -1067,27 +1068,25 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
func TestGetBackupVolumeInfos(t *testing.T) {
tests := []struct {
name string
volumeInfo *volume.VolumeInfos
volumeInfo []*internalVolume.VolumeInfo
volumeInfoStr string
expectedErr string
expectedResult []volume.VolumeInfo
expectedResult []*internalVolume.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,
},
volumeInfo: []*internalVolume.VolumeInfo{
{
PVCName: "pvcName",
PVName: "pvName",
Skipped: true,
SnapshotDataMoved: false,
},
},
expectedResult: []volume.VolumeInfo{
expectedResult: []*internalVolume.VolumeInfo{
{
PVCName: "pvcName",
PVName: "pvName",
@ -1098,8 +1097,8 @@ func TestGetBackupVolumeInfos(t *testing.T) {
},
{
name: "Invalid VolumeInfo string, should also pass.",
volumeInfoStr: `{"volumeInfos": [{"abc": "123", "def": "456", "pvcName": "pvcName"}]}`,
expectedResult: []volume.VolumeInfo{
volumeInfoStr: `[{"abc": "123", "def": "456", "pvcName": "pvcName"}]`,
expectedResult: []*internalVolume.VolumeInfo{
{
PVCName: "pvcName",
},
@ -1141,7 +1140,7 @@ func TestGetBackupVolumeInfos(t *testing.T) {
}
if len(tc.expectedResult) > 0 {
require.Equal(t, tc.expectedResult, result.VolumeInfos)
require.Equal(t, tc.expectedResult, result)
}
})

View File

@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/internal/credentials"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
"github.com/vmware-tanzu/velero/pkg/volume"
@ -132,7 +133,7 @@ func getSnapshotInfo(pvName string, backup *api.Backup, volumeSnapshots []*volum
}
// add credential to config
err = volume.UpdateVolumeSnapshotLocationWithCredentialConfig(snapshotLocation, credentialStore)
err = internalVolume.UpdateVolumeSnapshotLocationWithCredentialConfig(snapshotLocation, credentialStore)
if err != nil {
return nil, errors.WithStack(err)
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"github.com/vmware-tanzu/velero/internal/resourcemodifiers"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
"github.com/vmware-tanzu/velero/pkg/volume"
@ -62,7 +63,7 @@ type Request struct {
ResourceModifiers *resourcemodifiers.ResourceModifiers
DisableInformerCache bool
CSIVolumeSnapshots []*snapshotv1api.VolumeSnapshot
VolumeInfoMap map[string]volume.VolumeInfo
VolumeInfoMap map[string]internalVolume.VolumeInfo
}
type restoredItemStatus struct {

View File

@ -53,6 +53,7 @@ import (
"github.com/vmware-tanzu/velero/internal/credentials"
"github.com/vmware-tanzu/velero/internal/hook"
"github.com/vmware-tanzu/velero/internal/resourcemodifiers"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/archive"
"github.com/vmware-tanzu/velero/pkg/client"
@ -389,7 +390,7 @@ type restoreContext struct {
disableInformerCache bool
featureVerifier features.Verifier
hookTracker *hook.HookTracker
volumeInfoMap map[string]volume.VolumeInfo
volumeInfoMap map[string]internalVolume.VolumeInfo
}
type resourceClientKey struct {
@ -1248,11 +1249,11 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
ctx.log.Infof("Find VolumeInfo for PV %s.", obj.GetName())
switch volumeInfo.BackupMethod {
case volume.NativeSnapshot:
case internalVolume.NativeSnapshot:
pvCondition = pvHasNativeSnapshot
case volume.PodVolumeBackup:
case internalVolume.PodVolumeBackup:
pvCondition = pvHasPodVolumeBackup
case volume.CSISnapshot:
case internalVolume.CSISnapshot:
if volumeInfo.SnapshotDataMoved {
pvCondition = pvHasDataUpload
} else {

View File

@ -42,6 +42,7 @@ import (
"k8s.io/client-go/dynamic"
kubetesting "k8s.io/client-go/testing"
internalVolume "github.com/vmware-tanzu/velero/internal/volume"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/archive"
"github.com/vmware-tanzu/velero/pkg/builder"
@ -70,7 +71,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources []*test.APIResource
tarball io.Reader
want map[*test.APIResource][]string
volumeInfoMap map[string]volume.VolumeInfo
volumeInfoMap map[string]internalVolume.VolumeInfo
}{
{
name: "Restore PV with native snapshot",
@ -83,11 +84,11 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
BackupMethod: volume.NativeSnapshot,
BackupMethod: internalVolume.NativeSnapshot,
PVName: "pv-1",
NativeSnapshotInfo: volume.NativeSnapshotInfo{
NativeSnapshotInfo: internalVolume.NativeSnapshotInfo{
SnapshotHandle: "testSnapshotHandle",
},
},
@ -107,11 +108,11 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
BackupMethod: volume.PodVolumeBackup,
BackupMethod: internalVolume.PodVolumeBackup,
PVName: "pv-1",
PVBInfo: volume.PodVolumeBackupInfo{
PVBInfo: internalVolume.PodVolumeBackupInfo{
SnapshotHandle: "testSnapshotHandle",
Size: 100,
NodeName: "testNode",
@ -133,12 +134,12 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
BackupMethod: volume.CSISnapshot,
BackupMethod: internalVolume.CSISnapshot,
SnapshotDataMoved: false,
PVName: "pv-1",
CSISnapshotInfo: volume.CSISnapshotInfo{
CSISnapshotInfo: internalVolume.CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
},
},
@ -158,15 +159,15 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
BackupMethod: volume.CSISnapshot,
BackupMethod: internalVolume.CSISnapshot,
SnapshotDataMoved: true,
PVName: "pv-1",
CSISnapshotInfo: volume.CSISnapshotInfo{
CSISnapshotInfo: internalVolume.CSISnapshotInfo{
Driver: "pd.csi.storage.gke.io",
},
SnapshotDataMovementInfo: volume.SnapshotDataMovementInfo{
SnapshotDataMovementInfo: internalVolume.SnapshotDataMovementInfo{
DataMover: "velero",
},
},
@ -186,7 +187,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
PVName: "pv-1",
Skipped: true,
@ -207,7 +208,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
apiResources: []*test.APIResource{
test.PVs(),
},
volumeInfoMap: map[string]volume.VolumeInfo{
volumeInfoMap: map[string]internalVolume.VolumeInfo{
"pv-1": {
PVName: "pv-1",
Skipped: true,

View File

@ -26,7 +26,6 @@ import (
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
cmdtest "github.com/vmware-tanzu/velero/pkg/cmd/test"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
)
@ -35,7 +34,7 @@ func TestInternalLW(t *testing.T) {
client := velerotest.NewFakeControllerRuntimeClient(t).(kbclient.WithWatch)
lw := InternalLW{
Client: client,
Namespace: cmdtest.VeleroNameSpace,
Namespace: "velero",
ObjectList: new(velerov1api.BackupList),
}

View File

@ -1,156 +0,0 @@
/*
Copyright 2018 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 volume
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
type VolumeBackupMethod string
const (
NativeSnapshot VolumeBackupMethod = "NativeSnapshot"
PodVolumeBackup VolumeBackupMethod = "PodVolumeBackup"
CSISnapshot VolumeBackupMethod = "CSISnapshot"
)
type VolumeInfos struct {
VolumeInfos []VolumeInfo `json:"volumeInfos"`
}
type VolumeInfo struct {
// The PVC's name.
PVCName string `json:"pvcName,omitempty"`
// The PVC's namespace
PVCNamespace string `json:"pvcNamespace,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 VolumeBackupMethod `json:"backupMethod,omitempty"`
// Whether the volume's snapshot data is moved to specified storage.
SnapshotDataMoved bool `json:"snapshotDataMoved"`
// Whether the local snapshot is preserved after snapshot is moved.
// The local snapshot may be a result of CSI snapshot backup(no data movement)
// or a CSI snapshot data movement plus preserve local snapshot.
PreserveLocalSnapshot bool `json:"preserveLocalSnapshot"`
// Whether the Volume is skipped in this backup.
Skipped bool `json:"skipped"`
// The reason for the volume is skipped in the backup.
SkippedReason string `json:"skippedReason,omitempty"`
// Snapshot starts timestamp.
StartTimestamp *metav1.Time `json:"startTimestamp,omitempty"`
// The Async Operation's ID.
OperationID string `json:"operationID,omitempty"`
CSISnapshotInfo CSISnapshotInfo `json:"csiSnapshotInfo,omitempty"`
SnapshotDataMovementInfo SnapshotDataMovementInfo `json:"snapshotDataMovementInfo,omitempty"`
NativeSnapshotInfo NativeSnapshotInfo `json:"nativeSnapshotInfo,omitempty"`
PVBInfo PodVolumeBackupInfo `json:"pvbInfo,omitempty"`
PVInfo PVInfo `json:"pvInfo,omitempty"`
}
// CSISnapshotInfo is used for displaying the CSI snapshot status
type CSISnapshotInfo struct {
// It's the storage provider's snapshot ID for CSI.
SnapshotHandle string `json:"snapshotHandle"`
// The snapshot corresponding volume size.
Size int64 `json:"size"`
// The name of the CSI driver.
Driver string `json:"driver"`
// The name of the VolumeSnapshotContent.
VSCName string `json:"vscName"`
}
// SnapshotDataMovementInfo is used for displaying the snapshot data mover status.
type SnapshotDataMovementInfo 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`.
UploaderType string `json:"uploaderType"`
// 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 movement.
RetainedSnapshot string `json:"retainedSnapshot"`
// It's the filesystem repository's snapshot ID.
SnapshotHandle string `json:"snapshotHandle"`
}
// NativeSnapshotInfo is used for displaying the Velero native snapshot status.
// A Velero Native Snapshot is a cloud storage snapshot taken by the Velero native
// plugins, e.g. velero-plugin-for-aws, velero-plugin-for-gcp, and
// velero-plugin-for-microsoft-azure.
type NativeSnapshotInfo struct {
// It's the storage provider's snapshot ID for the Velero-native snapshot.
SnapshotHandle string `json:"snapshotHandle"`
// 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 {
// It's the file-system uploader's snapshot ID for PodVolumeBackup.
SnapshotHandle string `json:"snapshotHandle"`
// The snapshot corresponding volume size.
Size int64 `json:"size"`
// The type of the uploader that uploads the data. The valid values are `kopia` and `restic`.
UploaderType string `json:"uploaderType"`
// The PVC's corresponding volume name used by Pod
// https://github.com/kubernetes/kubernetes/blob/e4b74dd12fa8cb63c174091d5536a10b8ec19d34/pkg/apis/core/types.go#L48
VolumeName string `json:"volumeName"`
// The Pod name mounting this PVC.
PodName string `json:"podName"`
// The Pod namespace
PodNamespace string `json:"podNamespace"`
// The PVB-taken k8s node's name.
NodeName string `json:"nodeName"`
}
// PVInfo is used to store some PV information modified after creation.
// Those information are lost after PV recreation.
type PVInfo struct {
// ReclaimPolicy of PV. It could be different from the referenced StorageClass.
ReclaimPolicy string `json:"reclaimPolicy"`
// The PV's labels should be kept after recreation.
Labels map[string]string `json:"labels"`
}