Remove tight CLI coupling with S3 storage provider (#4237)

<!-- PR description-->

One of the 2 remaining setup PRs before we can introduce local storage repos.

**Changes:**
1. Read storage provider(`provider`) from config file except for `repo init * ` or `repo connect *` commands.
2. Apply flag overrides based on provider type( e.g. `S3FlagOverrides` if provider is `S3`)
3. Propagate storage provider type to functions which read/write config. These functions arbitrate on config hierarchy - flags, env, config file, in that order. 

**Reasons**
* Reason 1 is needed is because config file is the source of truth for storage provider for all commands except `repo init` or `repo connect`. In the exception cases, we pick the provider in command (e.g. `s3`) as the source of truth. e.g. consider a `repo init s3`, followed by `repo init filesystem`.During `repo init filesystem`, config file would indicate `S3` provider, but the correct behavior here is to select `filesystem` provider.
* One alternative was to push provider from the init/connect cmds into an override flag, and let the config code decide on hierarchy. However, this felt hacky. provider here is not a flag to begin with. It's part of init/connect commands.


---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [x] 🕐 Yes, but in a later PR
- [ ]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* https://github.com/alcionai/corso/issues/1416

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
This commit is contained in:
Abhishek Pandey 2023-09-18 18:31:00 +05:30 committed by GitHub
parent 471a0fa35a
commit fb6398ea41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 288 additions and 137 deletions

View File

@ -12,7 +12,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common/idname" "github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
@ -290,7 +289,10 @@ func genericDeleteCommand(
ctx := clues.Add(cmd.Context(), "delete_backup_id", bID) ctx := clues.Add(cmd.Context(), "delete_backup_id", bID)
r, _, _, _, err := utils.GetAccountAndConnect(ctx, pst, repo.S3Overrides(cmd)) r, _, _, _, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
pst)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -316,7 +318,10 @@ func genericListCommand(
) error { ) error {
ctx := cmd.Context() ctx := cmd.Context()
r, _, _, _, err := utils.GetAccountAndConnect(ctx, service, repo.S3Overrides(cmd)) r, _, _, _, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
service)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
@ -168,7 +167,10 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
return err return err
} }
r, acct, err := utils.AccountConnectAndWriteRepoConfig(ctx, path.ExchangeService, repo.S3Overrides(cmd)) r, acct, err := utils.AccountConnectAndWriteRepoConfig(
ctx,
cmd,
path.ExchangeService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -277,7 +279,10 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
opts := utils.MakeExchangeOpts(cmd) opts := utils.MakeExchangeOpts(cmd)
r, _, _, ctrlOpts, err := utils.GetAccountAndConnect(ctx, path.ExchangeService, repo.S3Overrides(cmd)) r, _, _, ctrlOpts, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
path.ExchangeService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common/idname" "github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
@ -154,7 +153,10 @@ func createGroupsCmd(cmd *cobra.Command, args []string) error {
return err return err
} }
r, acct, err := utils.AccountConnectAndWriteRepoConfig(ctx, path.GroupsService, repo.S3Overrides(cmd)) r, acct, err := utils.AccountConnectAndWriteRepoConfig(
ctx,
cmd,
path.GroupsService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -226,7 +228,10 @@ func detailsGroupsCmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
opts := utils.MakeGroupsOpts(cmd) opts := utils.MakeGroupsOpts(cmd)
r, _, _, ctrlOpts, err := utils.GetAccountAndConnect(ctx, path.GroupsService, repo.S3Overrides(cmd)) r, _, _, ctrlOpts, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
path.GroupsService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
@ -149,7 +148,10 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error {
return err return err
} }
r, acct, err := utils.AccountConnectAndWriteRepoConfig(ctx, path.OneDriveService, repo.S3Overrides(cmd)) r, acct, err := utils.AccountConnectAndWriteRepoConfig(
ctx,
cmd,
path.OneDriveService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -235,7 +237,10 @@ func detailsOneDriveCmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
opts := utils.MakeOneDriveOpts(cmd) opts := utils.MakeOneDriveOpts(cmd)
r, _, _, ctrlOpts, err := utils.GetAccountAndConnect(ctx, path.OneDriveService, repo.S3Overrides(cmd)) r, _, _, ctrlOpts, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
path.OneDriveService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -11,7 +11,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common/idname" "github.com/alcionai/corso/src/internal/common/idname"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
@ -159,7 +158,10 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error {
return err return err
} }
r, acct, err := utils.AccountConnectAndWriteRepoConfig(ctx, path.SharePointService, repo.S3Overrides(cmd)) r, acct, err := utils.AccountConnectAndWriteRepoConfig(
ctx,
cmd,
path.SharePointService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -319,7 +321,10 @@ func detailsSharePointCmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
opts := utils.MakeSharePointOpts(cmd) opts := utils.MakeSharePointOpts(cmd)
r, _, _, ctrlOpts, err := utils.GetAccountAndConnect(ctx, path.SharePointService, repo.S3Overrides(cmd)) r, _, _, ctrlOpts, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
path.SharePointService)
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -64,15 +64,24 @@ func preRun(cc *cobra.Command, args []string) error {
avoidTheseDescription := []string{ avoidTheseDescription := []string{
"Initialize a repository.", "Initialize a repository.",
"Initialize a S3 repository", "Initialize a S3 repository",
"Connect to a S3 repository",
"Help about any command", "Help about any command",
"Free, Secure, Open-Source Backup for M365.", "Free, Secure, Open-Source Backup for M365.",
"env var guide", "env var guide",
} }
if !slices.Contains(avoidTheseDescription, cc.Short) { if !slices.Contains(avoidTheseDescription, cc.Short) {
overrides := repo.S3Overrides(cc) provider, overrides, err := utils.GetStorageProviderAndOverrides(ctx, cc)
if err != nil {
return err
}
cfg, err := config.GetConfigRepoDetails(ctx, true, false, overrides) cfg, err := config.GetConfigRepoDetails(
ctx,
provider,
true,
false,
overrides)
if err != nil { if err != nil {
log.Error("Error while getting config info to run command: ", cc.Use) log.Error("Error while getting config info to run command: ", cc.Use)
return err return err

View File

@ -16,17 +16,17 @@ import (
func m365ConfigsFromViper(vpr *viper.Viper) (account.M365Config, error) { func m365ConfigsFromViper(vpr *viper.Viper) (account.M365Config, error) {
var m365 account.M365Config var m365 account.M365Config
m365.AzureClientID = vpr.GetString(AzureClientID) m365.AzureClientID = vpr.GetString(account.AzureClientID)
m365.AzureClientSecret = vpr.GetString(AzureSecret) m365.AzureClientSecret = vpr.GetString(account.AzureSecret)
m365.AzureTenantID = vpr.GetString(AzureTenantIDKey) m365.AzureTenantID = vpr.GetString(account.AzureTenantIDKey)
return m365, nil return m365, nil
} }
func m365Overrides(in map[string]string) map[string]string { func m365Overrides(in map[string]string) map[string]string {
return map[string]string{ return map[string]string{
account.AzureTenantID: in[account.AzureTenantID], account.AzureTenantID: in[account.AzureTenantID],
AccountProviderTypeKey: in[AccountProviderTypeKey], account.AccountProviderTypeKey: in[account.AccountProviderTypeKey],
} }
} }
@ -52,7 +52,7 @@ func configureAccount(
} }
if matchFromConfig { if matchFromConfig {
providerType := vpr.GetString(AccountProviderTypeKey) providerType := vpr.GetString(account.AccountProviderTypeKey)
if providerType != account.ProviderM365.String() { if providerType != account.ProviderM365.String() {
return acct, clues.New("unsupported account provider: " + providerType) return acct, clues.New("unsupported account provider: " + providerType)
} }

View File

@ -21,7 +21,6 @@ import (
const ( const (
// S3 config // S3 config
StorageProviderTypeKey = "provider"
BucketNameKey = "bucket" BucketNameKey = "bucket"
EndpointKey = "endpoint" EndpointKey = "endpoint"
PrefixKey = "prefix" PrefixKey = "prefix"
@ -33,12 +32,6 @@ const (
SecretAccessKey = "aws_secret_access_key" SecretAccessKey = "aws_secret_access_key"
SessionToken = "aws_session_token" SessionToken = "aws_session_token"
// M365 config
AccountProviderTypeKey = "account_provider"
AzureTenantIDKey = "azure_tenantid"
AzureClientID = "azure_client_id"
AzureSecret = "azure_secret"
// Corso passphrase in config // Corso passphrase in config
CorsoPassphrase = "passphrase" CorsoPassphrase = "passphrase"
CorsoUser = "corso_user" CorsoUser = "corso_user"
@ -228,7 +221,7 @@ func writeRepoConfigWithViper(
s3Config = s3Config.Normalize() s3Config = s3Config.Normalize()
// Rudimentary support for persisting repo config // Rudimentary support for persisting repo config
// TODO: Handle conflicts, support other config types // TODO: Handle conflicts, support other config types
vpr.Set(StorageProviderTypeKey, storage.ProviderS3.String()) vpr.Set(storage.StorageProviderTypeKey, storage.ProviderS3.String())
vpr.Set(BucketNameKey, s3Config.Bucket) vpr.Set(BucketNameKey, s3Config.Bucket)
vpr.Set(EndpointKey, s3Config.Endpoint) vpr.Set(EndpointKey, s3Config.Endpoint)
vpr.Set(PrefixKey, s3Config.Prefix) vpr.Set(PrefixKey, s3Config.Prefix)
@ -245,8 +238,8 @@ func writeRepoConfigWithViper(
vpr.Set(CorsoHost, repoOpts.Host) vpr.Set(CorsoHost, repoOpts.Host)
} }
vpr.Set(AccountProviderTypeKey, account.ProviderM365.String()) vpr.Set(account.AccountProviderTypeKey, account.ProviderM365.String())
vpr.Set(AzureTenantIDKey, m365Config.AzureTenantID) vpr.Set(account.AzureTenantIDKey, m365Config.AzureTenantID)
if err := vpr.SafeWriteConfig(); err != nil { if err := vpr.SafeWriteConfig(); err != nil {
if _, ok := err.(viper.ConfigFileAlreadyExistsError); ok { if _, ok := err.(viper.ConfigFileAlreadyExistsError); ok {
@ -263,6 +256,7 @@ func writeRepoConfigWithViper(
// data sources (config file, env vars, flag overrides) and the config file. // data sources (config file, env vars, flag overrides) and the config file.
func GetConfigRepoDetails( func GetConfigRepoDetails(
ctx context.Context, ctx context.Context,
provider storage.ProviderType,
readFromFile bool, readFromFile bool,
mustMatchFromConfig bool, mustMatchFromConfig bool,
overrides map[string]string, overrides map[string]string,
@ -270,7 +264,13 @@ func GetConfigRepoDetails(
RepoDetails, RepoDetails,
error, error,
) { ) {
config, err := getStorageAndAccountWithViper(GetViper(ctx), readFromFile, mustMatchFromConfig, overrides) config, err := getStorageAndAccountWithViper(
GetViper(ctx),
provider,
readFromFile,
mustMatchFromConfig,
overrides)
return config, err return config, err
} }
@ -278,6 +278,7 @@ func GetConfigRepoDetails(
// struct for testing. // struct for testing.
func getStorageAndAccountWithViper( func getStorageAndAccountWithViper(
vpr *viper.Viper, vpr *viper.Viper,
provider storage.ProviderType,
readFromFile bool, readFromFile bool,
mustMatchFromConfig bool, mustMatchFromConfig bool,
overrides map[string]string, overrides map[string]string,
@ -312,7 +313,7 @@ func getStorageAndAccountWithViper(
return config, clues.Wrap(err, "retrieving account configuration details") return config, clues.Wrap(err, "retrieving account configuration details")
} }
config.Storage, err = configureStorage(vpr, readConfigFromViper, mustMatchFromConfig, overrides) config.Storage, err = configureStorage(vpr, provider, readConfigFromViper, mustMatchFromConfig, overrides)
if err != nil { if err != nil {
return config, clues.Wrap(err, "retrieving storage provider details") return config, clues.Wrap(err, "retrieving storage provider details")
} }
@ -336,12 +337,12 @@ func getUserHost(vpr *viper.Viper, readConfigFromViper bool) (string, string) {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
var constToTomlKeyMap = map[string]string{ var constToTomlKeyMap = map[string]string{
account.AzureTenantID: AzureTenantIDKey, account.AzureTenantID: account.AzureTenantIDKey,
AccountProviderTypeKey: AccountProviderTypeKey, account.AccountProviderTypeKey: account.AccountProviderTypeKey,
storage.Bucket: BucketNameKey, storage.Bucket: BucketNameKey,
storage.Endpoint: EndpointKey, storage.Endpoint: EndpointKey,
storage.Prefix: PrefixKey, storage.Prefix: PrefixKey,
StorageProviderTypeKey: StorageProviderTypeKey, storage.StorageProviderTypeKey: storage.StorageProviderTypeKey,
} }
// mustMatchConfig compares the values of each key to their config file value in viper. // mustMatchConfig compares the values of each key to their config file value in viper.

View File

@ -29,15 +29,15 @@ const (
` + BucketNameKey + ` = '%s' ` + BucketNameKey + ` = '%s'
` + EndpointKey + ` = 's3.amazonaws.com' ` + EndpointKey + ` = 's3.amazonaws.com'
` + PrefixKey + ` = 'test-prefix/' ` + PrefixKey + ` = 'test-prefix/'
` + StorageProviderTypeKey + ` = 'S3' ` + storage.StorageProviderTypeKey + ` = 'S3'
` + AccountProviderTypeKey + ` = 'M365' ` + account.AccountProviderTypeKey + ` = 'M365'
` + AzureTenantIDKey + ` = '%s' ` + account.AzureTenantIDKey + ` = '%s'
` + AccessKey + ` = '%s' ` + AccessKey + ` = '%s'
` + SecretAccessKey + ` = '%s' ` + SecretAccessKey + ` = '%s'
` + SessionToken + ` = '%s' ` + SessionToken + ` = '%s'
` + CorsoPassphrase + ` = '%s' ` + CorsoPassphrase + ` = '%s'
` + AzureClientID + ` = '%s' ` + account.AzureClientID + ` = '%s'
` + AzureSecret + ` = '%s' ` + account.AzureSecret + ` = '%s'
` + DisableTLSKey + ` = '%s' ` + DisableTLSKey + ` = '%s'
` + DisableTLSVerificationKey + ` = '%s' ` + DisableTLSVerificationKey + ` = '%s'
` `
@ -326,6 +326,7 @@ func (suite *ConfigSuite) TestReadFromFlags() {
repoDetails, err := getStorageAndAccountWithViper( repoDetails, err := getStorageAndAccountWithViper(
vpr, vpr,
storage.ProviderS3,
true, true,
false, false,
overrides) overrides)
@ -400,7 +401,7 @@ func (suite *ConfigIntegrationSuite) TestGetStorageAndAccount() {
err = vpr.ReadInConfig() err = vpr.ReadInConfig()
require.NoError(t, err, "reading repo config", clues.ToCore(err)) require.NoError(t, err, "reading repo config", clues.ToCore(err))
cfg, err := getStorageAndAccountWithViper(vpr, true, true, nil) cfg, err := getStorageAndAccountWithViper(vpr, storage.ProviderS3, true, true, nil)
require.NoError(t, err, "getting storage and account from config", clues.ToCore(err)) require.NoError(t, err, "getting storage and account from config", clues.ToCore(err))
readS3Cfg, err := cfg.Storage.S3Config() readS3Cfg, err := cfg.Storage.S3Config()
@ -438,17 +439,17 @@ func (suite *ConfigIntegrationSuite) TestGetStorageAndAccount_noFileOnlyOverride
m365 := account.M365Config{AzureTenantID: tid} m365 := account.M365Config{AzureTenantID: tid}
overrides := map[string]string{ overrides := map[string]string{
account.AzureTenantID: tid, account.AzureTenantID: tid,
AccountProviderTypeKey: account.ProviderM365.String(), account.AccountProviderTypeKey: account.ProviderM365.String(),
storage.Bucket: bkt, storage.Bucket: bkt,
storage.Endpoint: end, storage.Endpoint: end,
storage.Prefix: pfx, storage.Prefix: pfx,
storage.DoNotUseTLS: "true", storage.DoNotUseTLS: "true",
storage.DoNotVerifyTLS: "true", storage.DoNotVerifyTLS: "true",
StorageProviderTypeKey: storage.ProviderS3.String(), storage.StorageProviderTypeKey: storage.ProviderS3.String(),
} }
cfg, err := getStorageAndAccountWithViper(vpr, false, true, overrides) cfg, err := getStorageAndAccountWithViper(vpr, storage.ProviderS3, false, true, overrides)
require.NoError(t, err, "getting storage and account from config", clues.ToCore(err)) require.NoError(t, err, "getting storage and account from config", clues.ToCore(err))
readS3Cfg, err := cfg.Storage.S3Config() readS3Cfg, err := cfg.Storage.S3Config()

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"context"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -40,12 +41,12 @@ func s3CredsFromViper(vpr *viper.Viper, s3Config storage.S3Config) (storage.S3Co
func s3Overrides(in map[string]string) map[string]string { func s3Overrides(in map[string]string) map[string]string {
return map[string]string{ return map[string]string{
storage.Bucket: in[storage.Bucket], storage.Bucket: in[storage.Bucket],
storage.Endpoint: in[storage.Endpoint], storage.Endpoint: in[storage.Endpoint],
storage.Prefix: in[storage.Prefix], storage.Prefix: in[storage.Prefix],
storage.DoNotUseTLS: in[storage.DoNotUseTLS], storage.DoNotUseTLS: in[storage.DoNotUseTLS],
storage.DoNotVerifyTLS: in[storage.DoNotVerifyTLS], storage.DoNotVerifyTLS: in[storage.DoNotVerifyTLS],
StorageProviderTypeKey: in[StorageProviderTypeKey], storage.StorageProviderTypeKey: in[storage.StorageProviderTypeKey],
} }
} }
@ -53,6 +54,7 @@ func s3Overrides(in map[string]string) map[string]string {
// viper properties and manual overrides. // viper properties and manual overrides.
func configureStorage( func configureStorage(
vpr *viper.Viper, vpr *viper.Viper,
provider storage.ProviderType,
readConfigFromViper bool, readConfigFromViper bool,
matchFromConfig bool, matchFromConfig bool,
overrides map[string]string, overrides map[string]string,
@ -77,7 +79,7 @@ func configureStorage(
} }
if matchFromConfig { if matchFromConfig {
providerType := vpr.GetString(StorageProviderTypeKey) providerType := vpr.GetString(storage.StorageProviderTypeKey)
if providerType != storage.ProviderS3.String() { if providerType != storage.ProviderS3.String() {
return store, clues.New("unsupported storage provider: " + providerType) return store, clues.New("unsupported storage provider: " + providerType)
} }
@ -92,7 +94,6 @@ func configureStorage(
return store, clues.Wrap(err, "reading s3 configs from corso config file") return store, clues.Wrap(err, "reading s3 configs from corso config file")
} }
s3Overrides(overrides)
aws := credentials.GetAWS(overrides) aws := credentials.GetAWS(overrides)
if len(aws.AccessKey) <= 0 || len(aws.SecretKey) <= 0 { if len(aws.AccessKey) <= 0 || len(aws.SecretKey) <= 0 {
@ -152,7 +153,7 @@ func configureStorage(
} }
// build the storage // build the storage
store, err = storage.NewStorage(storage.ProviderS3, s3Cfg, cCfg) store, err = storage.NewStorage(provider, s3Cfg, cCfg)
if err != nil { if err != nil {
return store, clues.Wrap(err, "configuring repository storage") return store, clues.Wrap(err, "configuring repository storage")
} }
@ -170,3 +171,22 @@ func GetAndInsertCorso(passphase string) credentials.Corso {
CorsoPassphrase: corsoPassph, CorsoPassphrase: corsoPassph,
} }
} }
// GetStorageProviderFromConfigFile reads the storage provider from the config file.
// Storage provider can only be sourced from config file with the exception of
// commands that create or connect to a repo.
func GetStorageProviderFromConfigFile(ctx context.Context) (storage.ProviderType, error) {
vpr := GetViper(ctx)
err := vpr.ReadInConfig()
if err != nil {
return storage.ProviderUnknown, clues.Wrap(err, "reading config file")
}
provider := vpr.GetString(storage.StorageProviderTypeKey)
if provider != storage.ProviderS3.String() {
return storage.ProviderUnknown, clues.New("unsupported storage provider: " + provider)
}
return storage.StringToProviderType[provider], nil
}

View File

@ -9,7 +9,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/common/dttm" "github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
@ -70,7 +69,10 @@ func runExport(
sel selectors.Selector, sel selectors.Selector,
backupID, serviceName string, backupID, serviceName string,
) error { ) error {
r, _, _, _, err := utils.GetAccountAndConnect(ctx, sel.PathService(), repo.S3Overrides(cmd)) r, _, _, _, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
sel.PathService())
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -1,6 +1,14 @@
package flags package flags
import "github.com/spf13/cobra" import (
"strconv"
"github.com/spf13/cobra"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/storage"
)
// S3 bucket flags // S3 bucket flags
const ( const (
@ -39,3 +47,49 @@ func AddS3BucketFlags(cmd *cobra.Command) {
fs.BoolVar(&SucceedIfExistsFV, SucceedIfExistsFN, false, "Exit with success if the repo has already been initialized.") fs.BoolVar(&SucceedIfExistsFV, SucceedIfExistsFN, false, "Exit with success if the repo has already been initialized.")
cobra.CheckErr(fs.MarkHidden("succeed-if-exists")) cobra.CheckErr(fs.MarkHidden("succeed-if-exists"))
} }
func S3FlagOverrides(cmd *cobra.Command) map[string]string {
fs := GetPopulatedFlags(cmd)
return PopulateS3Flags(fs)
}
func PopulateS3Flags(flagset PopulatedFlags) map[string]string {
s3Overrides := make(map[string]string)
// TODO(pandeyabs): Move account overrides out of s3 flags
s3Overrides[account.AccountProviderTypeKey] = account.ProviderM365.String()
s3Overrides[storage.StorageProviderTypeKey] = storage.ProviderS3.String()
if _, ok := flagset[AWSAccessKeyFN]; ok {
s3Overrides[credentials.AWSAccessKeyID] = AWSAccessKeyFV
}
if _, ok := flagset[AWSSecretAccessKeyFN]; ok {
s3Overrides[credentials.AWSSecretAccessKey] = AWSSecretAccessKeyFV
}
if _, ok := flagset[AWSSessionTokenFN]; ok {
s3Overrides[credentials.AWSSessionToken] = AWSSessionTokenFV
}
if _, ok := flagset[BucketFN]; ok {
s3Overrides[storage.Bucket] = BucketFV
}
if _, ok := flagset[PrefixFN]; ok {
s3Overrides[storage.Prefix] = PrefixFV
}
if _, ok := flagset[DoNotUseTLSFN]; ok {
s3Overrides[storage.DoNotUseTLS] = strconv.FormatBool(DoNotUseTLSFV)
}
if _, ok := flagset[DoNotVerifyTLSFN]; ok {
s3Overrides[storage.DoNotVerifyTLS] = strconv.FormatBool(DoNotVerifyTLSFV)
}
if _, ok := flagset[EndpointFN]; ok {
s3Overrides[storage.Endpoint] = EndpointFV
}
return s3Overrides
}

View File

@ -123,10 +123,10 @@ func handleMaintenanceCmd(cmd *cobra.Command, args []string) error {
r, _, err := utils.AccountConnectAndWriteRepoConfig( r, _, err := utils.AccountConnectAndWriteRepoConfig(
ctx, ctx,
cmd,
// Need to give it a valid service so it won't error out on us even though // Need to give it a valid service so it won't error out on us even though
// we don't need the graph client. // we don't need the graph client.
path.OneDriveService, path.OneDriveService)
S3Overrides(cmd))
if err != nil { if err != nil {
return print.Only(ctx, err) return print.Only(ctx, err)
} }

View File

@ -1,7 +1,6 @@
package repo package repo
import ( import (
"strconv"
"strings" "strings"
"github.com/alcionai/clues" "github.com/alcionai/clues"
@ -13,8 +12,6 @@ import (
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/events" "github.com/alcionai/corso/src/internal/events"
"github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/credentials"
"github.com/alcionai/corso/src/pkg/repository" "github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/storage" "github.com/alcionai/corso/src/pkg/storage"
) )
@ -89,10 +86,12 @@ func s3InitCmd() *cobra.Command {
func initS3Cmd(cmd *cobra.Command, args []string) error { func initS3Cmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
// s3 values from flags cfg, err := config.GetConfigRepoDetails(
s3Override := S3Overrides(cmd) ctx,
storage.ProviderS3,
cfg, err := config.GetConfigRepoDetails(ctx, true, false, s3Override) true,
false,
flags.S3FlagOverrides(cmd))
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -175,10 +174,12 @@ func s3ConnectCmd() *cobra.Command {
func connectS3Cmd(cmd *cobra.Command, args []string) error { func connectS3Cmd(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() ctx := cmd.Context()
// s3 values from flags cfg, err := config.GetConfigRepoDetails(
s3Override := S3Overrides(cmd) ctx,
storage.ProviderS3,
cfg, err := config.GetConfigRepoDetails(ctx, true, true, s3Override) true,
true,
flags.S3FlagOverrides(cmd))
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }
@ -227,48 +228,3 @@ func connectS3Cmd(cmd *cobra.Command, args []string) error {
return nil return nil
} }
func S3Overrides(cmd *cobra.Command) map[string]string {
fs := flags.GetPopulatedFlags(cmd)
return PopulateS3Flags(fs)
}
func PopulateS3Flags(flagset flags.PopulatedFlags) map[string]string {
s3Overrides := make(map[string]string)
s3Overrides[config.AccountProviderTypeKey] = account.ProviderM365.String()
s3Overrides[config.StorageProviderTypeKey] = storage.ProviderS3.String()
if _, ok := flagset[flags.AWSAccessKeyFN]; ok {
s3Overrides[credentials.AWSAccessKeyID] = flags.AWSAccessKeyFV
}
if _, ok := flagset[flags.AWSSecretAccessKeyFN]; ok {
s3Overrides[credentials.AWSSecretAccessKey] = flags.AWSSecretAccessKeyFV
}
if _, ok := flagset[flags.AWSSessionTokenFN]; ok {
s3Overrides[credentials.AWSSessionToken] = flags.AWSSessionTokenFV
}
if _, ok := flagset[flags.BucketFN]; ok {
s3Overrides[storage.Bucket] = flags.BucketFV
}
if _, ok := flagset[flags.PrefixFN]; ok {
s3Overrides[storage.Prefix] = flags.PrefixFV
}
if _, ok := flagset[flags.DoNotUseTLSFN]; ok {
s3Overrides[storage.DoNotUseTLS] = strconv.FormatBool(flags.DoNotUseTLSFV)
}
if _, ok := flagset[flags.DoNotVerifyTLSFN]; ok {
s3Overrides[storage.DoNotVerifyTLS] = strconv.FormatBool(flags.DoNotVerifyTLSFV)
}
if _, ok := flagset[flags.EndpointFN]; ok {
s3Overrides[storage.Endpoint] = flags.EndpointFV
}
return s3Overrides
}

View File

@ -10,7 +10,6 @@ import (
"github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/cli/flags"
. "github.com/alcionai/corso/src/cli/print" . "github.com/alcionai/corso/src/cli/print"
"github.com/alcionai/corso/src/cli/repo"
"github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/cli/utils"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
"github.com/alcionai/corso/src/pkg/count" "github.com/alcionai/corso/src/pkg/count"
@ -103,7 +102,10 @@ func runRestore(
sel selectors.Selector, sel selectors.Selector,
backupID, serviceName string, backupID, serviceName string,
) error { ) error {
r, _, _, _, err := utils.GetAccountAndConnect(ctx, sel.PathService(), repo.S3Overrides(cmd)) r, _, _, _, err := utils.GetAccountAndConnectWithOverrides(
ctx,
cmd,
sel.PathService())
if err != nil { if err != nil {
return Only(ctx, err) return Only(ctx, err)
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/alcionai/corso/src/cli/config" "github.com/alcionai/corso/src/cli/config"
"github.com/alcionai/corso/src/cli/flags"
"github.com/alcionai/corso/src/internal/events" "github.com/alcionai/corso/src/internal/events"
"github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/account"
"github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/control"
@ -21,12 +22,34 @@ import (
var ErrNotYetImplemented = clues.New("not yet implemented") var ErrNotYetImplemented = clues.New("not yet implemented")
// GetAccountAndConnectWithOverrides is a wrapper for GetAccountAndConnect
// that also gets the storage provider and any storage provider specific
// flag overrides from the command line.
func GetAccountAndConnectWithOverrides(
ctx context.Context,
cmd *cobra.Command,
pst path.ServiceType,
) (repository.Repository, *storage.Storage, *account.Account, *control.Options, error) {
provider, overrides, err := GetStorageProviderAndOverrides(ctx, cmd)
if err != nil {
return nil, nil, nil, nil, err
}
return GetAccountAndConnect(ctx, pst, provider, overrides)
}
func GetAccountAndConnect( func GetAccountAndConnect(
ctx context.Context, ctx context.Context,
pst path.ServiceType, pst path.ServiceType,
provider storage.ProviderType,
overrides map[string]string, overrides map[string]string,
) (repository.Repository, *storage.Storage, *account.Account, *control.Options, error) { ) (repository.Repository, *storage.Storage, *account.Account, *control.Options, error) {
cfg, err := config.GetConfigRepoDetails(ctx, true, true, overrides) cfg, err := config.GetConfigRepoDetails(
ctx,
provider,
true,
true,
overrides)
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
@ -54,10 +77,13 @@ func GetAccountAndConnect(
func AccountConnectAndWriteRepoConfig( func AccountConnectAndWriteRepoConfig(
ctx context.Context, ctx context.Context,
cmd *cobra.Command,
pst path.ServiceType, pst path.ServiceType,
overrides map[string]string,
) (repository.Repository, *account.Account, error) { ) (repository.Repository, *account.Account, error) {
r, stg, acc, opts, err := GetAccountAndConnect(ctx, pst, overrides) r, stg, acc, opts, err := GetAccountAndConnectWithOverrides(
ctx,
cmd,
pst)
if err != nil { if err != nil {
logger.CtxErr(ctx, err).Info("getting and connecting account") logger.CtxErr(ctx, err).Info("getting and connecting account")
return nil, nil, err return nil, nil, err
@ -203,3 +229,24 @@ func SendStartCorsoEvent(
bus.SetRepoID(repoID) bus.SetRepoID(repoID)
bus.Event(ctx, events.CorsoStart, data) bus.Event(ctx, events.CorsoStart, data)
} }
// GetStorageProviderAndOverrides returns the storage provider type and
// any flags specified on the command line which are storage provider specific.
func GetStorageProviderAndOverrides(
ctx context.Context,
cmd *cobra.Command,
) (storage.ProviderType, map[string]string, error) {
provider, err := config.GetStorageProviderFromConfigFile(ctx)
if err != nil {
return provider, nil, clues.Stack(err)
}
overrides := map[string]string{}
switch provider {
case storage.ProviderS3:
overrides = flags.S3FlagOverrides(cmd)
}
return provider, overrides, nil
}

View File

@ -16,12 +16,14 @@ import (
"github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
"github.com/alcionai/corso/src/pkg/repository" "github.com/alcionai/corso/src/pkg/repository"
"github.com/alcionai/corso/src/pkg/storage"
"github.com/alcionai/corso/src/pkg/store" "github.com/alcionai/corso/src/pkg/store"
) )
// deleteBackups connects to the repository and deletes all backups for // deleteBackups connects to the repository and deletes all backups for
// service that are at least deletionDays old. Returns the IDs of all backups // service that are at least deletionDays old. Returns the IDs of all backups
// that were deleted. // that were deleted.
// Only supported for S3 repos currently.
func deleteBackups( func deleteBackups(
ctx context.Context, ctx context.Context,
service path.ServiceType, service path.ServiceType,
@ -29,7 +31,11 @@ func deleteBackups(
) ([]string, error) { ) ([]string, error) {
ctx = clues.Add(ctx, "cutoff_days", deletionDays) ctx = clues.Add(ctx, "cutoff_days", deletionDays)
r, _, _, _, err := utils.GetAccountAndConnect(ctx, service, nil) r, _, _, _, err := utils.GetAccountAndConnect(
ctx,
service,
storage.ProviderS3,
nil)
if err != nil { if err != nil {
return nil, clues.Wrap(err, "connecting to account").WithClues(ctx) return nil, clues.Wrap(err, "connecting to account").WithClues(ctx)
} }
@ -67,6 +73,7 @@ func deleteBackups(
// pitrListBackups connects to the repository at the given point in time and // pitrListBackups connects to the repository at the given point in time and
// lists the backups for service. It then checks the list of backups contains // lists the backups for service. It then checks the list of backups contains
// the backups in backupIDs. // the backups in backupIDs.
// Only supported for S3 repos currently.
func pitrListBackups( func pitrListBackups(
ctx context.Context, ctx context.Context,
service path.ServiceType, service path.ServiceType,
@ -87,7 +94,12 @@ func pitrListBackups(
// TODO(ashmrtn): This may be moved into CLI layer at some point when we add // TODO(ashmrtn): This may be moved into CLI layer at some point when we add
// flags for opening a repo at a point in time. // flags for opening a repo at a point in time.
cfg, err := config.GetConfigRepoDetails(ctx, true, true, nil) cfg, err := config.GetConfigRepoDetails(
ctx,
storage.ProviderS3,
true,
true,
nil)
if err != nil { if err != nil {
return clues.Wrap(err, "getting config info") return clues.Wrap(err, "getting config info")
} }

View File

@ -187,7 +187,12 @@ func handleCheckerCommand(cmd *cobra.Command, args []string, f flags) error {
storage.Prefix: f.bucketPrefix, storage.Prefix: f.bucketPrefix,
} }
repoDetails, err := config.GetConfigRepoDetails(ctx, false, false, overrides) repoDetails, err := config.GetConfigRepoDetails(
ctx,
storage.ProviderS3,
false,
false,
overrides)
if err != nil { if err != nil {
return clues.Wrap(err, "getting storage config") return clues.Wrap(err, "getting storage config")
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/cli/config"
"github.com/alcionai/corso/src/internal/common/prefixmatcher" "github.com/alcionai/corso/src/internal/common/prefixmatcher"
"github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/data"
dataMock "github.com/alcionai/corso/src/internal/data/mock" dataMock "github.com/alcionai/corso/src/internal/data/mock"
@ -1641,7 +1640,7 @@ func makeMockItem(
func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() { func (suite *AssistBackupIntegrationSuite) TestBackupTypesForFailureModes() {
var ( var (
acct = tconfig.NewM365Account(suite.T()) acct = tconfig.NewM365Account(suite.T())
tenantID = acct.Config[config.AzureTenantIDKey] tenantID = acct.Config[account.AzureTenantIDKey]
opts = control.DefaultOptions() opts = control.DefaultOptions()
osel = selectors.NewOneDriveBackup([]string{userID}) osel = selectors.NewOneDriveBackup([]string{userID})
) )
@ -1905,7 +1904,7 @@ func selectFilesFromDeets(d details.Details) map[string]details.Entry {
func (suite *AssistBackupIntegrationSuite) TestExtensionsIncrementals() { func (suite *AssistBackupIntegrationSuite) TestExtensionsIncrementals() {
var ( var (
acct = tconfig.NewM365Account(suite.T()) acct = tconfig.NewM365Account(suite.T())
tenantID = acct.Config[config.AzureTenantIDKey] tenantID = acct.Config[account.AzureTenantIDKey]
opts = control.DefaultOptions() opts = control.DefaultOptions()
osel = selectors.NewOneDriveBackup([]string{userID}) osel = selectors.NewOneDriveBackup([]string{userID})
// Default policy used by SDK clients // Default policy used by SDK clients

View File

@ -19,6 +19,14 @@ var (
errMissingRequired = clues.New("missing required storage configuration") errMissingRequired = clues.New("missing required storage configuration")
) )
const (
// M365 config
AccountProviderTypeKey = "account_provider"
AzureTenantIDKey = "azure_tenantid"
AzureClientID = "azure_client_id"
AzureSecret = "azure_secret"
)
// Account defines an account provider, along with any credentials // Account defines an account provider, along with any credentials
// and identifiers required to set up or communicate with that provider. // and identifiers required to set up or communicate with that provider.
type Account struct { type Account struct {

View File

@ -17,6 +17,16 @@ const (
ProviderFilesystem ProviderType = 2 // Filesystem ProviderFilesystem ProviderType = 2 // Filesystem
) )
var StringToProviderType = map[string]ProviderType{
ProviderUnknown.String(): ProviderUnknown,
ProviderS3.String(): ProviderS3,
ProviderFilesystem.String(): ProviderFilesystem,
}
const (
StorageProviderTypeKey = "provider"
)
// storage parsing errors // storage parsing errors
var ( var (
errMissingRequired = clues.New("missing required storage configuration") errMissingRequired = clues.New("missing required storage configuration")