add --backup flag to list (#1117)

## Description

Allows listing individual backups, instead of always listing the entire set.  Could be expanded to accept multiple backup IDs in the future.

## Type of change

- [x] 🌻 Feature

## Issue(s)

* #1077

## Test Plan

- [x] 💪 Manual
- [x] 💚 E2E
This commit is contained in:
Keepers 2022-10-13 14:38:35 -06:00 committed by GitHub
parent 1cbb34e403
commit 5ee4ceb5a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 21 deletions

View File

@ -119,7 +119,11 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
options.AddOperationFlags(c)
case listCommand:
c, _ = utils.AddCommand(parent, exchangeListCmd())
c, fs = utils.AddCommand(parent, exchangeListCmd())
fs.StringVar(&backupID,
"backup", "",
"ID of the backup to retrieve.")
case detailsCommand:
c, fs = utils.AddCommand(parent, exchangeDetailsCmd())
@ -350,6 +354,21 @@ func listExchangeCmd(cmd *cobra.Command, args []string) error {
defer utils.CloseRepo(ctx, r)
if len(backupID) > 0 {
b, err := r.Backup(ctx, model.StableID(backupID))
if err != nil {
if errors.Is(err, kopia.ErrNotFound) {
return Only(ctx, errors.Errorf("No backup exists with the id %s", backupID))
}
return Only(ctx, errors.Wrap(err, "Failed to find backup "+backupID))
}
b.Print(ctx)
return nil
}
bs, err := r.Backups(ctx)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to list backups in the repository"))
@ -443,7 +462,7 @@ func runDetailsExchangeCmd(
d, _, err := r.BackupDetails(ctx, backupID)
if err != nil {
if errors.Is(err, kopia.ErrNotFound) {
return nil, errors.Errorf("no backup exists with the id %s", backupID)
return nil, errors.Errorf("No backup exists with the id %s", backupID)
}
return nil, errors.Wrap(err, "Failed to get backup details in the repository")

View File

@ -140,7 +140,8 @@ type PreparedBackupExchangeIntegrationSuite struct {
cfgFP string
repo repository.Repository
m365UserID string
backupOps map[path.CategoryType]operations.BackupOperation
backupOps map[path.CategoryType]string
recorder strings.Builder
}
func TestPreparedBackupExchangeIntegrationSuite(t *testing.T) {
@ -165,6 +166,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
// prepare common details
suite.acct = tester.NewM365Account(t)
suite.st = tester.NewPrefixedS3Storage(t)
suite.recorder = strings.Builder{}
cfg, err := suite.st.S3Config()
require.NoError(t, err)
@ -188,7 +190,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
require.NoError(t, err)
suite.backupOps = make(map[path.CategoryType]operations.BackupOperation)
suite.backupOps = make(map[path.CategoryType]string)
for _, set := range backupDataSets {
var (
@ -213,21 +215,23 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
require.NoError(t, bop.Run(ctx))
require.NoError(t, err)
suite.backupOps[set] = bop
bIDs := string(bop.Results.BackupID)
// sanity check, ensure we can find the backup and its details immediately
_, err = suite.repo.Backup(ctx, bop.Results.BackupID)
b, err := suite.repo.Backup(ctx, bop.Results.BackupID)
require.NoError(t, err, "retrieving recent backup by ID")
_, _, err = suite.repo.BackupDetails(ctx, string(bop.Results.BackupID))
require.Equal(t, bIDs, string(b.ID), "repo backup matches results id")
_, b, err = suite.repo.BackupDetails(ctx, bIDs)
require.NoError(t, err, "retrieving recent backup details by ID")
require.Equal(t, bIDs, string(b.ID), "repo details matches results id")
suite.backupOps[set] = string(b.ID)
}
}
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
recorder := strings.Builder{}
for _, set := range backupDataSets {
recorder.Reset()
suite.recorder.Reset()
suite.T().Run(set.String(), func(t *testing.T) {
ctx, flush := tester.NewContext()
@ -239,7 +243,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
"--config-file", suite.cfgFP)
cli.BuildCommandTree(cmd)
cmd.SetOut(&recorder)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
@ -247,24 +251,74 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
require.NoError(t, cmd.ExecuteContext(ctx))
// compare the output
result := recorder.String()
assert.Contains(t, result, suite.backupOps[set].Results.BackupID)
result := suite.recorder.String()
assert.Contains(t, result, suite.backupOps[set])
})
}
}
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
recorder := strings.Builder{}
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd_singleID() {
for _, set := range backupDataSets {
recorder.Reset()
suite.recorder.Reset()
suite.T().Run(set.String(), func(t *testing.T) {
ctx, flush := tester.NewContext()
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
bID := suite.backupOps[set].Results.BackupID
bID := suite.backupOps[set]
cmd := tester.StubRootCmd(
"backup", "list", "exchange",
"--config-file", suite.cfgFP,
"--backup", string(bID))
cli.BuildCommandTree(cmd)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
require.NoError(t, cmd.ExecuteContext(ctx))
// compare the output
result := suite.recorder.String()
assert.Contains(t, result, bID)
})
}
}
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd_badID() {
for _, set := range backupDataSets {
suite.T().Run(set.String(), func(t *testing.T) {
ctx, flush := tester.NewContext()
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
cmd := tester.StubRootCmd(
"backup", "list", "exchange",
"--config-file", suite.cfgFP,
"--backup", "smarfs")
cli.BuildCommandTree(cmd)
ctx = print.SetRootCmd(ctx, cmd)
// run the command
require.Error(t, cmd.ExecuteContext(ctx))
})
}
}
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
for _, set := range backupDataSets {
suite.recorder.Reset()
suite.T().Run(set.String(), func(t *testing.T) {
ctx, flush := tester.NewContext()
ctx = config.SetViper(ctx, suite.vpr)
defer flush()
bID := suite.backupOps[set]
// fetch the details from the repo first
deets, _, err := suite.repo.BackupDetails(ctx, string(bID))
@ -276,7 +330,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
"--backup", string(bID))
cli.BuildCommandTree(cmd)
cmd.SetOut(&recorder)
cmd.SetOut(&suite.recorder)
ctx = print.SetRootCmd(ctx, cmd)
@ -284,7 +338,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
require.NoError(t, cmd.ExecuteContext(ctx))
// compare the output
result := recorder.String()
result := suite.recorder.String()
i := 0
foundFolders := 0

View File

@ -84,7 +84,11 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
options.AddOperationFlags(c)
case listCommand:
c, _ = utils.AddCommand(parent, oneDriveListCmd())
c, fs = utils.AddCommand(parent, oneDriveListCmd())
fs.StringVar(&backupID,
"backup", "",
"ID of the backup to retrieve.")
case detailsCommand:
c, fs = utils.AddCommand(parent, oneDriveDetailsCmd())
@ -246,6 +250,21 @@ func listOneDriveCmd(cmd *cobra.Command, args []string) error {
defer utils.CloseRepo(ctx, r)
if len(backupID) > 0 {
b, err := r.Backup(ctx, model.StableID(backupID))
if err != nil {
if errors.Is(err, kopia.ErrNotFound) {
return Only(ctx, errors.Errorf("No backup exists with the id %s", backupID))
}
return Only(ctx, errors.Wrap(err, "Failed to find backup "+backupID))
}
b.Print(ctx)
return nil
}
bs, err := r.Backups(ctx)
if err != nil {
return Only(ctx, errors.Wrap(err, "Failed to list backups in the repository"))