diff --git a/src/cli/repo/s3.go b/src/cli/repo/s3.go index 2baa7f52f..11fbb7d1c 100644 --- a/src/cli/repo/s3.go +++ b/src/cli/repo/s3.go @@ -9,6 +9,7 @@ import ( "github.com/alcionai/corso/cli/config" "github.com/alcionai/corso/cli/utils" + "github.com/alcionai/corso/internal/kopia" "github.com/alcionai/corso/pkg/account" "github.com/alcionai/corso/pkg/credentials" "github.com/alcionai/corso/pkg/logger" @@ -18,10 +19,11 @@ import ( // s3 bucket info from flags var ( - accessKey string - bucket string - endpoint string - prefix string + accessKey string + bucket string + endpoint string + prefix string + succeedIfExists bool ) // called by repo.go to map parent subcommands to provider-specific handling. @@ -41,6 +43,10 @@ func addS3Commands(parent *cobra.Command) *cobra.Command { cobra.CheckErr(c.MarkFlagRequired("bucket")) fs.StringVar(&endpoint, "endpoint", "s3.amazonaws.com", "Server endpoint for S3 communication.") fs.StringVar(&prefix, "prefix", "", "Prefix applied to objects in the bucket.") + fs.BoolVar(&succeedIfExists, "succeed-if-exists", false, "Exit with success if the repo has already been initialized.") + // In general, we don't want to expose this flag to users and have them mistake it + // for a broad-scale idempotency solution. We can un-hide it later the need arises. + cobra.CheckErr(fs.MarkHidden("succeed-if-exists")) return c } @@ -88,6 +94,9 @@ func initS3Cmd(cmd *cobra.Command, args []string) error { r, err := repository.Initialize(ctx, a, s) if err != nil { + if succeedIfExists && kopia.IsRepoAlreadyExistsError(err) { + return nil + } return errors.Wrap(err, "Failed to initialize a new S3 repository") } defer utils.CloseRepo(ctx, r) diff --git a/src/internal/kopia/conn.go b/src/internal/kopia/conn.go index 600a5c041..261ec245a 100644 --- a/src/internal/kopia/conn.go +++ b/src/internal/kopia/conn.go @@ -8,6 +8,7 @@ import ( "github.com/kopia/kopia/repo/blob" "github.com/pkg/errors" + "github.com/alcionai/corso/internal/common" "github.com/alcionai/corso/pkg/storage" ) @@ -20,6 +21,19 @@ var ( errConnect = errors.New("connecting repo") ) +type ErrorRepoAlreadyExists struct { + common.Err +} + +func RepoAlreadyExistsError(e error) error { + return ErrorRepoAlreadyExists{*common.EncapsulateError(e)} +} + +func IsRepoAlreadyExistsError(e error) bool { + var erae ErrorRepoAlreadyExists + return errors.As(e, &erae) +} + type conn struct { storage storage.Storage repo.Repository @@ -47,6 +61,9 @@ func (w *conn) Initialize(ctx context.Context) error { // todo - issue #75: nil here should be a storage.NewRepoOptions() if err = repo.Initialize(ctx, bst, nil, cfg.CorsoPassword); err != nil { + if errors.Is(err, repo.ErrAlreadyInitialized) { + return RepoAlreadyExistsError(err) + } return errors.Wrap(err, errInit.Error()) }