mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-09 01:26:30 +00:00
block uploader snapshot implementation
Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Add snapshot operations for block uploader
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// implement in following PRs
|
||||
func openBlockDevice(path string, read bool) (*os.File, error) {
|
||||
return nil, errors.New("Not implemented")
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/cbtservice"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
|
||||
"github.com/vmware-tanzu/velero/pkg/uploader"
|
||||
@@ -202,6 +203,9 @@ func Restore(ctx context.Context, blkup Uploader, rep udmrepo.BackupRepo, snapsh
|
||||
|
||||
log.Infof("Restore from snapshot %s, description %s, created time %v, tags %v", snapshotID, snapshot.Description, snapshot.EndTime, snapshot.Tags)
|
||||
|
||||
bitmap := cbt.NewBitmap(blockSize, uint64(snapshot.TotalSize), "", "", "")
|
||||
bitmap.SetFull()
|
||||
|
||||
destPath, err := filepath.Abs(dest)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "invalid dest path '%s'", dest)
|
||||
@@ -214,7 +218,7 @@ func Restore(ctx context.Context, blkup Uploader, rep udmrepo.BackupRepo, snapsh
|
||||
return 0, errors.Wrapf(err, "error opening block device '%s'", destPath)
|
||||
}
|
||||
|
||||
size, err := blkup.Restore(snapshot, destInfo{dev: destDev, path: destPath}, uploaderCfg)
|
||||
size, err := blkup.Restore(snapshot, destInfo{dev: destDev, path: destPath}, bitmap.Iterator(), uploaderCfg)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "error restoring to block dev %s", destPath)
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ func (m *mockUploader) Backup(src sourceInfo, parent udmrepo.ID, iter cbttypes.I
|
||||
return args.Get(0).(udmrepo.Snapshot), args.Get(1).(int64), args.Error(2)
|
||||
}
|
||||
|
||||
func (m *mockUploader) Restore(snap udmrepo.Snapshot, dest destInfo, cfg map[string]string) (int64, error) {
|
||||
args := m.Called(snap, dest, cfg)
|
||||
func (m *mockUploader) Restore(snap udmrepo.Snapshot, dest destInfo, iter cbttypes.Iterator, cfg map[string]string) (int64, error) {
|
||||
args := m.Called(snap, dest, iter, cfg)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
|
||||
@@ -360,6 +360,8 @@ func TestGetParentBackupInfo(t *testing.T) {
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("GetSnapshot", mock.Anything, udmrepo.ID("snap-valid")).
|
||||
Return(validSnap, nil)
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(&udmrepo.Metadata{SubObjects: []udmrepo.ObjectMetadata{{ID: "root-obj"}}}, nil)
|
||||
},
|
||||
expectedParent: "root-obj",
|
||||
expectedCID: "cid-abc",
|
||||
@@ -386,6 +388,8 @@ func TestGetParentBackupInfo(t *testing.T) {
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("ListSnapshot", mock.Anything, realSource).
|
||||
Return([]udmrepo.Snapshot{validSnap}, nil)
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(&udmrepo.Metadata{SubObjects: []udmrepo.ObjectMetadata{{ID: "root-obj"}}}, nil)
|
||||
},
|
||||
expectedParent: "root-obj",
|
||||
expectedCID: "cid-abc",
|
||||
@@ -566,7 +570,7 @@ func TestRestore(t *testing.T) {
|
||||
setupMocks: func(blkup *mockUploader, repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("GetSnapshot", mock.Anything, udmrepo.ID("snap-001")).
|
||||
Return(storedSnap, nil)
|
||||
blkup.On("Restore", mock.Anything, mock.Anything, mock.Anything).
|
||||
blkup.On("Restore", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(int64(0), errors.New("restore I/O error"))
|
||||
},
|
||||
setupOpenDev: func(t *testing.T) *os.File {
|
||||
@@ -579,7 +583,7 @@ func TestRestore(t *testing.T) {
|
||||
setupMocks: func(blkup *mockUploader, repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("GetSnapshot", mock.Anything, udmrepo.ID("snap-001")).
|
||||
Return(storedSnap, nil)
|
||||
blkup.On("Restore", mock.Anything, mock.Anything, mock.Anything).
|
||||
blkup.On("Restore", mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(int64(4096), nil)
|
||||
},
|
||||
setupOpenDev: func(t *testing.T) *os.File {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
|
||||
"github.com/vmware-tanzu/velero/pkg/uploader"
|
||||
cbt "github.com/vmware-tanzu/velero/pkg/uploader/cbt/types"
|
||||
@@ -46,9 +47,10 @@ type destInfo struct {
|
||||
|
||||
type Uploader interface {
|
||||
Backup(sourceInfo, udmrepo.ID, cbt.Iterator, map[string]string) (udmrepo.Snapshot, int64, error)
|
||||
Restore(udmrepo.Snapshot, destInfo, map[string]string) (int64, error)
|
||||
Restore(udmrepo.Snapshot, destInfo, cbt.Iterator, map[string]string) (int64, error)
|
||||
}
|
||||
|
||||
// implement in following PRs
|
||||
func NewUploader(ctx context.Context, repoWriter udmrepo.BackupRepo, progress uploader.ProgressUpdater, log logrus.FieldLogger) Uploader {
|
||||
return nil
|
||||
}
|
||||
@@ -60,11 +62,11 @@ func loadObjectFromSnapshot(ctx context.Context, rep udmrepo.BackupRepo, snapsho
|
||||
|
||||
parentMeta, err := rep.ReadMetadata(ctx, snapshot.RootObject.ID)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error readding snapshot metadata for %s", snapshot.Description)
|
||||
return "", errors.Wrapf(err, "error reading snapshot metadata for %s", snapshot.Description)
|
||||
}
|
||||
|
||||
if len(parentMeta.SubObjects) != 1 {
|
||||
return "", errors.Wrapf(err, "unexpected number of bdev object (%d) for snapshot %s", len(parentMeta.SubObjects), snapshot.Description)
|
||||
return "", errors.Errorf("unexpected number of bdev object (%d) for snapshot %s", len(parentMeta.SubObjects), snapshot.Description)
|
||||
}
|
||||
|
||||
return parentMeta.SubObjects[0].ID, nil
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
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 block
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/repository/udmrepo"
|
||||
udmrepomocks "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/mocks"
|
||||
)
|
||||
|
||||
func TestLoadObjectFromSnapshot(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
snapshot *udmrepo.Snapshot
|
||||
setupMocks func(repo *udmrepomocks.BackupRepo)
|
||||
expectedErrStr string
|
||||
expectedID udmrepo.ID
|
||||
}{
|
||||
{
|
||||
name: "nil snapshot",
|
||||
snapshot: nil,
|
||||
expectedErrStr: "snapshot is empty",
|
||||
},
|
||||
{
|
||||
name: "ReadMetadata error",
|
||||
snapshot: &udmrepo.Snapshot{
|
||||
RootObject: udmrepo.ObjectMetadata{ID: "root-obj"},
|
||||
},
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(nil, errors.New("read error"))
|
||||
},
|
||||
expectedErrStr: "error reading snapshot metadata",
|
||||
},
|
||||
{
|
||||
name: "unexpected number of subobjects (0)",
|
||||
snapshot: &udmrepo.Snapshot{
|
||||
RootObject: udmrepo.ObjectMetadata{ID: "root-obj"},
|
||||
},
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(&udmrepo.Metadata{SubObjects: []udmrepo.ObjectMetadata{}}, nil)
|
||||
},
|
||||
expectedErrStr: "unexpected number of bdev object",
|
||||
},
|
||||
{
|
||||
name: "unexpected number of subobjects (2)",
|
||||
snapshot: &udmrepo.Snapshot{
|
||||
RootObject: udmrepo.ObjectMetadata{ID: "root-obj"},
|
||||
},
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(&udmrepo.Metadata{SubObjects: []udmrepo.ObjectMetadata{{ID: "obj-1"}, {ID: "obj-2"}}}, nil)
|
||||
},
|
||||
expectedErrStr: "unexpected number of bdev object",
|
||||
},
|
||||
{
|
||||
name: "success",
|
||||
snapshot: &udmrepo.Snapshot{
|
||||
RootObject: udmrepo.ObjectMetadata{ID: "root-obj"},
|
||||
},
|
||||
setupMocks: func(repo *udmrepomocks.BackupRepo) {
|
||||
repo.On("ReadMetadata", mock.Anything, udmrepo.ID("root-obj")).
|
||||
Return(&udmrepo.Metadata{SubObjects: []udmrepo.ObjectMetadata{{ID: "bdev-obj"}}}, nil)
|
||||
},
|
||||
expectedID: "bdev-obj",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
mockRepo := udmrepomocks.NewBackupRepo(t)
|
||||
|
||||
if tc.setupMocks != nil {
|
||||
tc.setupMocks(mockRepo)
|
||||
}
|
||||
|
||||
id, err := loadObjectFromSnapshot(ctx, mockRepo, tc.snapshot)
|
||||
|
||||
if tc.expectedErrStr != "" {
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, tc.expectedErrStr)
|
||||
assert.Empty(t, id)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedID, id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user