corso/src/internal/tester/config.go
Vaibhav Kamra 66103892c5
Allow specifying a role to assume when initializing a storage provider (#3284)
Allows caller to specify a IAM role to assume in the Kopia storage provider

---

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

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

#### Type of change

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

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* #2106 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [ ]  Unit test
- [x] 💚 E2E
2023-05-09 21:40:10 +00:00

220 lines
6.5 KiB
Go

package tester
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/alcionai/clues"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/pkg/account"
)
const (
// S3 config
TestCfgBucket = "bucket"
TestCfgEndpoint = "endpoint"
TestCfgPrefix = "prefix"
TestCfgStorageProvider = "provider"
// M365 config
TestCfgAzureTenantID = "azure_tenantid"
TestCfgSiteID = "m365siteid"
TestCfgSiteURL = "m365siteurl"
TestCfgUserID = "m365userid"
TestCfgSecondaryUserID = "secondarym365userid"
TestCfgTertiaryUserID = "tertiarym365userid"
TestCfgLoadTestUserID = "loadtestm365userid"
TestCfgLoadTestOrgUsers = "loadtestm365orgusers"
TestCfgAccountProvider = "account_provider"
)
// test specific env vars
const (
EnvCorsoM365TestSiteID = "CORSO_M365_TEST_SITE_ID"
EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL"
EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID"
EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID"
EnvCorsoTertiaryM365TestUserID = "CORSO_TERTIARY_M365_TEST_USER_ID"
EnvCorsoM365LoadTestUserID = "CORSO_M365_LOAD_TEST_USER_ID"
EnvCorsoM365LoadTestOrgUsers = "CORSO_M365_LOAD_TEST_ORG_USERS"
EnvCorsoTestConfigFilePath = "CORSO_TEST_CONFIG_FILE"
)
// global to hold the test config results.
var testConfig map[string]string
// call this instead of returning testConfig directly.
func cloneTestConfig() map[string]string {
if testConfig == nil {
return map[string]string{}
}
return maps.Clone(testConfig)
}
func NewTestViper() (*viper.Viper, error) {
vpr := viper.New()
configFilePath := os.Getenv(EnvCorsoTestConfigFilePath)
if len(configFilePath) == 0 {
return vpr, nil
}
// Or use a custom file location
ext := filepath.Ext(configFilePath)
if len(ext) == 0 {
return nil, clues.New("corso_test requires an extension")
}
vpr.SetConfigFile(configFilePath)
vpr.AddConfigPath(filepath.Dir(configFilePath))
vpr.SetConfigType(ext[1:])
fileName := filepath.Base(configFilePath)
fileName = strings.TrimSuffix(fileName, ext)
vpr.SetConfigName(fileName)
return vpr, nil
}
// reads a corso configuration file with values specific to
// local integration test controls. Populates values with
// defaults where standard.
func readTestConfig() (map[string]string, error) {
if testConfig != nil {
return cloneTestConfig(), nil
}
vpr, err := NewTestViper()
if err != nil {
return nil, err
}
// only error if reading an existing file failed. No problem if we're missing files.
if err = vpr.ReadInConfig(); err != nil {
_, ok := err.(viper.ConfigFileNotFoundError)
if !ok {
return nil, clues.Wrap(err, "reading config file: "+viper.ConfigFileUsed())
}
}
testEnv := map[string]string{}
fallbackTo(testEnv, TestCfgStorageProvider, vpr.GetString(TestCfgStorageProvider))
fallbackTo(testEnv, TestCfgAccountProvider, vpr.GetString(TestCfgAccountProvider))
fallbackTo(testEnv, TestCfgBucket, os.Getenv("S3_BUCKET"), vpr.GetString(TestCfgBucket), "test-corso-repo-init")
fallbackTo(testEnv, TestCfgEndpoint, vpr.GetString(TestCfgEndpoint), "s3.amazonaws.com")
fallbackTo(testEnv, TestCfgPrefix, vpr.GetString(TestCfgPrefix))
fallbackTo(testEnv, TestCfgAzureTenantID, os.Getenv(account.AzureTenantID), vpr.GetString(TestCfgAzureTenantID))
fallbackTo(
testEnv,
TestCfgUserID,
os.Getenv(EnvCorsoM365TestUserID),
vpr.GetString(TestCfgUserID),
"LynneR@10rqc2.onmicrosoft.com")
fallbackTo(
testEnv,
TestCfgSecondaryUserID,
os.Getenv(EnvCorsoSecondaryM365TestUserID),
vpr.GetString(TestCfgSecondaryUserID),
"AdeleV@10rqc2.onmicrosoft.com")
fallbackTo(
testEnv,
TestCfgTertiaryUserID,
os.Getenv(EnvCorsoTertiaryM365TestUserID),
vpr.GetString(TestCfgTertiaryUserID),
"PradeepG@10rqc2.onmicrosoft.com")
fallbackTo(
testEnv,
TestCfgLoadTestUserID,
os.Getenv(EnvCorsoM365LoadTestUserID),
vpr.GetString(TestCfgLoadTestUserID),
"leeg@10rqc2.onmicrosoft.com")
fallbackTo(
testEnv,
TestCfgLoadTestOrgUsers,
os.Getenv(EnvCorsoM365LoadTestOrgUsers),
vpr.GetString(TestCfgLoadTestOrgUsers),
"AdeleV@10rqc2.onmicrosoft.com,LynneR@10rqc2.onmicrosoft.com")
fallbackTo(
testEnv,
TestCfgSiteID,
os.Getenv(EnvCorsoM365TestSiteID),
vpr.GetString(TestCfgSiteID),
"10rqc2.sharepoint.com,4892edf5-2ebf-46be-a6e5-a40b2cbf1c1a,38ab6d06-fc82-4417-af93-22d8733c22be")
fallbackTo(
testEnv,
TestCfgSiteURL,
os.Getenv(EnvCorsoM365TestSiteURL),
vpr.GetString(TestCfgSiteURL),
"https://10rqc2.sharepoint.com/sites/CorsoCI")
testEnv[EnvCorsoTestConfigFilePath] = os.Getenv(EnvCorsoTestConfigFilePath)
testConfig = testEnv
return cloneTestConfig(), nil
}
// MakeTempTestConfigClone makes a copy of the test config file in a temp directory.
// This allows tests which interface with reading and writing to a config file
// (such as the CLI) to safely manipulate file contents without amending the user's
// original file.
//
// Attempts to copy values sourced from the caller's test config file.
// The overrides prop replaces config values with the provided value.
//
// Returns a filepath string pointing to the location of the temp file.
func MakeTempTestConfigClone(t *testing.T, overrides map[string]string) (*viper.Viper, string) {
cfg, err := readTestConfig()
require.NoError(t, err, "reading tester config", clues.ToCore(err))
fName := filepath.Base(os.Getenv(EnvCorsoTestConfigFilePath))
if len(fName) == 0 || fName == "." || fName == "/" {
fName = ".corso_test.toml"
}
tDir := t.TempDir()
tDirFp := filepath.Join(tDir, fName)
_, err = os.Create(tDirFp)
require.NoError(t, err, "creating temp test dir", clues.ToCore(err))
ext := filepath.Ext(fName)
vpr := viper.New()
vpr.SetConfigFile(tDirFp)
vpr.AddConfigPath(tDir)
vpr.SetConfigType(strings.TrimPrefix(ext, "."))
vpr.SetConfigName(strings.TrimSuffix(fName, ext))
vpr.Set("corso-testing", true)
for k, v := range cfg {
vpr.Set(k, v)
}
for k, v := range overrides {
vpr.Set(k, v)
}
err = vpr.WriteConfig()
require.NoError(t, err, "writing temp dir viper config file", clues.ToCore(err))
return vpr, tDirFp
}
// writes the first non-zero valued string to the map at the key.
// fallback priority should match viper ordering (manually handled
// here since viper fails to provide fallbacks on fileNotFoundErr):
// manual overrides > flags > env vars > config file > default value
func fallbackTo(m map[string]string, key string, fallbacks ...string) {
for _, fb := range fallbacks {
if len(fb) > 0 {
m[key] = fb
return
}
}
}