Compare commits
6 Commits
main
...
completeKo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
328a84ed9f | ||
|
|
a32bcfdb87 | ||
|
|
585a394367 | ||
|
|
a8001f2aa9 | ||
|
|
1b1ddcc713 | ||
|
|
ae66673492 |
@ -12,18 +12,20 @@ const (
|
||||
AWSSessionTokenFN = "aws-session-token"
|
||||
|
||||
// Corso Flags
|
||||
CorsoPassphraseFN = "passphrase"
|
||||
SucceedIfExistsFN = "succeed-if-exists"
|
||||
CorsoPassphraseFN = "passphrase"
|
||||
UpdateCorsoPassphraseFN = "update-passphrase"
|
||||
SucceedIfExistsFN = "succeed-if-exists"
|
||||
)
|
||||
|
||||
var (
|
||||
BackupIDFV string
|
||||
BackupIDsFV []string
|
||||
AWSAccessKeyFV string
|
||||
AWSSecretAccessKeyFV string
|
||||
AWSSessionTokenFV string
|
||||
CorsoPassphraseFV string
|
||||
SucceedIfExistsFV bool
|
||||
BackupIDFV string
|
||||
BackupIDsFV []string
|
||||
AWSAccessKeyFV string
|
||||
AWSSecretAccessKeyFV string
|
||||
AWSSessionTokenFV string
|
||||
CorsoPassphraseFV string
|
||||
UpdateCorsoPhasephraseFV string
|
||||
SucceedIfExistsFV bool
|
||||
)
|
||||
|
||||
// AddMultipleBackupIDsFlag adds the --backups flag.
|
||||
@ -73,6 +75,15 @@ func AddCorsoPassphaseFlags(cmd *cobra.Command) {
|
||||
"Passphrase to protect encrypted repository contents")
|
||||
}
|
||||
|
||||
// M365 flags
|
||||
func AddCorsoUpdatePassphraseFlags(cmd *cobra.Command) {
|
||||
fs := cmd.Flags()
|
||||
fs.StringVar(&UpdateCorsoPhasephraseFV,
|
||||
UpdateCorsoPassphraseFN,
|
||||
"",
|
||||
"update Corso passphrase for repo")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Provider
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
const (
|
||||
initCommand = "init"
|
||||
connectCommand = "connect"
|
||||
updateCommand = "update"
|
||||
maintenanceCommand = "maintenance"
|
||||
)
|
||||
|
||||
@ -34,12 +35,14 @@ func AddCommands(cmd *cobra.Command) {
|
||||
initCmd = initCmd()
|
||||
connectCmd = connectCmd()
|
||||
maintenanceCmd = maintenanceCmd()
|
||||
updateCmd = updateCmd()
|
||||
)
|
||||
|
||||
cmd.AddCommand(repoCmd)
|
||||
repoCmd.AddCommand(initCmd)
|
||||
repoCmd.AddCommand(connectCmd)
|
||||
repoCmd.AddCommand(maintenanceCmd)
|
||||
repoCmd.AddCommand(updateCmd)
|
||||
|
||||
flags.AddMaintenanceModeFlag(maintenanceCmd)
|
||||
flags.AddForceMaintenanceFlag(maintenanceCmd)
|
||||
@ -50,6 +53,8 @@ func AddCommands(cmd *cobra.Command) {
|
||||
addRepoTo(initCmd)
|
||||
addRepoTo(connectCmd)
|
||||
}
|
||||
|
||||
addS3Commands(updateCmd)
|
||||
}
|
||||
|
||||
// The repo category of commands.
|
||||
@ -58,7 +63,7 @@ func repoCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "repo",
|
||||
Short: "Manage your repositories",
|
||||
Long: `Initialize, configure, and connect to your account backup repositories.`,
|
||||
Long: `Initialize, configure, connect and update to your account backup repositories.`,
|
||||
RunE: handleRepoCmd,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
@ -170,3 +175,20 @@ func getMaintenanceType(t string) (repository.MaintenanceType, error) {
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// The repo update subcommand.
|
||||
// `corso repo update <repository> [<flag>...]`
|
||||
func updateCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: updateCommand,
|
||||
Short: "Update a repository.",
|
||||
Long: `Update repository configuration and behavior.`,
|
||||
RunE: handleUpdateCmd,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for calls to `corso repo init`.
|
||||
func handleUpdateCmd(cmd *cobra.Command, args []string) error {
|
||||
return cmd.Help()
|
||||
}
|
||||
|
||||
@ -28,6 +28,11 @@ func addS3Commands(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
case connectCommand:
|
||||
c, _ = utils.AddCommand(cmd, s3ConnectCmd())
|
||||
|
||||
case updateCommand:
|
||||
update := s3UpdateCmd()
|
||||
flags.AddCorsoUpdatePassphraseFlags(update)
|
||||
c, _ = utils.AddCommand(cmd, update)
|
||||
}
|
||||
|
||||
c.Use = c.Use + " " + s3ProviderCommandUseSuffix
|
||||
@ -239,3 +244,60 @@ func connectS3Cmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
// Update Password
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
// `corso repo update s3 [<flag>...]`
|
||||
func s3UpdateCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: s3ProviderCommand,
|
||||
Short: "Update to a S3 repository",
|
||||
Long: `Update to an existing S3 repository.`,
|
||||
RunE: updateS3Cmd,
|
||||
Args: cobra.NoArgs,
|
||||
Example: s3ProviderCommandConnectExamples,
|
||||
}
|
||||
}
|
||||
|
||||
// updates to an existing s3 repo.
|
||||
// currently just updating Kopia password
|
||||
func updateS3Cmd(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Context()
|
||||
|
||||
cfg, err := config.GetConfigRepoDetails(
|
||||
ctx,
|
||||
storage.ProviderS3,
|
||||
true,
|
||||
true,
|
||||
flags.S3FlagOverrides(cmd))
|
||||
if err != nil {
|
||||
return Only(ctx, err)
|
||||
}
|
||||
|
||||
repoID := cfg.RepoID
|
||||
if len(repoID) == 0 {
|
||||
repoID = events.RepoIDNotFound
|
||||
}
|
||||
|
||||
opts := utils.ControlWithConfig(cfg)
|
||||
|
||||
r, err := repository.New(
|
||||
ctx,
|
||||
cfg.Account,
|
||||
cfg.Storage,
|
||||
opts,
|
||||
repoID)
|
||||
if err != nil {
|
||||
return Only(ctx, clues.Wrap(err, "Failed to create a repository controller"))
|
||||
}
|
||||
|
||||
if err := r.UpdatePassword(ctx, flags.UpdateCorsoPhasephraseFV); err != nil {
|
||||
return Only(ctx, clues.Wrap(err, "Failed to update s3"))
|
||||
}
|
||||
|
||||
Infof(ctx, "Updated repo password.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -289,3 +289,80 @@ func (suite *S3E2ESuite) TestConnectS3Cmd_BadPrefix() {
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.Error(t, err, clues.ToCore(err))
|
||||
}
|
||||
|
||||
func (suite *S3E2ESuite) TestUpdateS3Cmd() {
|
||||
t := suite.T()
|
||||
ctx, flush := tester.NewContext(t)
|
||||
|
||||
defer flush()
|
||||
|
||||
st := storeTD.NewPrefixedS3Storage(t)
|
||||
sc, err := st.StorageConfig()
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
cfg := sc.(*storage.S3Config)
|
||||
|
||||
vpr, configFP := tconfig.MakeTempTestConfigClone(t, nil)
|
||||
|
||||
ctx = config.SetViper(ctx, vpr)
|
||||
|
||||
cmd := cliTD.StubRootCmd(
|
||||
"repo", "init", "s3",
|
||||
"--config-file", configFP,
|
||||
"--prefix", cfg.Prefix)
|
||||
|
||||
cli.BuildCommandTree(cmd)
|
||||
|
||||
// run the command
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// connect with old passphrase
|
||||
cmd = cliTD.StubRootCmd(
|
||||
"repo", "connect", "s3",
|
||||
"--config-file", configFP,
|
||||
"--bucket", cfg.Bucket,
|
||||
"--prefix", cfg.Prefix)
|
||||
cli.BuildCommandTree(cmd)
|
||||
|
||||
// run the command
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
cmd = cliTD.StubRootCmd(
|
||||
"repo", "update", "s3",
|
||||
"--config-file", configFP,
|
||||
"--bucket", cfg.Bucket,
|
||||
"--prefix", cfg.Prefix,
|
||||
"--update-passphrase", "newpass")
|
||||
cli.BuildCommandTree(cmd)
|
||||
|
||||
// run the command
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// connect again with new passphrase
|
||||
cmd = cliTD.StubRootCmd(
|
||||
"repo", "connect", "s3",
|
||||
"--config-file", configFP,
|
||||
"--bucket", cfg.Bucket,
|
||||
"--prefix", cfg.Prefix,
|
||||
"--passphrase", "newpass")
|
||||
cli.BuildCommandTree(cmd)
|
||||
|
||||
// run the command
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// connect with old passphrase - it will fail
|
||||
cmd = cliTD.StubRootCmd(
|
||||
"repo", "connect", "s3",
|
||||
"--config-file", configFP,
|
||||
"--bucket", cfg.Bucket,
|
||||
"--prefix", cfg.Prefix)
|
||||
cli.BuildCommandTree(cmd)
|
||||
|
||||
// run the command
|
||||
err = cmd.ExecuteContext(ctx)
|
||||
require.Error(t, err, clues.ToCore(err))
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ func (suite *S3Suite) TestAddS3Commands() {
|
||||
}{
|
||||
{"init s3", initCommand, expectUse, s3InitCmd().Short, initS3Cmd},
|
||||
{"connect s3", connectCommand, expectUse, s3ConnectCmd().Short, connectS3Cmd},
|
||||
{"update s3", updateCommand, expectUse, s3UpdateCmd().Short, updateS3Cmd},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
|
||||
@ -31,6 +31,7 @@ const (
|
||||
CorsoStart = "Corso Start"
|
||||
RepoInit = "Repo Init"
|
||||
RepoConnect = "Repo Connect"
|
||||
RepoUpdate = "Repo Update"
|
||||
BackupStart = "Backup Start"
|
||||
BackupEnd = "Backup End"
|
||||
RestoreStart = "Restore Start"
|
||||
|
||||
@ -578,3 +578,17 @@ func (w *conn) LoadSnapshot(
|
||||
func (w *conn) SnapshotRoot(man *snapshot.Manifest) (fs.Entry, error) {
|
||||
return snapshotfs.SnapshotRoot(w.Repository, man)
|
||||
}
|
||||
|
||||
func (w *conn) UpdatePassword(ctx context.Context, password string, opts repository.Options) error {
|
||||
kopiaRef := NewConn(w.storage)
|
||||
if err := kopiaRef.Connect(ctx, opts); err != nil {
|
||||
return clues.Wrap(err, "connecting kopia client")
|
||||
}
|
||||
|
||||
defer kopiaRef.Close(ctx)
|
||||
|
||||
repository := kopiaRef.Repository.(repo.DirectRepository)
|
||||
err := repository.FormatManager().ChangePassword(ctx, password)
|
||||
|
||||
return errors.Wrap(err, "unable to update password")
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ func New(
|
||||
s storage.Storage,
|
||||
opts control.Options,
|
||||
configFileRepoID string,
|
||||
) (repo *repository, err error) {
|
||||
) (singleRepo *repository, err error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"acct_provider", acct.Provider.String(),
|
||||
@ -272,6 +272,41 @@ func (r *repository) Connect(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdatePassword will-
|
||||
// - connect to the provider storage using existing password
|
||||
// - update the repo with new password
|
||||
func (r *repository) UpdatePassword(ctx context.Context, password string) (err error) {
|
||||
ctx = clues.Add(
|
||||
ctx,
|
||||
"acct_provider", r.Account.Provider.String(),
|
||||
"acct_id", clues.Hide(r.Account.ID()),
|
||||
"storage_provider", r.Storage.Provider.String())
|
||||
|
||||
defer func() {
|
||||
if crErr := crash.Recovery(ctx, recover(), "repo connect"); crErr != nil {
|
||||
err = crErr
|
||||
}
|
||||
}()
|
||||
|
||||
progressBar := observe.MessageWithCompletion(ctx, "Connecting to repository")
|
||||
defer close(progressBar)
|
||||
|
||||
kopiaRef := kopia.NewConn(r.Storage)
|
||||
if err := kopiaRef.Connect(ctx, r.Opts.Repo); err != nil {
|
||||
return clues.Wrap(err, "connecting kopia client")
|
||||
}
|
||||
|
||||
if err := kopiaRef.UpdatePassword(ctx, password, r.Opts.Repo); err != nil {
|
||||
return clues.Wrap(err, "updating on kopia")
|
||||
}
|
||||
|
||||
defer kopiaRef.Close(ctx)
|
||||
|
||||
r.Bus.Event(ctx, events.RepoUpdate, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) Close(ctx context.Context) error {
|
||||
if err := r.Bus.Close(); err != nil {
|
||||
logger.Ctx(ctx).With("err", err).Debugw("closing the event bus", clues.In(ctx).Slice()...)
|
||||
|
||||
@ -236,6 +236,37 @@ func (suite *RepositoryIntegrationSuite) TestConnect() {
|
||||
assert.NoError(t, err, clues.ToCore(err))
|
||||
}
|
||||
|
||||
func (suite *RepositoryIntegrationSuite) TestRepository_UpdatePassword() {
|
||||
t := suite.T()
|
||||
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
// need to initialize the repository before we can test connecting to it.
|
||||
st := storeTD.NewPrefixedS3Storage(t)
|
||||
r, err := New(
|
||||
ctx,
|
||||
account.Account{},
|
||||
st,
|
||||
control.DefaultOptions(),
|
||||
NewRepoID)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
err = r.Initialize(ctx, ctrlRepo.Retention{})
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// now re-connect
|
||||
err = r.Connect(ctx)
|
||||
assert.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
err = r.UpdatePassword(ctx, "newpass")
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
// now reconnect with new pass
|
||||
err = r.Connect(ctx)
|
||||
assert.Error(t, err, clues.ToCore(err))
|
||||
}
|
||||
|
||||
func (suite *RepositoryIntegrationSuite) TestConnect_sameID() {
|
||||
t := suite.T()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user