* remove repository.Account Now that account.Account exists, we can replace the deprecated repository.Account struct with the account version.
182 lines
5.0 KiB
Go
182 lines
5.0 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/viper"
|
|
|
|
"github.com/alcionai/corso/cli/utils"
|
|
"github.com/alcionai/corso/pkg/account"
|
|
"github.com/alcionai/corso/pkg/credentials"
|
|
"github.com/alcionai/corso/pkg/storage"
|
|
)
|
|
|
|
const (
|
|
// S3 config
|
|
ProviderTypeKey = "provider"
|
|
BucketNameKey = "bucket"
|
|
EndpointKey = "endpoint"
|
|
PrefixKey = "prefix"
|
|
|
|
// M365 config
|
|
TenantIDKey = "tenantid"
|
|
)
|
|
|
|
func InitConfig(configFilePath string) error {
|
|
// Configure default config file location
|
|
if configFilePath == "" {
|
|
// Find home directory.
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Search config in home directory with name ".corso" (without extension).
|
|
viper.AddConfigPath(home)
|
|
viper.SetConfigType("toml")
|
|
viper.SetConfigName(".corso")
|
|
return nil
|
|
}
|
|
// Use a custom file location
|
|
|
|
viper.SetConfigFile(configFilePath)
|
|
// We also configure the path, type and filename
|
|
// because `viper.SafeWriteConfig` needs these set to
|
|
// work correctly (it does not use the configured file)
|
|
viper.AddConfigPath(path.Dir(configFilePath))
|
|
|
|
fileName := path.Base(configFilePath)
|
|
ext := path.Ext(configFilePath)
|
|
if len(ext) == 0 {
|
|
return errors.New("config file requires an extension e.g. `toml`")
|
|
}
|
|
fileName = strings.TrimSuffix(fileName, ext)
|
|
viper.SetConfigType(ext[1:])
|
|
viper.SetConfigName(fileName)
|
|
return nil
|
|
}
|
|
|
|
// WriteRepoConfig currently just persists corso config to the config file
|
|
// It does not check for conflicts or existing data.
|
|
func WriteRepoConfig(s3Config storage.S3Config, account account.M365Config) error {
|
|
// Rudimentary support for persisting repo config
|
|
// TODO: Handle conflicts, support other config types
|
|
viper.Set(ProviderTypeKey, storage.ProviderS3.String())
|
|
viper.Set(BucketNameKey, s3Config.Bucket)
|
|
viper.Set(EndpointKey, s3Config.Endpoint)
|
|
viper.Set(PrefixKey, s3Config.Prefix)
|
|
viper.Set(TenantIDKey, account.TenantID)
|
|
|
|
if err := viper.SafeWriteConfig(); err != nil {
|
|
if _, ok := err.(viper.ConfigFileAlreadyExistsError); ok {
|
|
return viper.GetViper().WriteConfig()
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReadRepoConfig() (storage.S3Config, account.Account, error) {
|
|
var (
|
|
s3Config storage.S3Config
|
|
acct account.Account
|
|
err error
|
|
)
|
|
|
|
if err = viper.ReadInConfig(); err != nil {
|
|
return s3Config, acct, errors.Wrap(err, "reading config file: "+viper.ConfigFileUsed())
|
|
}
|
|
|
|
if providerType := viper.GetString(ProviderTypeKey); providerType != storage.ProviderS3.String() {
|
|
return s3Config, acct, errors.New("Unsupported storage provider: " + providerType)
|
|
}
|
|
|
|
s3Config.Bucket = viper.GetString(BucketNameKey)
|
|
s3Config.Endpoint = viper.GetString(EndpointKey)
|
|
s3Config.Prefix = viper.GetString(PrefixKey)
|
|
|
|
m365Creds := credentials.GetM365()
|
|
cfgTenantID := viper.GetString(TenantIDKey)
|
|
if len(cfgTenantID) > 0 {
|
|
m365Creds.TenantID = cfgTenantID
|
|
}
|
|
acct, err = account.NewAccount(
|
|
account.ProviderM365,
|
|
account.M365Config{
|
|
M365: m365Creds,
|
|
},
|
|
)
|
|
|
|
return s3Config, acct, err
|
|
}
|
|
|
|
// GetStorageAndAccount creates a storage and account instance by mediating all the possible
|
|
// data sources (config file, env vars, flag overrides) and the config file.
|
|
func GetStorageAndAccount(readFromFile bool, overrides map[string]string) (storage.Storage, account.Account, error) {
|
|
var (
|
|
s3Cfg storage.S3Config
|
|
acct account.Account
|
|
err error
|
|
)
|
|
|
|
// possibly read the prior config from a .corso file
|
|
if readFromFile {
|
|
s3Cfg, acct, err = ReadRepoConfig()
|
|
if err != nil {
|
|
return storage.Storage{}, acct, errors.Wrap(err, "reading corso config file")
|
|
}
|
|
}
|
|
|
|
// compose the s3 storage config and credentials
|
|
aws := credentials.GetAWS(overrides)
|
|
if err := aws.Validate(); err != nil {
|
|
return storage.Storage{}, acct, errors.Wrap(err, "validating aws credentials")
|
|
}
|
|
s3Cfg = storage.S3Config{
|
|
AWS: aws,
|
|
Bucket: first(overrides[storage.Bucket], s3Cfg.Bucket),
|
|
Endpoint: first(overrides[storage.Endpoint], s3Cfg.Endpoint),
|
|
Prefix: first(overrides[storage.Prefix], s3Cfg.Prefix),
|
|
}
|
|
|
|
// compose the common config and credentials
|
|
corso := credentials.GetCorso()
|
|
if err := corso.Validate(); err != nil {
|
|
return storage.Storage{}, acct, errors.Wrap(err, "validating corso credentials")
|
|
}
|
|
cCfg := storage.CommonConfig{
|
|
Corso: corso,
|
|
}
|
|
|
|
// ensure requried properties are present
|
|
if err := utils.RequireProps(map[string]string{
|
|
credentials.AWSAccessKeyID: aws.AccessKey,
|
|
storage.Bucket: s3Cfg.Bucket,
|
|
credentials.AWSSecretAccessKey: aws.SecretKey,
|
|
credentials.AWSSessionToken: aws.SessionToken,
|
|
credentials.CorsoPassword: corso.CorsoPassword,
|
|
}); err != nil {
|
|
return storage.Storage{}, acct, err
|
|
}
|
|
|
|
// return a complete storage
|
|
s, err := storage.NewStorage(storage.ProviderS3, s3Cfg, cCfg)
|
|
if err != nil {
|
|
return storage.Storage{}, acct, errors.Wrap(err, "configuring repository storage")
|
|
}
|
|
return s, acct, nil
|
|
}
|
|
|
|
// returns the first non-zero valued string
|
|
func first(vs ...string) string {
|
|
for _, v := range vs {
|
|
if len(v) > 0 {
|
|
return v
|
|
}
|
|
}
|
|
return ""
|
|
}
|