add --all and --data to backup create (#342)
* add --all and --data to backup create Adds flags for backing up all exchange data, and for isolating the data in the backup by data type. Introduces validation and selector creation in backup create. Switches the --user flag variable type from a string to a []string.
This commit is contained in:
parent
bd3b2a8096
commit
efaa2da1bb
@ -15,8 +15,16 @@ import (
|
||||
|
||||
// exchange bucket info from flags
|
||||
var (
|
||||
user string
|
||||
backupDetailsID string
|
||||
exchangeAll bool
|
||||
exchangeData []string
|
||||
user []string
|
||||
)
|
||||
|
||||
const (
|
||||
dataContacts = "contacts"
|
||||
dataEmail = "email"
|
||||
dataEvents = "events"
|
||||
)
|
||||
|
||||
// called by backup.go to map parent subcommands to provider-specific handling.
|
||||
@ -28,12 +36,18 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
|
||||
switch parent.Use {
|
||||
case createCommand:
|
||||
c, fs = utils.AddCommand(parent, exchangeCreateCmd)
|
||||
fs.StringVar(&user, "user", "", "ID of the user whose Exchange data is to be backed up.")
|
||||
fs.StringArrayVar(&user, "user", nil, "Back up Exchange data by user ID; accepts "+utils.Wildcard+" to select all users")
|
||||
fs.BoolVar(&exchangeAll, "all", false, "Back up all Exchange data for all users")
|
||||
fs.StringArrayVar(
|
||||
&exchangeData,
|
||||
"data",
|
||||
nil,
|
||||
"Select one or more types of data to backup: "+dataEmail+", "+dataContacts+", or "+dataEvents)
|
||||
case listCommand:
|
||||
c, _ = utils.AddCommand(parent, exchangeListCmd)
|
||||
case detailsCommand:
|
||||
c, fs = utils.AddCommand(parent, exchangeDetailsCmd)
|
||||
fs.StringVar(&backupDetailsID, "backup-details", "", "ID of the backup details to be shown.")
|
||||
fs.StringVar(&backupDetailsID, "backup-details", "", "ID of the backup details to be shown")
|
||||
cobra.CheckErr(c.MarkFlagRequired("backup-details"))
|
||||
}
|
||||
return c
|
||||
@ -56,6 +70,9 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
if utils.HasNoFlagsAndShownHelp(cmd) {
|
||||
return nil
|
||||
}
|
||||
if err := validateBackupCreateFlags(exchangeAll, user, exchangeData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s, acct, err := config.GetStorageAndAccount(true, nil)
|
||||
if err != nil {
|
||||
@ -79,10 +96,9 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
defer utils.CloseRepo(ctx, r)
|
||||
|
||||
sel := selectors.NewExchangeBackup()
|
||||
sel.Include(sel.Users(user))
|
||||
sel := exchangeBackupCreateSelectors(exchangeAll, user, exchangeData)
|
||||
|
||||
bo, err := r.NewBackup(ctx, sel.Selector)
|
||||
bo, err := r.NewBackup(ctx, sel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed to initialize Exchange backup")
|
||||
}
|
||||
@ -97,6 +113,65 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func exchangeBackupCreateSelectors(all bool, users, data []string) selectors.Selector {
|
||||
sel := selectors.NewExchangeBackup()
|
||||
if all {
|
||||
sel.Include(sel.Users(selectors.All))
|
||||
return sel.Selector
|
||||
}
|
||||
if len(data) == 0 {
|
||||
for _, user := range users {
|
||||
if user == utils.Wildcard {
|
||||
user = selectors.All
|
||||
}
|
||||
sel.Include(sel.ContactFolders(user, selectors.All))
|
||||
sel.Include(sel.MailFolders(user, selectors.All))
|
||||
sel.Include(sel.Events(user, selectors.All))
|
||||
}
|
||||
}
|
||||
for _, d := range data {
|
||||
switch d {
|
||||
case dataContacts:
|
||||
for _, user := range users {
|
||||
if user == utils.Wildcard {
|
||||
user = selectors.All
|
||||
}
|
||||
sel.Include(sel.ContactFolders(user, selectors.All))
|
||||
}
|
||||
case dataEmail:
|
||||
for _, user := range users {
|
||||
if user == utils.Wildcard {
|
||||
user = selectors.All
|
||||
}
|
||||
sel.Include(sel.MailFolders(user, selectors.All))
|
||||
}
|
||||
case dataEvents:
|
||||
for _, user := range users {
|
||||
if user == utils.Wildcard {
|
||||
user = selectors.All
|
||||
}
|
||||
sel.Include(sel.Events(user, selectors.All))
|
||||
}
|
||||
}
|
||||
}
|
||||
return sel.Selector
|
||||
}
|
||||
|
||||
func validateBackupCreateFlags(all bool, users, data []string) error {
|
||||
if len(users) == 0 && !all {
|
||||
return errors.New("requries one or more --user ids, the wildcard --user *, or the --all flag.")
|
||||
}
|
||||
if len(data) > 0 && all {
|
||||
return errors.New("--all backs up all data, and cannot be reduced with --data")
|
||||
}
|
||||
for _, d := range data {
|
||||
if d != dataContacts && d != dataEmail && d != dataEvents {
|
||||
return errors.New(d + " is an unrecognized data type; must be one of " + dataContacts + ", " + dataEmail + ", or " + dataEvents)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// `corso backup list exchange [<flag>...]`
|
||||
var exchangeListCmd = &cobra.Command{
|
||||
Use: exchangeServiceCommand,
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/cli/utils"
|
||||
ctesting "github.com/alcionai/corso/internal/testing"
|
||||
)
|
||||
|
||||
@ -49,3 +50,165 @@ func (suite *ExchangeSuite) TestAddExchangeCommands() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ExchangeSuite) TestValidateBackupCreateFlags() {
|
||||
table := []struct {
|
||||
name string
|
||||
all bool
|
||||
user, data []string
|
||||
expect assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "no users, not all",
|
||||
expect: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "all and data",
|
||||
all: true,
|
||||
data: []string{dataEmail},
|
||||
expect: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "unrecognized data",
|
||||
user: []string{"fnord"},
|
||||
data: []string{"smurfs"},
|
||||
expect: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "users, not all",
|
||||
user: []string{"fnord"},
|
||||
expect: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "no users, all",
|
||||
all: true,
|
||||
expect: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "users, all",
|
||||
all: true,
|
||||
user: []string{"fnord"},
|
||||
expect: assert.NoError,
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
test.expect(t, validateBackupCreateFlags(test.all, test.user, test.data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
|
||||
table := []struct {
|
||||
name string
|
||||
all bool
|
||||
user, data []string
|
||||
expectIncludeLen int
|
||||
}{
|
||||
{
|
||||
name: "all",
|
||||
all: true,
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "all users, no data",
|
||||
user: []string{utils.Wildcard},
|
||||
expectIncludeLen: 3,
|
||||
},
|
||||
{
|
||||
name: "single user, no data",
|
||||
user: []string{"u1"},
|
||||
expectIncludeLen: 3,
|
||||
},
|
||||
{
|
||||
name: "all users, contacts",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataContacts},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "single user, contacts",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataContacts},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "all users, email",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataEmail},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "single user, email",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataEmail},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "all users, events",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataEvents},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "single user, events",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataEvents},
|
||||
expectIncludeLen: 1,
|
||||
},
|
||||
{
|
||||
name: "all users, contacts + email",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataContacts, dataEmail},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "single user, contacts + email",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataContacts, dataEmail},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "all users, email + events",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataEmail, dataEvents},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "single user, email + events",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataEmail, dataEvents},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "all users, events + contacts",
|
||||
user: []string{utils.Wildcard},
|
||||
data: []string{dataEvents, dataContacts},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "single user, events + contacts",
|
||||
user: []string{"u1"},
|
||||
data: []string{dataEvents, dataContacts},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "many users, events",
|
||||
user: []string{"fnord", "smarf"},
|
||||
data: []string{dataEvents},
|
||||
expectIncludeLen: 2,
|
||||
},
|
||||
{
|
||||
name: "many users, events + contacts",
|
||||
user: []string{"fnord", "smarf"},
|
||||
data: []string{dataEvents, dataContacts},
|
||||
expectIncludeLen: 4,
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
sel := exchangeBackupCreateSelectors(test.all, test.user, test.data)
|
||||
assert.Equal(t, test.expectIncludeLen, len(sel.Includes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,10 +127,10 @@ func validateRestoreFlags(u, f, m, rpid string) error {
|
||||
return errors.New("a restore point ID is requried")
|
||||
}
|
||||
lu, lf, lm := len(u), len(f), len(m)
|
||||
if (lu == 0 || u == "*") && (lf+lm > 0) {
|
||||
if (lu == 0 || u == utils.Wildcard) && (lf+lm > 0) {
|
||||
return errors.New("a specific --user must be provided if --folder or --mail is specified")
|
||||
}
|
||||
if (lf == 0 || f == "*") && lm > 0 {
|
||||
if (lf == 0 || f == utils.Wildcard) && lm > 0 {
|
||||
return errors.New("a specific --folder must be provided if a --mail is specified")
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/cli/utils"
|
||||
ctesting "github.com/alcionai/corso/internal/testing"
|
||||
)
|
||||
|
||||
@ -27,10 +28,10 @@ func (suite *ExchangeSuite) TestValidateRestoreFlags() {
|
||||
}{
|
||||
{"all populated", "u", "f", "m", "rpid", assert.NoError},
|
||||
{"folder missing user", "", "f", "m", "rpid", assert.Error},
|
||||
{"folder with wildcard user", "*", "f", "m", "rpid", assert.Error},
|
||||
{"folder with wildcard user", utils.Wildcard, "f", "m", "rpid", assert.Error},
|
||||
{"mail missing user", "", "", "m", "rpid", assert.Error},
|
||||
{"mail missing folder", "u", "", "m", "rpid", assert.Error},
|
||||
{"mail with wildcard folder", "u", "*", "m", "rpid", assert.Error},
|
||||
{"mail with wildcard folder", "u", utils.Wildcard, "m", "rpid", assert.Error},
|
||||
{"missing backup id", "u", "f", "m", "", assert.Error},
|
||||
{"all missing", "", "", "", "rpid", assert.NoError},
|
||||
}
|
||||
|
||||
@ -10,6 +10,10 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
Wildcard = "*"
|
||||
)
|
||||
|
||||
// RequireProps validates the existence of the properties
|
||||
// in the map. Expects the format map[propName]propVal.
|
||||
func RequireProps(props map[string]string) error {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user