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:
parent
1cbb34e403
commit
5ee4ceb5a6
@ -119,7 +119,11 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
|
|||||||
options.AddOperationFlags(c)
|
options.AddOperationFlags(c)
|
||||||
|
|
||||||
case listCommand:
|
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:
|
case detailsCommand:
|
||||||
c, fs = utils.AddCommand(parent, exchangeDetailsCmd())
|
c, fs = utils.AddCommand(parent, exchangeDetailsCmd())
|
||||||
@ -350,6 +354,21 @@ func listExchangeCmd(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
defer utils.CloseRepo(ctx, r)
|
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)
|
bs, err := r.Backups(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, errors.Wrap(err, "Failed to list backups in the repository"))
|
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)
|
d, _, err := r.BackupDetails(ctx, backupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, kopia.ErrNotFound) {
|
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")
|
return nil, errors.Wrap(err, "Failed to get backup details in the repository")
|
||||||
|
|||||||
@ -140,7 +140,8 @@ type PreparedBackupExchangeIntegrationSuite struct {
|
|||||||
cfgFP string
|
cfgFP string
|
||||||
repo repository.Repository
|
repo repository.Repository
|
||||||
m365UserID string
|
m365UserID string
|
||||||
backupOps map[path.CategoryType]operations.BackupOperation
|
backupOps map[path.CategoryType]string
|
||||||
|
recorder strings.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPreparedBackupExchangeIntegrationSuite(t *testing.T) {
|
func TestPreparedBackupExchangeIntegrationSuite(t *testing.T) {
|
||||||
@ -165,6 +166,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
|
|||||||
// prepare common details
|
// prepare common details
|
||||||
suite.acct = tester.NewM365Account(t)
|
suite.acct = tester.NewM365Account(t)
|
||||||
suite.st = tester.NewPrefixedS3Storage(t)
|
suite.st = tester.NewPrefixedS3Storage(t)
|
||||||
|
suite.recorder = strings.Builder{}
|
||||||
|
|
||||||
cfg, err := suite.st.S3Config()
|
cfg, err := suite.st.S3Config()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -188,7 +190,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
|
|||||||
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
suite.backupOps = make(map[path.CategoryType]operations.BackupOperation)
|
suite.backupOps = make(map[path.CategoryType]string)
|
||||||
|
|
||||||
for _, set := range backupDataSets {
|
for _, set := range backupDataSets {
|
||||||
var (
|
var (
|
||||||
@ -213,21 +215,23 @@ func (suite *PreparedBackupExchangeIntegrationSuite) SetupSuite() {
|
|||||||
require.NoError(t, bop.Run(ctx))
|
require.NoError(t, bop.Run(ctx))
|
||||||
require.NoError(t, err)
|
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
|
// 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")
|
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.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() {
|
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
|
||||||
recorder := strings.Builder{}
|
|
||||||
|
|
||||||
for _, set := range backupDataSets {
|
for _, set := range backupDataSets {
|
||||||
recorder.Reset()
|
suite.recorder.Reset()
|
||||||
|
|
||||||
suite.T().Run(set.String(), func(t *testing.T) {
|
suite.T().Run(set.String(), func(t *testing.T) {
|
||||||
ctx, flush := tester.NewContext()
|
ctx, flush := tester.NewContext()
|
||||||
@ -239,7 +243,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
|
|||||||
"--config-file", suite.cfgFP)
|
"--config-file", suite.cfgFP)
|
||||||
cli.BuildCommandTree(cmd)
|
cli.BuildCommandTree(cmd)
|
||||||
|
|
||||||
cmd.SetOut(&recorder)
|
cmd.SetOut(&suite.recorder)
|
||||||
|
|
||||||
ctx = print.SetRootCmd(ctx, cmd)
|
ctx = print.SetRootCmd(ctx, cmd)
|
||||||
|
|
||||||
@ -247,24 +251,74 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd() {
|
|||||||
require.NoError(t, cmd.ExecuteContext(ctx))
|
require.NoError(t, cmd.ExecuteContext(ctx))
|
||||||
|
|
||||||
// compare the output
|
// compare the output
|
||||||
result := recorder.String()
|
result := suite.recorder.String()
|
||||||
assert.Contains(t, result, suite.backupOps[set].Results.BackupID)
|
assert.Contains(t, result, suite.backupOps[set])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
|
func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeListCmd_singleID() {
|
||||||
recorder := strings.Builder{}
|
|
||||||
|
|
||||||
for _, set := range backupDataSets {
|
for _, set := range backupDataSets {
|
||||||
recorder.Reset()
|
suite.recorder.Reset()
|
||||||
|
|
||||||
suite.T().Run(set.String(), func(t *testing.T) {
|
suite.T().Run(set.String(), func(t *testing.T) {
|
||||||
ctx, flush := tester.NewContext()
|
ctx, flush := tester.NewContext()
|
||||||
ctx = config.SetViper(ctx, suite.vpr)
|
ctx = config.SetViper(ctx, suite.vpr)
|
||||||
defer flush()
|
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
|
// fetch the details from the repo first
|
||||||
deets, _, err := suite.repo.BackupDetails(ctx, string(bID))
|
deets, _, err := suite.repo.BackupDetails(ctx, string(bID))
|
||||||
@ -276,7 +330,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
|
|||||||
"--backup", string(bID))
|
"--backup", string(bID))
|
||||||
cli.BuildCommandTree(cmd)
|
cli.BuildCommandTree(cmd)
|
||||||
|
|
||||||
cmd.SetOut(&recorder)
|
cmd.SetOut(&suite.recorder)
|
||||||
|
|
||||||
ctx = print.SetRootCmd(ctx, cmd)
|
ctx = print.SetRootCmd(ctx, cmd)
|
||||||
|
|
||||||
@ -284,7 +338,7 @@ func (suite *PreparedBackupExchangeIntegrationSuite) TestExchangeDetailsCmd() {
|
|||||||
require.NoError(t, cmd.ExecuteContext(ctx))
|
require.NoError(t, cmd.ExecuteContext(ctx))
|
||||||
|
|
||||||
// compare the output
|
// compare the output
|
||||||
result := recorder.String()
|
result := suite.recorder.String()
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
foundFolders := 0
|
foundFolders := 0
|
||||||
|
|||||||
@ -84,7 +84,11 @@ func addOneDriveCommands(parent *cobra.Command) *cobra.Command {
|
|||||||
options.AddOperationFlags(c)
|
options.AddOperationFlags(c)
|
||||||
|
|
||||||
case listCommand:
|
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:
|
case detailsCommand:
|
||||||
c, fs = utils.AddCommand(parent, oneDriveDetailsCmd())
|
c, fs = utils.AddCommand(parent, oneDriveDetailsCmd())
|
||||||
@ -246,6 +250,21 @@ func listOneDriveCmd(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
defer utils.CloseRepo(ctx, r)
|
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)
|
bs, err := r.Backups(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, errors.Wrap(err, "Failed to list backups in the repository"))
|
return Only(ctx, errors.Wrap(err, "Failed to list backups in the repository"))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user