Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

Split backups by (resource owner, service) at CLI layer #1609

Merged
merged 9 commits into from
Nov 30, 2022
55 changes: 46 additions & 9 deletions src/cli/backup/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backup
import (
"context"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand All @@ -19,6 +20,7 @@ import (
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/services/m365"
"github.com/alcionai/corso/src/pkg/store"
)

Expand Down Expand Up @@ -270,27 +272,62 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {

sel := exchangeBackupCreateSelectors(user, exchangeData)

bo, err := r.NewBackup(ctx, sel)
users, err := m365.UserIDs(ctx, acct)
ryanfkeepers marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to initialize Exchange backup"))
return Only(ctx, errors.Wrap(err, "Failed to retrieve M365 users"))
}

err = bo.Run(ctx)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to run Exchange backup"))
var (
errs *multierror.Error
bIDs []model.StableID
)

for _, scope := range sel.DiscreteScopes(users) {
ryanfkeepers marked this conversation as resolved.
Show resolved Hide resolved
for _, selUser := range scope.Get(selectors.ExchangeUser) {
opSel := selectors.NewExchangeBackup()
opSel.Include([]selectors.ExchangeScope{scope.DiscreteCopy(selUser)})

bo, err := r.NewBackup(ctx, opSel.Selector)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to initialize Exchange backup for user %s",
scope.Get(selectors.ExchangeUser),
))

continue
}

err = bo.Run(ctx)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to run Exchange backup for user %s",
scope.Get(selectors.ExchangeUser),
))

continue
}

bIDs = append(bIDs, bo.Results.BackupID)
}
}

bu, err := r.Backup(ctx, bo.Results.BackupID)
bups, err := r.Backups(ctx, bIDs)
if err != nil {
return Only(ctx, errors.Wrap(err, "Unable to retrieve backup results from storage"))
}

bu.Print(ctx)
backup.PrintAll(ctx, bups)

if e := errs.ErrorOrNil(); e != nil {
return Only(ctx, e)
}

return nil
}

func exchangeBackupCreateSelectors(userIDs, data []string) selectors.Selector {
func exchangeBackupCreateSelectors(userIDs, data []string) *selectors.ExchangeBackup {
sel := selectors.NewExchangeBackup()

if len(data) == 0 {
Expand All @@ -310,7 +347,7 @@ func exchangeBackupCreateSelectors(userIDs, data []string) selectors.Selector {
}
}

return sel.Selector
return sel
}

func validateExchangeBackupCreateFlags(userIDs, data []string) error {
Expand Down
57 changes: 47 additions & 10 deletions src/cli/backup/onedrive.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backup
import (
"context"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/services/m365"
"github.com/alcionai/corso/src/pkg/store"
)

Expand Down Expand Up @@ -192,22 +194,57 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error {

sel := oneDriveBackupCreateSelectors(user)

bo, err := r.NewBackup(ctx, sel)
users, err := m365.UserIDs(ctx, acct)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to initialize OneDrive backup"))
return Only(ctx, errors.Wrap(err, "Failed to retrieve M365 users"))
}

err = bo.Run(ctx)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to run OneDrive backup"))
var (
errs *multierror.Error
bIDs []model.StableID
)

for _, scope := range sel.DiscreteScopes(users) {
for _, selUser := range scope.Get(selectors.OneDriveUser) {
opSel := selectors.NewOneDriveBackup()
opSel.Include([]selectors.OneDriveScope{scope.DiscreteCopy(selUser)})

bo, err := r.NewBackup(ctx, opSel.Selector)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to initialize Exchange backup for user %s",
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
scope.Get(selectors.OneDriveUser),
))

continue
}

err = bo.Run(ctx)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to run Exchange backup for user %s",
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
scope.Get(selectors.OneDriveUser),
))

continue
}

bIDs = append(bIDs, bo.Results.BackupID)
}
}

bu, err := r.Backup(ctx, bo.Results.BackupID)
bups, err := r.Backups(ctx, bIDs)
if err != nil {
return errors.Wrap(err, "Unable to retrieve backup results from storage")
return Only(ctx, errors.Wrap(err, "Unable to retrieve backup results from storage"))
}

bu.Print(ctx)
backup.PrintAll(ctx, bups)

if e := errs.ErrorOrNil(); e != nil {
return Only(ctx, e)
}

return nil
}
Expand All @@ -220,11 +257,11 @@ func validateOneDriveBackupCreateFlags(users []string) error {
return nil
}

func oneDriveBackupCreateSelectors(users []string) selectors.Selector {
func oneDriveBackupCreateSelectors(users []string) *selectors.OneDriveBackup {
sel := selectors.NewOneDriveBackup()
sel.Include(sel.Users(users))

return sel.Selector
return sel
}

// ------------------------------------------------------------------------------------------------
Expand Down
57 changes: 47 additions & 10 deletions src/cli/backup/sharepoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backup
import (
"context"

"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/selectors"
"github.com/alcionai/corso/src/pkg/services/m365"
"github.com/alcionai/corso/src/pkg/store"
)

Expand Down Expand Up @@ -181,22 +183,57 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error {

sel := sharePointBackupCreateSelectors(site)

bo, err := r.NewBackup(ctx, sel)
sites, err := m365.UserIDs(ctx, acct)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to initialize SharePoint backup"))
return Only(ctx, errors.Wrap(err, "Failed to retrieve M365 users"))
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
}

err = bo.Run(ctx)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to run SharePoint backup"))
var (
errs *multierror.Error
bIDs []model.StableID
)

for _, scope := range sel.DiscreteScopes(sites) {
for _, selSite := range scope.Get(selectors.SharePointSite) {
opSel := selectors.NewSharePointBackup()
opSel.Include([]selectors.SharePointScope{scope.DiscreteCopy(selSite)})

bo, err := r.NewBackup(ctx, opSel.Selector)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to initialize Exchange backup for user %s",
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
scope.Get(selectors.SharePointSite),
))

continue
}

err = bo.Run(ctx)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(
err,
"Failed to run Exchange backup for user %s",
ashmrtn marked this conversation as resolved.
Show resolved Hide resolved
scope.Get(selectors.SharePointSite),
))

continue
}

bIDs = append(bIDs, bo.Results.BackupID)
}
}

bu, err := r.Backup(ctx, bo.Results.BackupID)
bups, err := r.Backups(ctx, bIDs)
if err != nil {
return errors.Wrap(err, "Unable to retrieve backup results from storage")
return Only(ctx, errors.Wrap(err, "Unable to retrieve backup results from storage"))
}

bu.Print(ctx)
backup.PrintAll(ctx, bups)

if e := errs.ErrorOrNil(); e != nil {
return Only(ctx, e)
}

return nil
}
Expand All @@ -209,11 +246,11 @@ func validateSharePointBackupCreateFlags(sites []string) error {
return nil
}

func sharePointBackupCreateSelectors(sites []string) selectors.Selector {
func sharePointBackupCreateSelectors(sites []string) *selectors.SharePointBackup {
sel := selectors.NewSharePointBackup()
sel.Include(sel.Sites(sites))

return sel.Selector
return sel
}

// ------------------------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions src/internal/connector/graph_connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ func NewGraphConnector(ctx context.Context, acct account.Account, r resource) (*

gc.graphService = *aService

// TODO(ashmrtn): When selectors only encapsulate a single resource owner that
// is not a wildcard don't populate users or sites when making the connector.
// For now this keeps things functioning if callers do pass in a selector like
// "*" instead of.
if r == AllResources || r == Users {
if err = gc.setTenantUsers(ctx); err != nil {
return nil, errors.Wrap(err, "retrieving tenant user list")
Expand Down
11 changes: 11 additions & 0 deletions src/pkg/services/m365/m365.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ func Users(ctx context.Context, m365Account account.Account) ([]string, error) {
return gc.GetUsers(), nil
}

// Sites returns a list of SharePoint sites in the specified M365 tenant
// TODO: Implement paging support
func Sites(ctx context.Context, m365Account account.Account) ([]string, error) {
gc, err := connector.NewGraphConnector(ctx, m365Account, connector.Sites)
if err != nil {
return nil, errors.Wrap(err, "could not initialize M365 graph connection")
}

return gc.GetSites(), nil
}

// UserIDs returns a list of user IDs for the specified M365 tenant
// TODO: Implement paging support
func UserIDs(ctx context.Context, m365Account account.Account) ([]string, error) {
Expand Down