## Description Aid in testing. Break the interface in two for the moment, one of which is embedded in the other. The smaller interface focuses just on getting information about backups from the ModelStore. ## Type of change <!--- Please check the type of change your PR introduces: ---> - [ ] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Test - [ ] 💻 CI/Deployment - [x] 🐹 Trivial/Minor ## Issue(s) * #913 ## Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
242 lines
6.0 KiB
Go
242 lines
6.0 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/alcionai/corso/src/internal/events"
|
|
"github.com/alcionai/corso/src/internal/kopia"
|
|
"github.com/alcionai/corso/src/internal/model"
|
|
"github.com/alcionai/corso/src/internal/operations"
|
|
"github.com/alcionai/corso/src/pkg/account"
|
|
"github.com/alcionai/corso/src/pkg/backup"
|
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
|
"github.com/alcionai/corso/src/pkg/control"
|
|
"github.com/alcionai/corso/src/pkg/logger"
|
|
"github.com/alcionai/corso/src/pkg/selectors"
|
|
"github.com/alcionai/corso/src/pkg/storage"
|
|
"github.com/alcionai/corso/src/pkg/store"
|
|
)
|
|
|
|
// BackupGetter deals with retrieving metadata about backups from the
|
|
// repository.
|
|
type BackupGetter interface {
|
|
Backup(ctx context.Context, id model.StableID) (*backup.Backup, error)
|
|
Backups(ctx context.Context) ([]backup.Backup, error)
|
|
BackupDetails(
|
|
ctx context.Context,
|
|
backupID string,
|
|
) (*details.Details, *backup.Backup, error)
|
|
}
|
|
|
|
type Repository interface {
|
|
Close(context.Context) error
|
|
NewBackup(
|
|
ctx context.Context,
|
|
self selectors.Selector,
|
|
) (operations.BackupOperation, error)
|
|
NewRestore(
|
|
ctx context.Context,
|
|
backupID string,
|
|
sel selectors.Selector,
|
|
) (operations.RestoreOperation, error)
|
|
DeleteBackup(ctx context.Context, id model.StableID) error
|
|
BackupGetter
|
|
}
|
|
|
|
// Repository contains storage provider information.
|
|
type repository struct {
|
|
ID uuid.UUID
|
|
CreatedAt time.Time
|
|
Version string // in case of future breaking changes
|
|
|
|
Account account.Account // the user's m365 account connection details
|
|
Storage storage.Storage // the storage provider details and configuration
|
|
Opts control.Options
|
|
|
|
Bus events.Eventer
|
|
dataLayer *kopia.Wrapper
|
|
modelStore *kopia.ModelStore
|
|
}
|
|
|
|
// Initialize will:
|
|
// * validate the m365 account & secrets
|
|
// * connect to the m365 account to ensure communication capability
|
|
// * validate the provider config & secrets
|
|
// * initialize the kopia repo with the provider
|
|
// * store the configuration details
|
|
// * connect to the provider
|
|
// * return the connected repository
|
|
func Initialize(
|
|
ctx context.Context,
|
|
acct account.Account,
|
|
s storage.Storage,
|
|
opts control.Options,
|
|
) (Repository, error) {
|
|
kopiaRef := kopia.NewConn(s)
|
|
if err := kopiaRef.Initialize(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
// kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe
|
|
// to close here.
|
|
defer kopiaRef.Close(ctx)
|
|
|
|
w, err := kopia.NewWrapper(kopiaRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ms, err := kopia.NewModelStore(kopiaRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
r := repository{
|
|
ID: uuid.New(),
|
|
Version: "v1",
|
|
Account: acct,
|
|
Storage: s,
|
|
Bus: events.NewBus(s, acct.ID(), opts),
|
|
dataLayer: w,
|
|
modelStore: ms,
|
|
}
|
|
|
|
r.Bus.Event(ctx, events.RepoInit, nil)
|
|
|
|
return &r, nil
|
|
}
|
|
|
|
// Connect will:
|
|
// * validate the m365 account details
|
|
// * connect to the m365 account to ensure communication capability
|
|
// * connect to the provider storage
|
|
// * return the connected repository
|
|
func Connect(
|
|
ctx context.Context,
|
|
acct account.Account,
|
|
s storage.Storage,
|
|
opts control.Options,
|
|
) (Repository, error) {
|
|
kopiaRef := kopia.NewConn(s)
|
|
if err := kopiaRef.Connect(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
// kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe
|
|
// to close here.
|
|
defer kopiaRef.Close(ctx)
|
|
|
|
w, err := kopia.NewWrapper(kopiaRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ms, err := kopia.NewModelStore(kopiaRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// todo: ID and CreatedAt should get retrieved from a stored kopia config.
|
|
r := repository{
|
|
Version: "v1",
|
|
Account: acct,
|
|
Storage: s,
|
|
Bus: events.NewBus(s, acct.ID(), opts),
|
|
dataLayer: w,
|
|
modelStore: ms,
|
|
}
|
|
|
|
return &r, nil
|
|
}
|
|
|
|
func (r *repository) Close(ctx context.Context) error {
|
|
if err := r.Bus.Close(); err != nil {
|
|
logger.Ctx(ctx).Debugw("closing the event bus", "err", err)
|
|
}
|
|
|
|
if r.dataLayer != nil {
|
|
if err := r.dataLayer.Close(ctx); err != nil {
|
|
logger.Ctx(ctx).Debugw("closing Datalayer", "err", err)
|
|
}
|
|
|
|
r.dataLayer = nil
|
|
}
|
|
|
|
if r.modelStore != nil {
|
|
if err := r.modelStore.Close(ctx); err != nil {
|
|
logger.Ctx(ctx).Debugw("closing modelStore", "err", err)
|
|
}
|
|
|
|
r.modelStore = nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewBackup generates a BackupOperation runner.
|
|
func (r repository) NewBackup(
|
|
ctx context.Context,
|
|
selector selectors.Selector,
|
|
) (operations.BackupOperation, error) {
|
|
return operations.NewBackupOperation(
|
|
ctx,
|
|
r.Opts,
|
|
r.dataLayer,
|
|
store.NewKopiaStore(r.modelStore),
|
|
r.Account,
|
|
selector,
|
|
r.Bus)
|
|
}
|
|
|
|
// NewRestore generates a restoreOperation runner.
|
|
func (r repository) NewRestore(
|
|
ctx context.Context,
|
|
backupID string,
|
|
sel selectors.Selector,
|
|
) (operations.RestoreOperation, error) {
|
|
return operations.NewRestoreOperation(
|
|
ctx,
|
|
r.Opts,
|
|
r.dataLayer,
|
|
store.NewKopiaStore(r.modelStore),
|
|
r.Account,
|
|
model.StableID(backupID),
|
|
sel,
|
|
r.Bus)
|
|
}
|
|
|
|
// backups lists a backup by id
|
|
func (r repository) Backup(ctx context.Context, id model.StableID) (*backup.Backup, error) {
|
|
sw := store.NewKopiaStore(r.modelStore)
|
|
return sw.GetBackup(ctx, id)
|
|
}
|
|
|
|
// backups lists backups in a repository
|
|
func (r repository) Backups(ctx context.Context) ([]backup.Backup, error) {
|
|
sw := store.NewKopiaStore(r.modelStore)
|
|
return sw.GetBackups(ctx)
|
|
}
|
|
|
|
// BackupDetails returns the specified backup details object
|
|
func (r repository) BackupDetails(ctx context.Context, backupID string) (*details.Details, *backup.Backup, error) {
|
|
sw := store.NewKopiaStore(r.modelStore)
|
|
return sw.GetDetailsFromBackupID(ctx, model.StableID(backupID))
|
|
}
|
|
|
|
// DeleteBackup removes the backup from both the model store and the backup storage.
|
|
func (r repository) DeleteBackup(ctx context.Context, id model.StableID) error {
|
|
bu, err := r.Backup(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := r.dataLayer.DeleteSnapshot(ctx, bu.SnapshotID); err != nil {
|
|
return err
|
|
}
|
|
|
|
sw := store.NewKopiaStore(r.modelStore)
|
|
|
|
return sw.DeleteBackup(ctx, id)
|
|
}
|