mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-08 06:15:40 +00:00
Define itemoperations.json format and update DownloadRequest API.
This is to support uploading async operation metadata to object storage to support progress monitoring. Signed-off-by: Scott Seago <sseago@redhat.com>
This commit is contained in:
1
changelogs/unreleased/5752-sseago
Normal file
1
changelogs/unreleased/5752-sseago
Normal file
@@ -0,0 +1 @@
|
||||
Define itemoperations.json format and update DownloadRequest API
|
||||
@@ -46,10 +46,11 @@ spec:
|
||||
- BackupLog
|
||||
- BackupContents
|
||||
- BackupVolumeSnapshots
|
||||
- BackupItemSnapshots
|
||||
- BackupItemOperations
|
||||
- BackupResourceList
|
||||
- RestoreLog
|
||||
- RestoreResults
|
||||
- RestoreItemOperations
|
||||
- CSIBackupVolumeSnapshots
|
||||
- CSIBackupVolumeSnapshotContents
|
||||
type: string
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -46,8 +46,4 @@ const (
|
||||
|
||||
// APIGroupVersionsFeatureFlag is the feature flag string that defines whether or not to handle multiple API Group Versions
|
||||
APIGroupVersionsFeatureFlag = "EnableAPIGroupVersions"
|
||||
|
||||
// UploadProgressFeatureFlag is the feature flag string that defines whether or not upload progress monitoring is enabled
|
||||
// and whether or not ItemSnapshotters should be invoked
|
||||
UploadProgressFeatureFlag = "EnableUploadProgress"
|
||||
)
|
||||
|
||||
@@ -25,17 +25,18 @@ type DownloadRequestSpec struct {
|
||||
}
|
||||
|
||||
// DownloadTargetKind represents what type of file to download.
|
||||
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemSnapshots;BackupResourceList;RestoreLog;RestoreResults;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents
|
||||
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemOperations;BackupResourceList;RestoreLog;RestoreResults;RestoreItemOperations;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents
|
||||
type DownloadTargetKind string
|
||||
|
||||
const (
|
||||
DownloadTargetKindBackupLog DownloadTargetKind = "BackupLog"
|
||||
DownloadTargetKindBackupContents DownloadTargetKind = "BackupContents"
|
||||
DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots"
|
||||
DownloadTargetKindBackupItemSnapshots DownloadTargetKind = "BackupItemSnapshots"
|
||||
DownloadTargetKindBackupItemOperations DownloadTargetKind = "BackupItemOperations"
|
||||
DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList"
|
||||
DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog"
|
||||
DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults"
|
||||
DownloadTargetKindRestoreItemOperations DownloadTargetKind = "RestoreItemOperations"
|
||||
DownloadTargetKindCSIBackupVolumeSnapshots DownloadTargetKind = "CSIBackupVolumeSnapshots"
|
||||
DownloadTargetKindCSIBackupVolumeSnapshotContents DownloadTargetKind = "CSIBackupVolumeSnapshotContents"
|
||||
)
|
||||
|
||||
44
pkg/itemoperation/backup_operation.go
Normal file
44
pkg/itemoperation/backup_operation.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 itemoperation
|
||||
|
||||
// BackupOperation stores information about an async item operation
|
||||
// started by a BackupItemAction plugin (v2 or later)
|
||||
type BackupOperation struct {
|
||||
Spec BackupOperationSpec `json:"spec"`
|
||||
|
||||
Status OperationStatus `json:"status"`
|
||||
}
|
||||
|
||||
type BackupOperationSpec struct {
|
||||
// BackupName is the name of the Velero backup this item operation
|
||||
// is associated with.
|
||||
BackupName string `json:"backupName"`
|
||||
|
||||
// BackupUID is the UID of the Velero backup this item operation
|
||||
// is associated with.
|
||||
BackupUID string `json:"backupUID"`
|
||||
|
||||
// BackupItemAction is the name of the BackupItemAction plugin that started the operation
|
||||
BackupItemAction string `json:"backupItemAction"`
|
||||
|
||||
// Kubernetes resource identifier for the item
|
||||
ResourceIdentifier string "json:resourceIdentifier"
|
||||
|
||||
// OperationID returned by the BIA plugin
|
||||
OperationID string "json:operationID"
|
||||
}
|
||||
44
pkg/itemoperation/restore_operation.go
Normal file
44
pkg/itemoperation/restore_operation.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 itemoperation
|
||||
|
||||
// RestoreOperation stores information about an async item operation
|
||||
// started by a RestoreItemAction plugin (v2 or later)
|
||||
type RestoreOperation struct {
|
||||
Spec RestoreOperationSpec `json:"spec"`
|
||||
|
||||
Status OperationStatus `json:"status"`
|
||||
}
|
||||
|
||||
type RestoreOperationSpec struct {
|
||||
// RestoreName is the name of the Velero restore this item operation
|
||||
// is associated with.
|
||||
RestoreName string `json:"restoreName"`
|
||||
|
||||
// RestoreUID is the UID of the Velero restore this item operation
|
||||
// is associated with.
|
||||
RestoreUID string `json:"restoreUID"`
|
||||
|
||||
// RestoreItemAction is the name of the RestoreItemAction plugin that started the operation
|
||||
RestoreItemAction string `json:"restoreItemAction"`
|
||||
|
||||
// Kubernetes resource identifier for the item
|
||||
ResourceIdentifier string "json:resourceIdentifier"
|
||||
|
||||
// OperationID returned by the RIA plugin
|
||||
OperationID string "json:operationID"
|
||||
}
|
||||
67
pkg/itemoperation/shared.go
Normal file
67
pkg/itemoperation/shared.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
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 itemoperation
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// OperationPhase is the lifecycle phase of a Velero item operation
|
||||
type OperationPhase string
|
||||
|
||||
type OperationStatus struct {
|
||||
// Phase is the current state of the item operation.
|
||||
Phase OperationPhase `json:"phase,omitempty"`
|
||||
|
||||
// Error displays the reason for a failed operation
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// Amount of operation completed (measured in OperationUnits)
|
||||
// i.e. number of bytes transferred for a volume
|
||||
NCompleted int64 `json:"nCompleted,omitempty"`
|
||||
|
||||
// Total Amount of operation (measured in OperationUnits)
|
||||
// i.e. volume size in bytes
|
||||
NTotal int64 `json:"nTotal,omitempty"`
|
||||
|
||||
// Units that NCompleted,NTotal are measured in
|
||||
// i.e. "bytes"
|
||||
OperationUnits int64 `json:"nTotal,omitempty"`
|
||||
|
||||
// Started records the time the item operation was started, if known
|
||||
// +optional
|
||||
// +nullable
|
||||
Started *metav1.Time `json:"start,omitempty"`
|
||||
|
||||
// Updated records the time the item operation was updated, if known.
|
||||
// +optional
|
||||
// +nullable
|
||||
Updated *metav1.Time `json:"updated,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// OperationPhaseNew means the item operation has been created and started
|
||||
// by the plugin
|
||||
OperationPhaseInProgress OperationPhase = "New"
|
||||
|
||||
// OperationPhaseCompleted means the item operation was successfully completed
|
||||
// and can be used for restore.
|
||||
OperationPhaseCompleted OperationPhase = "Completed"
|
||||
|
||||
// OperationPhaseFailed means the item operation ended with an error.
|
||||
OperationPhaseFailed OperationPhase = "Failed"
|
||||
)
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
||||
persistence "github.com/vmware-tanzu/velero/pkg/persistence"
|
||||
volume "github.com/vmware-tanzu/velero/pkg/volume"
|
||||
)
|
||||
@@ -274,6 +275,34 @@ func (_m *BackupStore) PutRestoreResults(backup string, restore string, results
|
||||
return r0
|
||||
}
|
||||
|
||||
// PutRestoreItemOperations provides a mock function with given fields: backup, restore, restoreItemOperations
|
||||
func (_m *BackupStore) PutRestoreItemOperations(backup string, restore string, restoreItemOperations io.Reader) error {
|
||||
ret := _m.Called(backup, restore, restoreItemOperations)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, io.Reader) error); ok {
|
||||
r0 = rf(backup, restore, restoreItemOperations)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PutBackupItemOperations provides a mock function with given fields: backup, backupItemOperations
|
||||
func (_m *BackupStore) PutBackupItemOperations(backup string, backupItemOperations io.Reader) error {
|
||||
ret := _m.Called(backup, backupItemOperations)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, io.Reader) error); ok {
|
||||
r0 = rf(backup, backupItemOperations)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
func (_m *BackupStore) GetCSIVolumeSnapshots(backup string) ([]*snapshotv1api.VolumeSnapshot, error) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
@@ -289,6 +318,12 @@ func (_m *BackupStore) GetCSIVolumeSnapshotClasses(backup string) ([]*snapshotv1
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_m *BackupStore) GetItemSnapshots(name string) ([]*volume.ItemSnapshot, error) {
|
||||
func (_m *BackupStore) GetBackupItemOperations(name string) ([]*itemoperation.BackupOperation, error) {
|
||||
panic("implement me")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_m *BackupStore) GetRestoreItemOperations(name string) ([]*itemoperation.RestoreOperation, error) {
|
||||
panic("implement me")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
|
||||
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
)
|
||||
@@ -44,7 +45,7 @@ type BackupInfo struct {
|
||||
Log,
|
||||
PodVolumeBackups,
|
||||
VolumeSnapshots,
|
||||
ItemSnapshots,
|
||||
BackupItemOperations,
|
||||
BackupResourceList,
|
||||
CSIVolumeSnapshots,
|
||||
CSIVolumeSnapshotContents,
|
||||
@@ -59,8 +60,9 @@ type BackupStore interface {
|
||||
ListBackups() ([]string, error)
|
||||
|
||||
PutBackup(info BackupInfo) error
|
||||
PutBackupItemOperations(backup string, backupItemOperations io.Reader) error
|
||||
GetBackupMetadata(name string) (*velerov1api.Backup, error)
|
||||
GetItemSnapshots(name string) ([]*volume.ItemSnapshot, error)
|
||||
GetBackupItemOperations(name string) ([]*itemoperation.BackupOperation, error)
|
||||
GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error)
|
||||
GetPodVolumeBackups(name string) ([]*velerov1api.PodVolumeBackup, error)
|
||||
GetBackupContents(name string) (io.ReadCloser, error)
|
||||
@@ -75,6 +77,8 @@ type BackupStore interface {
|
||||
|
||||
PutRestoreLog(backup, restore string, log io.Reader) error
|
||||
PutRestoreResults(backup, restore string, results io.Reader) error
|
||||
PutRestoreItemOperations(backup, restore string, restoreItemOperations io.Reader) error
|
||||
GetRestoreItemOperations(name string) ([]*itemoperation.RestoreOperation, error)
|
||||
DeleteRestore(name string) error
|
||||
|
||||
GetDownloadURL(target velerov1api.DownloadTarget) (string, error)
|
||||
@@ -256,7 +260,7 @@ func (s *objectBackupStore) PutBackup(info BackupInfo) error {
|
||||
var backupObjs = map[string]io.Reader{
|
||||
s.layout.getPodVolumeBackupsKey(info.Name): info.PodVolumeBackups,
|
||||
s.layout.getBackupVolumeSnapshotsKey(info.Name): info.VolumeSnapshots,
|
||||
s.layout.getItemSnapshotsKey(info.Name): info.ItemSnapshots,
|
||||
s.layout.getBackupItemOperationsKey(info.Name): info.BackupItemOperations,
|
||||
s.layout.getBackupResourceListKey(info.Name): info.BackupResourceList,
|
||||
s.layout.getCSIVolumeSnapshotKey(info.Name): info.CSIVolumeSnapshots,
|
||||
s.layout.getCSIVolumeSnapshotContentsKey(info.Name): info.CSIVolumeSnapshotContents,
|
||||
@@ -329,11 +333,11 @@ func (s *objectBackupStore) GetBackupVolumeSnapshots(name string) ([]*volume.Sna
|
||||
return volumeSnapshots, nil
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) GetItemSnapshots(name string) ([]*volume.ItemSnapshot, error) {
|
||||
// if the itemsnapshots file doesn't exist, we don't want to return an error, since
|
||||
// a legacy backup or a backup with no snapshots would not have this file, so check for
|
||||
func (s *objectBackupStore) GetBackupItemOperations(name string) ([]*itemoperation.BackupOperation, error) {
|
||||
// if the itemoperations file doesn't exist, we don't want to return an error, since
|
||||
// a legacy backup or a backup with no async operations would not have this file, so check for
|
||||
// its existence before attempting to get its contents.
|
||||
res, err := tryGet(s.objectStore, s.bucket, s.layout.getItemSnapshotsKey(name))
|
||||
res, err := tryGet(s.objectStore, s.bucket, s.layout.getBackupItemOperationsKey(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -342,12 +346,33 @@ func (s *objectBackupStore) GetItemSnapshots(name string) ([]*volume.ItemSnapsho
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
var itemSnapshots []*volume.ItemSnapshot
|
||||
if err := decode(res, &itemSnapshots); err != nil {
|
||||
var backupItemOperations []*itemoperation.BackupOperation
|
||||
if err := decode(res, &backupItemOperations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return itemSnapshots, nil
|
||||
return backupItemOperations, nil
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) GetRestoreItemOperations(name string) ([]*itemoperation.RestoreOperation, error) {
|
||||
// if the itemoperations file doesn't exist, we don't want to return an error, since
|
||||
// a legacy restore or a restore with no async operations would not have this file, so check for
|
||||
// its existence before attempting to get its contents.
|
||||
res, err := tryGet(s.objectStore, s.bucket, s.layout.getRestoreItemOperationsKey(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
defer res.Close()
|
||||
|
||||
var restoreItemOperations []*itemoperation.RestoreOperation
|
||||
if err := decode(res, &restoreItemOperations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return restoreItemOperations, nil
|
||||
}
|
||||
|
||||
// tryGet returns the object with the given key if it exists, nil if it does not exist,
|
||||
@@ -510,6 +535,14 @@ func (s *objectBackupStore) PutRestoreResults(backup string, restore string, res
|
||||
return s.objectStore.PutObject(s.bucket, s.layout.getRestoreResultsKey(restore), results)
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) PutRestoreItemOperations(backup string, restore string, restoreItemOperations io.Reader) error {
|
||||
return seekAndPutObject(s.objectStore, s.bucket, s.layout.getRestoreItemOperationsKey(restore), restoreItemOperations)
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) PutBackupItemOperations(backup string, backupItemOperations io.Reader) error {
|
||||
return seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupItemOperationsKey(backup), backupItemOperations)
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) GetDownloadURL(target velerov1api.DownloadTarget) (string, error) {
|
||||
switch target.Kind {
|
||||
case velerov1api.DownloadTargetKindBackupContents:
|
||||
@@ -518,8 +551,10 @@ func (s *objectBackupStore) GetDownloadURL(target velerov1api.DownloadTarget) (s
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupLogKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindBackupVolumeSnapshots:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupVolumeSnapshotsKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindBackupItemSnapshots:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getItemSnapshotsKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindBackupItemOperations:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupItemOperationsKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindRestoreItemOperations:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getRestoreItemOperationsKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindBackupResourceList:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupResourceListKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindRestoreLog:
|
||||
|
||||
@@ -89,8 +89,8 @@ func (l *ObjectStoreLayout) getBackupVolumeSnapshotsKey(backup string) string {
|
||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-volumesnapshots.json.gz", backup))
|
||||
}
|
||||
|
||||
func (l *ObjectStoreLayout) getItemSnapshotsKey(backup string) string {
|
||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-itemsnapshots.json.gz", backup))
|
||||
func (l *ObjectStoreLayout) getBackupItemOperationsKey(backup string) string {
|
||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-itemoperations.json.gz", backup))
|
||||
}
|
||||
|
||||
func (l *ObjectStoreLayout) getBackupResourceListKey(backup string) string {
|
||||
@@ -105,6 +105,10 @@ func (l *ObjectStoreLayout) getRestoreResultsKey(restore string) string {
|
||||
return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-results.gz", restore))
|
||||
}
|
||||
|
||||
func (l *ObjectStoreLayout) getRestoreItemOperationsKey(restore string) string {
|
||||
return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-itemoperations.json.gz", restore))
|
||||
}
|
||||
|
||||
func (l *ObjectStoreLayout) getCSIVolumeSnapshotKey(backup string) string {
|
||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-csi-volumesnapshots.json.gz", backup))
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
providermocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
@@ -216,98 +217,98 @@ func TestListBackups(t *testing.T) {
|
||||
|
||||
func TestPutBackup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prefix string
|
||||
metadata io.Reader
|
||||
contents io.Reader
|
||||
log io.Reader
|
||||
podVolumeBackup io.Reader
|
||||
snapshots io.Reader
|
||||
itemSnapshots io.Reader
|
||||
resourceList io.Reader
|
||||
expectedErr string
|
||||
expectedKeys []string
|
||||
name string
|
||||
prefix string
|
||||
metadata io.Reader
|
||||
contents io.Reader
|
||||
log io.Reader
|
||||
podVolumeBackup io.Reader
|
||||
snapshots io.Reader
|
||||
backupItemOperations io.Reader
|
||||
resourceList io.Reader
|
||||
expectedErr string
|
||||
expectedKeys []string
|
||||
}{
|
||||
{
|
||||
name: "normal case",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
itemSnapshots: newStringReadSeeker("itemSnapshots"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
name: "normal case",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
expectedKeys: []string{
|
||||
"backups/backup-1/velero-backup.json",
|
||||
"backups/backup-1/backup-1.tar.gz",
|
||||
"backups/backup-1/backup-1-logs.gz",
|
||||
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
||||
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||
"backups/backup-1/backup-1-itemsnapshots.json.gz",
|
||||
"backups/backup-1/backup-1-itemoperations.json.gz",
|
||||
"backups/backup-1/backup-1-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "normal case with backup store prefix",
|
||||
prefix: "prefix-1/",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
itemSnapshots: newStringReadSeeker("itemSnapshots"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
name: "normal case with backup store prefix",
|
||||
prefix: "prefix-1/",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
expectedKeys: []string{
|
||||
"prefix-1/backups/backup-1/velero-backup.json",
|
||||
"prefix-1/backups/backup-1/backup-1.tar.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-logs.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-podvolumebackups.json.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-itemsnapshots.json.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-itemoperations.json.gz",
|
||||
"prefix-1/backups/backup-1/backup-1-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error on metadata upload does not upload data",
|
||||
metadata: new(errorReader),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
itemSnapshots: newStringReadSeeker("itemSnapshots"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "error readers return errors",
|
||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||
name: "error on metadata upload does not upload data",
|
||||
metadata: new(errorReader),
|
||||
contents: newStringReadSeeker("contents"),
|
||||
log: newStringReadSeeker("log"),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "error readers return errors",
|
||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||
},
|
||||
{
|
||||
name: "error on data upload deletes metadata",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: new(errorReader),
|
||||
log: newStringReadSeeker("log"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
itemSnapshots: newStringReadSeeker("itemSnapshots"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "error readers return errors",
|
||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||
name: "error on data upload deletes metadata",
|
||||
metadata: newStringReadSeeker("metadata"),
|
||||
contents: new(errorReader),
|
||||
log: newStringReadSeeker("log"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "error readers return errors",
|
||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||
},
|
||||
{
|
||||
name: "error on log upload is ok",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
contents: newStringReadSeeker("bar"),
|
||||
log: new(errorReader),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
itemSnapshots: newStringReadSeeker("itemSnapshots"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
name: "error on log upload is ok",
|
||||
metadata: newStringReadSeeker("foo"),
|
||||
contents: newStringReadSeeker("bar"),
|
||||
log: new(errorReader),
|
||||
podVolumeBackup: newStringReadSeeker("podVolumeBackup"),
|
||||
snapshots: newStringReadSeeker("snapshots"),
|
||||
backupItemOperations: newStringReadSeeker("backupItemOperations"),
|
||||
resourceList: newStringReadSeeker("resourceList"),
|
||||
expectedErr: "",
|
||||
expectedKeys: []string{
|
||||
"backups/backup-1/velero-backup.json",
|
||||
"backups/backup-1/backup-1.tar.gz",
|
||||
"backups/backup-1/backup-1-podvolumebackups.json.gz",
|
||||
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||
"backups/backup-1/backup-1-itemsnapshots.json.gz",
|
||||
"backups/backup-1/backup-1-itemoperations.json.gz",
|
||||
"backups/backup-1/backup-1-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -335,14 +336,14 @@ func TestPutBackup(t *testing.T) {
|
||||
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
||||
|
||||
backupInfo := BackupInfo{
|
||||
Name: "backup-1",
|
||||
Metadata: tc.metadata,
|
||||
Contents: tc.contents,
|
||||
Log: tc.log,
|
||||
PodVolumeBackups: tc.podVolumeBackup,
|
||||
VolumeSnapshots: tc.snapshots,
|
||||
ItemSnapshots: tc.itemSnapshots,
|
||||
BackupResourceList: tc.resourceList,
|
||||
Name: "backup-1",
|
||||
Metadata: tc.metadata,
|
||||
Contents: tc.contents,
|
||||
Log: tc.log,
|
||||
PodVolumeBackups: tc.podVolumeBackup,
|
||||
VolumeSnapshots: tc.snapshots,
|
||||
BackupItemOperations: tc.backupItemOperations,
|
||||
BackupResourceList: tc.resourceList,
|
||||
}
|
||||
err := harness.PutBackup(backupInfo)
|
||||
|
||||
@@ -442,30 +443,30 @@ func TestGetBackupVolumeSnapshots(t *testing.T) {
|
||||
assert.EqualValues(t, snapshots, res)
|
||||
}
|
||||
|
||||
func TestGetItemSnapshots(t *testing.T) {
|
||||
func TestGetBackupItemOperations(t *testing.T) {
|
||||
harness := newObjectBackupStoreTestHarness("test-bucket", "")
|
||||
|
||||
// volumesnapshots file not found should not error
|
||||
// itemoperations file not found should not error
|
||||
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/velero-backup.json", newStringReadSeeker("foo"))
|
||||
res, err := harness.GetItemSnapshots("test-backup")
|
||||
res, err := harness.GetBackupItemOperations("test-backup")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, res)
|
||||
|
||||
// volumesnapshots file containing invalid data should error
|
||||
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-itemsnapshots.json.gz", newStringReadSeeker("foo"))
|
||||
res, err = harness.GetItemSnapshots("test-backup")
|
||||
// itemoperations file containing invalid data should error
|
||||
harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-itemoperations.json.gz", newStringReadSeeker("foo"))
|
||||
res, err = harness.GetBackupItemOperations("test-backup")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// volumesnapshots file containing gzipped json data should return correctly
|
||||
snapshots := []*volume.ItemSnapshot{
|
||||
// itemoperations file containing gzipped json data should return correctly
|
||||
operations := []*itemoperation.BackupOperation{
|
||||
{
|
||||
Spec: volume.ItemSnapshotSpec{
|
||||
Spec: itemoperation.BackupOperationSpec{
|
||||
BackupName: "test-backup",
|
||||
ResourceIdentifier: "item-1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Spec: volume.ItemSnapshotSpec{
|
||||
Spec: itemoperation.BackupOperationSpec{
|
||||
BackupName: "test-backup",
|
||||
ResourceIdentifier: "item-2",
|
||||
},
|
||||
@@ -475,13 +476,13 @@ func TestGetItemSnapshots(t *testing.T) {
|
||||
obj := new(bytes.Buffer)
|
||||
gzw := gzip.NewWriter(obj)
|
||||
|
||||
require.NoError(t, json.NewEncoder(gzw).Encode(snapshots))
|
||||
require.NoError(t, json.NewEncoder(gzw).Encode(operations))
|
||||
require.NoError(t, gzw.Close())
|
||||
require.NoError(t, harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-itemsnapshots.json.gz", obj))
|
||||
require.NoError(t, harness.objectStore.PutObject(harness.bucket, "backups/test-backup/test-backup-itemoperations.json.gz", obj))
|
||||
|
||||
res, err = harness.GetItemSnapshots("test-backup")
|
||||
res, err = harness.GetBackupItemOperations("test-backup")
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, snapshots, res)
|
||||
assert.EqualValues(t, operations, res)
|
||||
}
|
||||
|
||||
func TestGetBackupContents(t *testing.T) {
|
||||
@@ -564,7 +565,7 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
velerov1api.DownloadTargetKindBackupContents: "backups/my-backup/my-backup.tar.gz",
|
||||
velerov1api.DownloadTargetKindBackupLog: "backups/my-backup/my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/my-backup/my-backup-volumesnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemSnapshots: "backups/my-backup/my-backup-itemsnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemOperations: "backups/my-backup/my-backup-itemoperations.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupResourceList: "backups/my-backup/my-backup-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -576,7 +577,7 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
velerov1api.DownloadTargetKindBackupContents: "velero-backups/backups/my-backup/my-backup.tar.gz",
|
||||
velerov1api.DownloadTargetKindBackupLog: "velero-backups/backups/my-backup/my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "velero-backups/backups/my-backup/my-backup-volumesnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemSnapshots: "velero-backups/backups/my-backup/my-backup-itemsnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemOperations: "velero-backups/backups/my-backup/my-backup-itemoperations.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupResourceList: "velero-backups/backups/my-backup/my-backup-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -587,7 +588,7 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
velerov1api.DownloadTargetKindBackupContents: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902.tar.gz",
|
||||
velerov1api.DownloadTargetKindBackupLog: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-logs.gz",
|
||||
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-volumesnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemSnapshots: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-itemsnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemOperations: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-itemoperations.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupResourceList: "backups/b-cool-20170913154901-20170913154902/b-cool-20170913154901-20170913154902-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -598,7 +599,7 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
velerov1api.DownloadTargetKindBackupContents: "backups/my-backup-20170913154901/my-backup-20170913154901.tar.gz",
|
||||
velerov1api.DownloadTargetKindBackupLog: "backups/my-backup-20170913154901/my-backup-20170913154901-logs.gz",
|
||||
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "backups/my-backup-20170913154901/my-backup-20170913154901-volumesnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemSnapshots: "backups/my-backup-20170913154901/my-backup-20170913154901-itemsnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemOperations: "backups/my-backup-20170913154901/my-backup-20170913154901-itemoperations.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupResourceList: "backups/my-backup-20170913154901/my-backup-20170913154901-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -610,7 +611,7 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
velerov1api.DownloadTargetKindBackupContents: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901.tar.gz",
|
||||
velerov1api.DownloadTargetKindBackupLog: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-logs.gz",
|
||||
velerov1api.DownloadTargetKindBackupVolumeSnapshots: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-volumesnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemSnapshots: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-itemsnapshots.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupItemOperations: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-itemoperations.json.gz",
|
||||
velerov1api.DownloadTargetKindBackupResourceList: "velero-backups/backups/my-backup-20170913154901/my-backup-20170913154901-resource-list.json.gz",
|
||||
},
|
||||
},
|
||||
@@ -618,8 +619,9 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
name: "restore",
|
||||
targetName: "my-backup",
|
||||
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
||||
velerov1api.DownloadTargetKindRestoreLog: "restores/my-backup/restore-my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "restores/my-backup/restore-my-backup-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreLog: "restores/my-backup/restore-my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "restores/my-backup/restore-my-backup-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreItemOperations: "restores/my-backup/restore-my-backup-itemoperations.json.gz",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -627,16 +629,18 @@ func TestGetDownloadURL(t *testing.T) {
|
||||
targetName: "my-backup",
|
||||
prefix: "velero-backups/",
|
||||
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
||||
velerov1api.DownloadTargetKindRestoreLog: "velero-backups/restores/my-backup/restore-my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "velero-backups/restores/my-backup/restore-my-backup-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreLog: "velero-backups/restores/my-backup/restore-my-backup-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "velero-backups/restores/my-backup/restore-my-backup-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreItemOperations: "velero-backups/restores/my-backup/restore-my-backup-itemoperations.json.gz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "restore with multiple dashes",
|
||||
targetName: "b-cool-20170913154901-20170913154902",
|
||||
expectedKeyByKind: map[velerov1api.DownloadTargetKind]string{
|
||||
velerov1api.DownloadTargetKindRestoreLog: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreLog: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-logs.gz",
|
||||
velerov1api.DownloadTargetKindRestoreResults: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-results.gz",
|
||||
velerov1api.DownloadTargetKindRestoreItemOperations: "restores/b-cool-20170913154901-20170913154902/restore-b-cool-20170913154901-20170913154902-itemoperations.json.gz",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
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 isv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/item_snapshotter/v1"
|
||||
|
||||
// ItemSnapshot stores information about an item snapshot (includes volumes and other Astrolabe objects) taken as
|
||||
// part of a Velero backup.
|
||||
type ItemSnapshot struct {
|
||||
Spec ItemSnapshotSpec `json:"spec"`
|
||||
|
||||
Status ItemSnapshotStatus `json:"status"`
|
||||
}
|
||||
|
||||
type ItemSnapshotSpec struct {
|
||||
// ItemSnapshotter is the name of the ItemSnapshotter plugin that took the snapshot
|
||||
ItemSnapshotter string `json:"itemSnapshotter"`
|
||||
|
||||
// BackupName is the name of the Velero backup this snapshot
|
||||
// is associated with.
|
||||
BackupName string `json:"backupName"`
|
||||
|
||||
// BackupUID is the UID of the Velero backup this snapshot
|
||||
// is associated with.
|
||||
BackupUID string `json:"backupUID"`
|
||||
|
||||
// Location is the name of the location where this snapshot is stored.
|
||||
Location string `json:"location"`
|
||||
|
||||
// Kubernetes resource identifier for the item
|
||||
ResourceIdentifier string "json:resourceIdentifier"
|
||||
}
|
||||
|
||||
type ItemSnapshotStatus struct {
|
||||
// ProviderSnapshotID is the ID of the snapshot taken by the ItemSnapshotter
|
||||
ProviderSnapshotID string `json:"providerSnapshotID,omitempty"`
|
||||
|
||||
// Metadata is the metadata returned with the snapshot to be returned to the ItemSnapshotter at restore time
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
|
||||
// Phase is the current state of the ItemSnapshot.
|
||||
Phase isv1.SnapshotPhase `json:"phase,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user