Use path struct between components during restore (#842)

* Parse path structs from backup details for restore

* Fixup kopia tests for new restore interface
This commit is contained in:
ashmrtn 2022-09-14 12:11:58 -07:00 committed by GitHub
parent 5ae8259fd5
commit f2b287498b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 40 deletions

View File

@ -2,7 +2,6 @@ package kopia
import ( import (
"context" "context"
stdpath "path"
"sync" "sync"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
@ -28,7 +27,10 @@ const (
corsoUser = "corso" corsoUser = "corso"
) )
var errNotConnected = errors.New("not connected to repo") var (
errNotConnected = errors.New("not connected to repo")
errNoRestorePath = errors.New("no restore path given")
)
type BackupStats struct { type BackupStats struct {
SnapshotID string SnapshotID string
@ -428,7 +430,7 @@ func (w Wrapper) getEntry(
itemPath path.Path, itemPath path.Path,
) (fs.Entry, error) { ) (fs.Entry, error) {
if itemPath == nil { if itemPath == nil {
return nil, errors.New("no restore path given") return nil, errors.WithStack(errNoRestorePath)
} }
man, err := snapshot.LoadSnapshot(ctx, w.c, manifest.ID(snapshotID)) man, err := snapshot.LoadSnapshot(ctx, w.c, manifest.ID(snapshotID))
@ -463,21 +465,18 @@ func (w Wrapper) getEntry(
func (w Wrapper) collectItems( func (w Wrapper) collectItems(
ctx context.Context, ctx context.Context,
snapshotID string, snapshotID string,
itemPath []string, itemPath path.Path,
) ([]data.Collection, error) { ) ([]data.Collection, error) {
// TODO(ashmrtn): Remove this extra parsing once selectors pass path.Path to if itemPath == nil {
// this function. return nil, errors.WithStack(errNoRestorePath)
pth, err := path.FromDataLayerPath(stdpath.Join(itemPath...), true)
if err != nil {
return nil, errors.Wrap(err, "parsing to path struct")
} }
parentDir, err := pth.Dir() parentDir, err := itemPath.Dir()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "getting parent directory from path") return nil, errors.Wrap(err, "getting parent directory from path")
} }
e, err := w.getEntry(ctx, snapshotID, pth) e, err := w.getEntry(ctx, snapshotID, itemPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -505,7 +504,7 @@ func (w Wrapper) collectItems(
func (w Wrapper) RestoreSingleItem( func (w Wrapper) RestoreSingleItem(
ctx context.Context, ctx context.Context,
snapshotID string, snapshotID string,
itemPath []string, itemPath path.Path,
) (data.Collection, error) { ) (data.Collection, error) {
c, err := w.collectItems(ctx, snapshotID, itemPath) c, err := w.collectItems(ctx, snapshotID, itemPath)
if err != nil { if err != nil {
@ -553,8 +552,12 @@ func restoreSingleItem(
func (w Wrapper) RestoreMultipleItems( func (w Wrapper) RestoreMultipleItems(
ctx context.Context, ctx context.Context,
snapshotID string, snapshotID string,
paths [][]string, paths []path.Path,
) ([]data.Collection, error) { ) ([]data.Collection, error) {
if len(paths) == 0 {
return nil, errors.WithStack(errNoRestorePath)
}
var ( var (
dcs = []data.Collection{} dcs = []data.Collection{}
errs *multierror.Error errs *multierror.Error

View File

@ -617,11 +617,9 @@ func (suite *KopiaIntegrationSuite) TestRestoreAfterCompressionChange() {
result, err := w.RestoreMultipleItems( result, err := w.RestoreMultipleItems(
ctx, ctx,
string(stats.SnapshotID), string(stats.SnapshotID),
[][]string{ []path.Path{
// TODO(ashmrtn): Remove when selectors passes paths to wrapper for fp1,
// restore. fp2,
fp1.Elements(),
fp2.Elements(),
}) })
require.NoError(t, err) require.NoError(t, err)
@ -815,10 +813,13 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TearDownTest() {
func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupAndRestoreSingleItem() { func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupAndRestoreSingleItem() {
t := suite.T() t := suite.T()
itemPath, err := suite.testPath1.Append(testFileName, true)
require.NoError(t, err)
c, err := suite.w.RestoreSingleItem( c, err := suite.w.RestoreSingleItem(
suite.ctx, suite.ctx,
string(suite.snapshotID), string(suite.snapshotID),
append(testPath, testFileName), itemPath,
) )
require.NoError(t, err) require.NoError(t, err)
@ -840,30 +841,41 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupAndRestoreSingleItem() {
// TestBackupAndRestoreSingleItem_Errors exercises the public RestoreSingleItem // TestBackupAndRestoreSingleItem_Errors exercises the public RestoreSingleItem
// function. // function.
func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupAndRestoreSingleItem_Errors() { func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupAndRestoreSingleItem_Errors() {
itemPath, err := suite.testPath1.Append(testFileName, true)
require.NoError(suite.T(), err)
doesntExist, err := path.Builder{}.Append("subdir", "foo").ToDataLayerExchangePathForCategory(
testTenant,
testUser,
path.EmailCategory,
true,
)
require.NoError(suite.T(), err)
table := []struct { table := []struct {
name string name string
snapshotID string snapshotID string
path []string path path.Path
}{ }{
{ {
"EmptyPath", "EmptyPath",
string(suite.snapshotID), string(suite.snapshotID),
[]string{}, nil,
}, },
{ {
"NoSnapshot", "NoSnapshot",
"foo", "foo",
append(testPath, testFileName), itemPath,
}, },
{ {
"TargetNotAFile", "TargetNotAFile",
string(suite.snapshotID), string(suite.snapshotID),
testPath[:2], suite.testPath1,
}, },
{ {
"NonExistentFile", "NonExistentFile",
string(suite.snapshotID), string(suite.snapshotID),
append(testPath, "subdir", "foo"), doesntExist,
}, },
} }
@ -908,10 +920,9 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems() {
result, err := w.RestoreMultipleItems( result, err := w.RestoreMultipleItems(
ctx, ctx,
string(stats.SnapshotID), string(stats.SnapshotID),
[][]string{ []path.Path{
// TODO(ashmrtn): Remove when selectors pass path to kopia restore. fp1,
fp1.Elements(), fp2,
fp2.Elements(),
}) })
require.NoError(t, err) require.NoError(t, err)
@ -921,30 +932,46 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems() {
} }
func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems_Errors() { func (suite *KopiaSimpleRepoIntegrationSuite) TestRestoreMultipleItems_Errors() {
itemPath, err := suite.testPath1.Append(testFileName, true)
require.NoError(suite.T(), err)
doesntExist, err := path.Builder{}.Append("subdir", "foo").ToDataLayerExchangePathForCategory(
testTenant,
testUser,
path.EmailCategory,
true,
)
require.NoError(suite.T(), err)
table := []struct { table := []struct {
name string name string
snapshotID string snapshotID string
paths [][]string paths []path.Path
}{ }{
{
"NilPaths",
string(suite.snapshotID),
nil,
},
{ {
"EmptyPaths", "EmptyPaths",
string(suite.snapshotID), string(suite.snapshotID),
[][]string{{}}, []path.Path{},
}, },
{ {
"NoSnapshot", "NoSnapshot",
"foo", "foo",
[][]string{append(testPath, testFileName)}, []path.Path{itemPath},
}, },
{ {
"TargetNotAFile", "TargetNotAFile",
string(suite.snapshotID), string(suite.snapshotID),
[][]string{testPath[:2]}, []path.Path{suite.testPath1},
}, },
{ {
"NonExistentFile", "NonExistentFile",
string(suite.snapshotID), string(suite.snapshotID),
[][]string{append(testPath, "subdir", "foo")}, []path.Path{doesntExist},
}, },
} }
@ -966,8 +993,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestDeleteSnapshot() {
assert.NoError(t, suite.w.DeleteSnapshot(suite.ctx, string(suite.snapshotID))) assert.NoError(t, suite.w.DeleteSnapshot(suite.ctx, string(suite.snapshotID)))
// assert the deletion worked // assert the deletion worked
itemPath := append(testPath, testFileName) itemPath, err := suite.testPath1.Append(testFileName, true)
_, err := suite.w.RestoreSingleItem(suite.ctx, string(suite.snapshotID), itemPath) require.NoError(t, err)
_, err = suite.w.RestoreSingleItem(suite.ctx, string(suite.snapshotID), itemPath)
assert.Error(t, err, "snapshot should be deleted") assert.Error(t, err, "snapshot should be deleted")
} }

View File

@ -2,7 +2,6 @@ package operations
import ( import (
"context" "context"
"strings"
"time" "time"
multierror "github.com/hashicorp/go-multierror" multierror "github.com/hashicorp/go-multierror"
@ -13,6 +12,7 @@ import (
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/kopia"
"github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/model"
"github.com/alcionai/corso/src/internal/path"
"github.com/alcionai/corso/src/internal/stats" "github.com/alcionai/corso/src/internal/stats"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
@ -112,22 +112,36 @@ func (op *RestoreOperation) Run(ctx context.Context) (err error) {
return errors.New("nothing to restore: no items in the backup match the provided selectors") return errors.New("nothing to restore: no items in the backup match the provided selectors")
} }
// todo: use path pkg for this
fdsPaths := fds.Paths() fdsPaths := fds.Paths()
paths := make([][]string, len(fdsPaths)) paths := make([]path.Path, len(fdsPaths))
var parseErrs *multierror.Error
for i := range fdsPaths { for i := range fdsPaths {
paths[i] = strings.Split(fdsPaths[i], "/") p, err := path.FromDataLayerPath(fdsPaths[i], true)
if err != nil {
parseErrs = multierror.Append(
parseErrs,
errors.Wrap(err, "parsing details entry path"),
)
continue
}
paths[i] = p
} }
dcs, err := op.kopia.RestoreMultipleItems(ctx, b.SnapshotID, paths) dcs, err := op.kopia.RestoreMultipleItems(ctx, b.SnapshotID, paths)
if err != nil { if err != nil {
err = errors.Wrap(err, "retrieving service data") err = errors.Wrap(err, "retrieving service data")
opStats.readErr = err
parseErrs = multierror.Append(parseErrs, err)
opStats.readErr = parseErrs.ErrorOrNil()
return err return err
} }
opStats.readErr = parseErrs.ErrorOrNil()
opStats.cs = dcs opStats.cs = dcs
// restore those collections using graph // restore those collections using graph