corso/src/cli/config/config.go
Keepers 108065df75
remove repository.Account (#229)
* remove repository.Account

Now that account.Account exists, we can replace the
deprecated repository.Account struct with the account
version.
2022-06-23 16:02:12 -06:00

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 ""
}