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:
parent
5ae8259fd5
commit
f2b287498b
@ -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
|
||||||
|
|||||||
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user