look up restore resource if specified
If the restore configuration specifies a protected resource as a restore target, use that as the destination for the restore. First step is to ensure the provided target can be retrieved and identified.
This commit is contained in:
parent
1272066d50
commit
3a02e3269b
@ -144,7 +144,7 @@ func getControllerAndVerifyResourceOwner(
|
||||
return nil, account.Account{}, nil, clues.Wrap(err, "connecting to graph api")
|
||||
}
|
||||
|
||||
id, _, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, resourceOwner, nil)
|
||||
id, _, err := ctrl.PopulateProtectedResourceIDAndName(ctx, resourceOwner, nil)
|
||||
if err != nil {
|
||||
return nil, account.Account{}, nil, clues.Wrap(err, "verifying user")
|
||||
}
|
||||
|
||||
@ -28,6 +28,10 @@ type is struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func NewProvider(id, name string) *is {
|
||||
return &is{id, name}
|
||||
}
|
||||
|
||||
func (is is) ID() string { return is.id }
|
||||
func (is is) Name() string { return is.name }
|
||||
|
||||
|
||||
@ -367,7 +367,7 @@ func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Libraries() {
|
||||
siteIDs = []string{siteID}
|
||||
)
|
||||
|
||||
id, name, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, siteID, nil)
|
||||
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
sel := selectors.NewSharePointBackup(siteIDs)
|
||||
@ -414,7 +414,7 @@ func (suite *SPCollectionIntgSuite) TestCreateSharePointCollection_Lists() {
|
||||
siteIDs = []string{siteID}
|
||||
)
|
||||
|
||||
id, name, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, siteID, nil)
|
||||
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, siteID, nil)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
sel := selectors.NewSharePointBackup(siteIDs)
|
||||
|
||||
@ -248,7 +248,7 @@ func (r resourceClient) getOwnerIDAndNameFrom(
|
||||
return id, name, nil
|
||||
}
|
||||
|
||||
// PopulateOwnerIDAndNamesFrom takes the provided owner identifier and produces
|
||||
// PopulateProtectedResourceIDAndName takes the provided owner identifier and produces
|
||||
// the owner's name and ID from that value. Returns an error if the owner is
|
||||
// not recognized by the current tenant.
|
||||
//
|
||||
@ -256,7 +256,7 @@ func (r resourceClient) getOwnerIDAndNameFrom(
|
||||
// the tenant before reaching this step. In that case, the data gets handed
|
||||
// down for this func to consume instead of performing further queries. The
|
||||
// data gets stored inside the controller instance for later re-use.
|
||||
func (ctrl *Controller) PopulateOwnerIDAndNamesFrom(
|
||||
func (ctrl *Controller) PopulateProtectedResourceIDAndName(
|
||||
ctx context.Context,
|
||||
owner string, // input value, can be either id or name
|
||||
ins idname.Cacher,
|
||||
|
||||
@ -222,7 +222,7 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
|
||||
|
||||
ctrl := &Controller{ownerLookup: test.rc}
|
||||
|
||||
rID, rName, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, test.owner, test.ins)
|
||||
rID, rName, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.owner, test.ins)
|
||||
test.expectErr(t, err, clues.ToCore(err))
|
||||
assert.Equal(t, test.expectID, rID, "id")
|
||||
assert.Equal(t, test.expectName, rName, "name")
|
||||
@ -1295,7 +1295,7 @@ func (suite *ControllerIntegrationSuite) TestBackup_CreatesPrefixCollections() {
|
||||
start = time.Now()
|
||||
)
|
||||
|
||||
id, name, err := backupCtrl.PopulateOwnerIDAndNamesFrom(ctx, backupSel.DiscreteOwner, nil)
|
||||
id, name, err := backupCtrl.PopulateProtectedResourceIDAndName(ctx, backupSel.DiscreteOwner, nil)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
backupSel.SetDiscreteOwnerIDName(id, name)
|
||||
|
||||
@ -26,6 +26,10 @@ type Controller struct {
|
||||
Err error
|
||||
|
||||
Stats data.CollectionStats
|
||||
|
||||
ProtectedResourceID string
|
||||
ProtectedResourceName string
|
||||
ProtectedResourceErr error
|
||||
}
|
||||
|
||||
func (ctrl Controller) ProduceBackupCollections(
|
||||
@ -71,3 +75,13 @@ func (ctrl Controller) ConsumeRestoreCollections(
|
||||
}
|
||||
|
||||
func (ctrl Controller) CacheItemInfo(dii details.ItemInfo) {}
|
||||
|
||||
func (ctrl Controller) PopulateProtectedResourceIDAndName(
|
||||
ctx context.Context,
|
||||
protectedResource string, // input value, can be either id or name
|
||||
ins idname.Cacher,
|
||||
) (string, string, error) {
|
||||
return ctrl.ProtectedResourceID,
|
||||
ctrl.ProtectedResourceName,
|
||||
ctrl.ProtectedResourceErr
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ func ControllerWithSelector(
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
id, name, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, sel.DiscreteOwner, ins)
|
||||
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
|
||||
if !assert.NoError(t, err, clues.ToCore(err)) {
|
||||
if onFail != nil {
|
||||
onFail()
|
||||
|
||||
@ -48,6 +48,7 @@ type (
|
||||
Wait() *data.CollectionStats
|
||||
|
||||
CacheItemInfoer
|
||||
PopulateProtectedResourceIDAndNamer
|
||||
}
|
||||
|
||||
CacheItemInfoer interface {
|
||||
@ -59,6 +60,25 @@ type (
|
||||
CacheItemInfo(v details.ItemInfo)
|
||||
}
|
||||
|
||||
PopulateProtectedResourceIDAndNamer interface {
|
||||
// PopulateProtectedResourceIDAndName takes the provided owner identifier and produces
|
||||
// the owner's name and ID from that value. Returns an error if the owner is
|
||||
// not recognized by the current tenant.
|
||||
//
|
||||
// The id-name swapper should be optional. Some processes will look up all owners in
|
||||
// the tenant before reaching this step. In that case, the data gets handed
|
||||
// down for this func to consume instead of performing further queries. The
|
||||
// data gets stored inside the controller instance for later re-use.
|
||||
PopulateProtectedResourceIDAndName(
|
||||
ctx context.Context,
|
||||
owner string, // input value, can be either id or name
|
||||
ins idname.Cacher,
|
||||
) (
|
||||
id, name string,
|
||||
err error,
|
||||
)
|
||||
}
|
||||
|
||||
RepoMaintenancer interface {
|
||||
RepoMaintenance(ctx context.Context, opts repository.Maintenance) error
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/crash"
|
||||
"github.com/alcionai/corso/src/internal/common/dttm"
|
||||
"github.com/alcionai/corso/src/internal/common/idname"
|
||||
"github.com/alcionai/corso/src/internal/data"
|
||||
"github.com/alcionai/corso/src/internal/diagnostics"
|
||||
"github.com/alcionai/corso/src/internal/events"
|
||||
@ -217,7 +218,19 @@ func (op *RestoreOperation) do(
|
||||
return nil, clues.Wrap(err, "getting backup and details")
|
||||
}
|
||||
|
||||
observe.Message(ctx, "Restoring", observe.Bullet, clues.Hide(bup.Selector.DiscreteOwner))
|
||||
restoreProtectedResource, err := chooseRestoreResource(ctx, op.rc, op.RestoreCfg, bup.Selector)
|
||||
if err != nil {
|
||||
return nil, clues.Wrap(err, "getting destination protected resource")
|
||||
}
|
||||
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"backup_protected_resource_id", bup.Selector.ID(),
|
||||
"backup_protected_resource_name", clues.Hide(bup.Selector.Name()),
|
||||
"restore_protected_resource_id", restoreProtectedResource.ID(),
|
||||
"restore_protected_resource_name", clues.Hide(restoreProtectedResource.Name()))
|
||||
|
||||
observe.Message(ctx, "Restoring", observe.Bullet, clues.Hide(restoreProtectedResource.Name()))
|
||||
|
||||
paths, err := formatDetailsForRestoration(
|
||||
ctx,
|
||||
@ -232,8 +245,6 @@ func (op *RestoreOperation) do(
|
||||
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"resource_owner_id", bup.Selector.ID(),
|
||||
"resource_owner_name", clues.Hide(bup.Selector.Name()),
|
||||
"details_entries", len(deets.Entries),
|
||||
"details_paths", len(paths),
|
||||
"backup_snapshot_id", bup.SnapshotID,
|
||||
@ -321,6 +332,24 @@ func (op *RestoreOperation) persistResults(
|
||||
return op.Errors.Failure()
|
||||
}
|
||||
|
||||
func chooseRestoreResource(
|
||||
ctx context.Context,
|
||||
pprian inject.PopulateProtectedResourceIDAndNamer,
|
||||
restoreCfg control.RestoreConfig,
|
||||
orig idname.Provider,
|
||||
) (idname.Provider, error) {
|
||||
if len(restoreCfg.ProtectedResource) == 0 {
|
||||
return orig, nil
|
||||
}
|
||||
|
||||
id, name, err := pprian.PopulateProtectedResourceIDAndName(
|
||||
ctx,
|
||||
restoreCfg.ProtectedResource,
|
||||
nil)
|
||||
|
||||
return idname.NewProvider(id, name), clues.Stack(err).OrNil()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Restorer funcs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -10,6 +10,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/common/dttm"
|
||||
"github.com/alcionai/corso/src/internal/common/idname"
|
||||
inMock "github.com/alcionai/corso/src/internal/common/idname/mock"
|
||||
"github.com/alcionai/corso/src/internal/data"
|
||||
"github.com/alcionai/corso/src/internal/events"
|
||||
@ -40,15 +42,15 @@ import (
|
||||
// unit
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type RestoreOpSuite struct {
|
||||
type RestoreOpUnitSuite struct {
|
||||
tester.Suite
|
||||
}
|
||||
|
||||
func TestRestoreOpSuite(t *testing.T) {
|
||||
suite.Run(t, &RestoreOpSuite{Suite: tester.NewUnitSuite(t)})
|
||||
func TestRestoreOpUnitSuite(t *testing.T) {
|
||||
suite.Run(t, &RestoreOpUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||
}
|
||||
|
||||
func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() {
|
||||
func (suite *RestoreOpUnitSuite) TestRestoreOperation_PersistResults() {
|
||||
var (
|
||||
kw = &kopia.Wrapper{}
|
||||
sw = &store.Wrapper{}
|
||||
@ -138,6 +140,75 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *RestoreOpUnitSuite) TestChooseRestoreResource() {
|
||||
var (
|
||||
id = "id"
|
||||
name = "name"
|
||||
cfgWithPR = control.DefaultRestoreConfig(dttm.HumanReadable)
|
||||
)
|
||||
|
||||
cfgWithPR.ProtectedResource = "cfgid"
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
cfg control.RestoreConfig
|
||||
ctrl *mock.Controller
|
||||
orig idname.Provider
|
||||
expectErr assert.ErrorAssertionFunc
|
||||
expectProvider assert.ValueAssertionFunc
|
||||
expectID string
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
name: "use original",
|
||||
cfg: control.DefaultRestoreConfig(dttm.HumanReadable),
|
||||
ctrl: &mock.Controller{
|
||||
ProtectedResourceID: id,
|
||||
ProtectedResourceName: name,
|
||||
},
|
||||
orig: idname.NewProvider("oid", "oname"),
|
||||
expectErr: assert.NoError,
|
||||
expectID: "oid",
|
||||
expectName: "oname",
|
||||
},
|
||||
{
|
||||
name: "look up resource with iface",
|
||||
cfg: cfgWithPR,
|
||||
ctrl: &mock.Controller{
|
||||
ProtectedResourceID: id,
|
||||
ProtectedResourceName: name,
|
||||
},
|
||||
orig: idname.NewProvider("oid", "oname"),
|
||||
expectErr: assert.NoError,
|
||||
expectID: id,
|
||||
expectName: name,
|
||||
},
|
||||
{
|
||||
name: "error looking up protected resource",
|
||||
cfg: cfgWithPR,
|
||||
ctrl: &mock.Controller{
|
||||
ProtectedResourceErr: assert.AnError,
|
||||
},
|
||||
orig: idname.NewProvider("oid", "oname"),
|
||||
expectErr: assert.Error,
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
result, err := chooseRestoreResource(ctx, test.ctrl, test.cfg, test.orig)
|
||||
test.expectErr(t, err, clues.ToCore(err))
|
||||
require.NotNil(t, result)
|
||||
assert.Equal(t, test.expectID, result.ID())
|
||||
assert.Equal(t, test.expectName, result.Name())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// integration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -550,7 +550,7 @@ func ControllerWithSelector(
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
id, name, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, sel.DiscreteOwner, ins)
|
||||
id, name, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
|
||||
if !assert.NoError(t, err, clues.ToCore(err)) {
|
||||
if onFail != nil {
|
||||
onFail(t, ctx)
|
||||
|
||||
@ -329,7 +329,7 @@ func (r repository) NewBackupWithLookup(
|
||||
return operations.BackupOperation{}, clues.Wrap(err, "connecting to m365")
|
||||
}
|
||||
|
||||
ownerID, ownerName, err := ctrl.PopulateOwnerIDAndNamesFrom(ctx, sel.DiscreteOwner, ins)
|
||||
ownerID, ownerName, err := ctrl.PopulateProtectedResourceIDAndName(ctx, sel.DiscreteOwner, ins)
|
||||
if err != nil {
|
||||
return operations.BackupOperation{}, clues.Wrap(err, "resolving resource owner details")
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user