add cli commands to delete backups (#641)
This commit is contained in:
parent
db2c1ec8e2
commit
f1370b36e4
@ -4,21 +4,28 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var backupCommands = []func(parent *cobra.Command) *cobra.Command{
|
var subCommands = []*cobra.Command{
|
||||||
|
createCmd,
|
||||||
|
listCmd,
|
||||||
|
detailsCmd,
|
||||||
|
deleteCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceCommands = []func(parent *cobra.Command) *cobra.Command{
|
||||||
addExchangeCommands,
|
addExchangeCommands,
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCommands attaches all `corso backup * *` commands to the parent.
|
// AddCommands attaches all `corso backup * *` commands to the parent.
|
||||||
func AddCommands(parent *cobra.Command) {
|
func AddCommands(parent *cobra.Command) {
|
||||||
parent.AddCommand(backupCmd)
|
parent.AddCommand(backupCmd)
|
||||||
backupCmd.AddCommand(createCmd)
|
for _, sc := range subCommands {
|
||||||
backupCmd.AddCommand(listCmd)
|
backupCmd.AddCommand(sc)
|
||||||
backupCmd.AddCommand(detailsCmd)
|
}
|
||||||
|
|
||||||
for _, addBackupTo := range backupCommands {
|
for _, addBackupTo := range serviceCommands {
|
||||||
addBackupTo(createCmd)
|
for _, sc := range subCommands {
|
||||||
addBackupTo(listCmd)
|
addBackupTo(sc)
|
||||||
addBackupTo(detailsCmd)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ func handleListCmd(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The backup details subcommand.
|
// The backup details subcommand.
|
||||||
// `corso backup list <service> [<flag>...]`
|
// `corso backup details <service> [<flag>...]`
|
||||||
var (
|
var (
|
||||||
detailsCommand = "details"
|
detailsCommand = "details"
|
||||||
detailsCmd = &cobra.Command{
|
detailsCmd = &cobra.Command{
|
||||||
@ -91,3 +98,21 @@ var (
|
|||||||
func handleDetailsCmd(cmd *cobra.Command, args []string) error {
|
func handleDetailsCmd(cmd *cobra.Command, args []string) error {
|
||||||
return cmd.Help()
|
return cmd.Help()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The backup delete subcommand.
|
||||||
|
// `corso backup delete <service> [<flag>...]`
|
||||||
|
var (
|
||||||
|
deleteCommand = "delete"
|
||||||
|
deleteCmd = &cobra.Command{
|
||||||
|
Use: deleteCommand,
|
||||||
|
Short: "Deletes a backup for a service",
|
||||||
|
RunE: handleDeleteCmd,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler for calls to `corso backup delete`.
|
||||||
|
// Produces the same output as `corso backup delete --help`.
|
||||||
|
func handleDeleteCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
return cmd.Help()
|
||||||
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/alcionai/corso/cli/options"
|
"github.com/alcionai/corso/cli/options"
|
||||||
. "github.com/alcionai/corso/cli/print"
|
. "github.com/alcionai/corso/cli/print"
|
||||||
"github.com/alcionai/corso/cli/utils"
|
"github.com/alcionai/corso/cli/utils"
|
||||||
|
"github.com/alcionai/corso/internal/model"
|
||||||
"github.com/alcionai/corso/pkg/backup"
|
"github.com/alcionai/corso/pkg/backup"
|
||||||
"github.com/alcionai/corso/pkg/logger"
|
"github.com/alcionai/corso/pkg/logger"
|
||||||
"github.com/alcionai/corso/pkg/repository"
|
"github.com/alcionai/corso/pkg/repository"
|
||||||
@ -134,6 +135,11 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
|
|||||||
"",
|
"",
|
||||||
"Select backup details where the email subject lines contain this value",
|
"Select backup details where the email subject lines contain this value",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
case deleteCommand:
|
||||||
|
c, fs = utils.AddCommand(parent, exchangeDeleteCmd())
|
||||||
|
fs.StringVar(&backupID, "backup", "", "ID of the backup containing the details to be shown")
|
||||||
|
cobra.CheckErr(c.MarkFlagRequired("backup"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c
|
return c
|
||||||
@ -506,3 +512,54 @@ func validateExchangeBackupDetailFlags(
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// backup delete
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// `corso backup delete exchange [<flag>...]`
|
||||||
|
func exchangeDeleteCmd() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: exchangeServiceCommand,
|
||||||
|
Short: "Delete backed-up M365 Exchange service data",
|
||||||
|
RunE: deleteExchangeCmd,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes an exchange service backup.
|
||||||
|
func deleteExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
if utils.HasNoFlagsAndShownHelp(cmd) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, acct, err := config.GetStorageAndAccount(ctx, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m365, err := acct.M365Config()
|
||||||
|
if err != nil {
|
||||||
|
return Only(ctx, errors.Wrap(err, "Failed to parse m365 account config"))
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Ctx(ctx).Debugw(
|
||||||
|
"Called - "+cmd.CommandPath(),
|
||||||
|
"tenantID", m365.TenantID,
|
||||||
|
"clientID", m365.ClientID,
|
||||||
|
"hasClientSecret", len(m365.ClientSecret) > 0)
|
||||||
|
|
||||||
|
r, err := repository.Connect(ctx, acct, s)
|
||||||
|
if err != nil {
|
||||||
|
return Only(ctx, errors.Wrapf(err, "Failed to connect to the %s repository", s.Provider))
|
||||||
|
}
|
||||||
|
defer utils.CloseRepo(ctx, r)
|
||||||
|
|
||||||
|
if err := r.DeleteBackup(ctx, model.StableID(backupID)); err != nil {
|
||||||
|
return Only(ctx, errors.Wrapf(err, "Deleting backup %s", backupID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -217,3 +218,106 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// tests for deleting backups
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type BackupDeleteExchangeIntegrationSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
acct account.Account
|
||||||
|
st storage.Storage
|
||||||
|
vpr *viper.Viper
|
||||||
|
cfgFP string
|
||||||
|
repo *repository.Repository
|
||||||
|
backupOp operations.BackupOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackupDeleteExchangeIntegrationSuite(t *testing.T) {
|
||||||
|
if err := tester.RunOnAny(
|
||||||
|
tester.CorsoCITests,
|
||||||
|
tester.CorsoCLITests,
|
||||||
|
tester.CorsoCLIBackupTests,
|
||||||
|
); err != nil {
|
||||||
|
t.Skip(err)
|
||||||
|
}
|
||||||
|
suite.Run(t, new(BackupDeleteExchangeIntegrationSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BackupDeleteExchangeIntegrationSuite) SetupSuite() {
|
||||||
|
t := suite.T()
|
||||||
|
_, err := tester.GetRequiredEnvSls(
|
||||||
|
tester.AWSStorageCredEnvs,
|
||||||
|
tester.M365AcctCredEnvs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// prepare common details
|
||||||
|
suite.acct = tester.NewM365Account(t)
|
||||||
|
suite.st = tester.NewPrefixedS3Storage(t)
|
||||||
|
|
||||||
|
cfg, err := suite.st.S3Config()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
force := map[string]string{
|
||||||
|
tester.TestCfgAccountProvider: "M365",
|
||||||
|
tester.TestCfgStorageProvider: "S3",
|
||||||
|
tester.TestCfgPrefix: cfg.Prefix,
|
||||||
|
}
|
||||||
|
suite.vpr, suite.cfgFP, err = tester.MakeTempTestConfigClone(t, force)
|
||||||
|
require.NoError(t, err)
|
||||||
|
ctx := config.SetViper(tester.NewContext(), suite.vpr)
|
||||||
|
|
||||||
|
// init the repo first
|
||||||
|
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
m365UserID := tester.M365UserID(t)
|
||||||
|
|
||||||
|
// some tests require an existing backup
|
||||||
|
sel := selectors.NewExchangeBackup()
|
||||||
|
sel.Include(sel.MailFolders([]string{m365UserID}, []string{"Inbox"}))
|
||||||
|
|
||||||
|
suite.backupOp, err = suite.repo.NewBackup(
|
||||||
|
ctx,
|
||||||
|
sel.Selector,
|
||||||
|
control.NewOptions(false))
|
||||||
|
require.NoError(t, suite.backupOp.Run(ctx))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BackupDeleteExchangeIntegrationSuite) TestExchangeBackupDeleteCmd() {
|
||||||
|
ctx := config.SetViper(tester.NewContext(), suite.vpr)
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
cmd := tester.StubRootCmd(
|
||||||
|
"backup", "delete", "exchange",
|
||||||
|
"--config-file", suite.cfgFP,
|
||||||
|
"--backup", string(suite.backupOp.Results.BackupID))
|
||||||
|
cli.BuildCommandTree(cmd)
|
||||||
|
|
||||||
|
// run the command
|
||||||
|
require.NoError(t, cmd.ExecuteContext(ctx))
|
||||||
|
|
||||||
|
// a follow-up details call should fail, due to the backup ID being deleted
|
||||||
|
cmd = tester.StubRootCmd(
|
||||||
|
"backup", "details", "exchange",
|
||||||
|
"--config-file", suite.cfgFP,
|
||||||
|
"--backup", string(suite.backupOp.Results.BackupID))
|
||||||
|
cli.BuildCommandTree(cmd)
|
||||||
|
|
||||||
|
require.Error(t, cmd.ExecuteContext(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BackupDeleteExchangeIntegrationSuite) TestExchangeBackupDeleteCmd_UnknownID() {
|
||||||
|
ctx := config.SetViper(tester.NewContext(), suite.vpr)
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
cmd := tester.StubRootCmd(
|
||||||
|
"backup", "delete", "exchange",
|
||||||
|
"--config-file", suite.cfgFP,
|
||||||
|
"--backup", uuid.NewString())
|
||||||
|
cli.BuildCommandTree(cmd)
|
||||||
|
|
||||||
|
// unknown backupIDs should error since the modelStore can't find the backup
|
||||||
|
require.Error(t, cmd.ExecuteContext(ctx))
|
||||||
|
}
|
||||||
|
|||||||
@ -33,6 +33,7 @@ func (suite *ExchangeSuite) TestAddExchangeCommands() {
|
|||||||
{"create exchange", createCommand, expectUse, exchangeCreateCmd().Short, createExchangeCmd},
|
{"create exchange", createCommand, expectUse, exchangeCreateCmd().Short, createExchangeCmd},
|
||||||
{"list exchange", listCommand, expectUse, exchangeListCmd().Short, listExchangeCmd},
|
{"list exchange", listCommand, expectUse, exchangeListCmd().Short, listExchangeCmd},
|
||||||
{"details exchange", detailsCommand, expectUse, exchangeDetailsCmd().Short, detailsExchangeCmd},
|
{"details exchange", detailsCommand, expectUse, exchangeDetailsCmd().Short, detailsExchangeCmd},
|
||||||
|
{"delete exchange", deleteCommand, expectUse, exchangeDeleteCmd().Short, deleteExchangeCmd},
|
||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.T().Run(test.name, func(t *testing.T) {
|
suite.T().Run(test.name, func(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user