diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 3df6788252..7eb2a326fb 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -5,6 +5,22 @@ describe deprecations or breaking changes and help you to change your configurat across different versions. ## v0.86.0 ➞ v0.87.0 +### Provider configuration changes + +#### **IMPORTANT** *(bug fix)* Configuration hierarchy +There were several issues reported about the configuration hierarchy, e.g. [#2294](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2294) and [#2242](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2242). +In fact, the order of precedence described in the docs was not followed. This have led to the incorrect behavior. + +After migrating to this version, the hierarchy from the docs should be followed: +```text +The Snowflake provider will use the following order of precedence when determining which credentials to use: +1) Provider Configuration +2) Environment Variables +3) Config File +``` + +**BEWARE**: your configurations will be affected with that change because they may have been leveraging the incorrect configurations precedence. Please be sure to check all the configurations before running terraform. + ### snowflake_failover_group resource changes #### *(bug fix)* ACCOUNT PARAMETERS is returned as PARAMETERS from SHOW FAILOVER GROUPS Longer context in [#2517](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2517). diff --git a/docs/index.md b/docs/index.md index 77fe01af68..a25c829ca1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,7 +7,7 @@ description: Manage SnowflakeDB with Terraform. ~> **Disclaimer** the project is still in the 0.x.x version, which means it’s still in the experimental phase (check [Go module versioning](https://go.dev/doc/modules/version-numbers#v0-number) for more details). It can be used in production but makes no stability or backward compatibility guarantees. We do not provide backward bug fixes and, therefore, always suggest using the newest version. We are providing only limited support for the provider; priorities will be assigned on a case-by-case basis. Our main current goals are stabilization, addressing existing issues, and providing the missing features (prioritizing the GA features; supporting PrPr and PuPr features are not high priorities now). With all that in mind, we aim to reach V1 with a stable, reliable, and functional provider. V1 will be free of all the above limitations. --> **Note** Please check the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md) when changing the version of the provider. +~> **Note** Please check the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md) when changing the version of the provider. -> **Note** the current roadmap is available in our GitHub repository: [ROADMAP.md](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md). diff --git a/pkg/acceptance/testenvs/assertions.go b/pkg/acceptance/testenvs/assertions.go new file mode 100644 index 0000000000..77f0caf4d0 --- /dev/null +++ b/pkg/acceptance/testenvs/assertions.go @@ -0,0 +1,18 @@ +package testenvs + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func AssertEnvNotSet(t *testing.T, envName string) { + t.Helper() + require.Emptyf(t, os.Getenv(envName), "environment variable %v should not be set", envName) +} + +func AssertEnvSet(t *testing.T, envName string) { + t.Helper() + require.NotEmptyf(t, os.Getenv(envName), "environment variable %v should not be empty", envName) +} diff --git a/pkg/acceptance/testenvs/testenvs_test.go b/pkg/acceptance/testenvs/testenvs_test.go new file mode 100644 index 0000000000..f5ddd8c645 --- /dev/null +++ b/pkg/acceptance/testenvs/testenvs_test.go @@ -0,0 +1,86 @@ +package testenvs_test + +import ( + "sync" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/stretchr/testify/require" +) + +func Test_GetOrSkipTest(t *testing.T) { + // runGetOrSkipInGoroutineAndWaitForCompletion is needed because underneath we test t.Skipf, that leads to t.SkipNow() that in turn call runtime.Goexit() + // so we need to be wrapped in a Goroutine. + runGetOrSkipInGoroutineAndWaitForCompletion := func(t *testing.T) string { + t.Helper() + var env string + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + env = testenvs.GetOrSkipTest(t, testenvs.User) + }() + wg.Wait() + return env + } + + t.Run("skip test if missing", func(t *testing.T) { + t.Setenv(string(testenvs.User), "") + + tut := &testing.T{} + env := runGetOrSkipInGoroutineAndWaitForCompletion(tut) + + require.True(t, tut.Skipped()) + require.Empty(t, env) + }) + + t.Run("get env if exists", func(t *testing.T) { + t.Setenv(string(testenvs.User), "user") + + tut := &testing.T{} + env := runGetOrSkipInGoroutineAndWaitForCompletion(tut) + + require.False(t, tut.Skipped()) + require.Equal(t, "user", env) + }) +} + +func Test_Assertions(t *testing.T) { + // runAssertionInGoroutineAndWaitForCompletion is needed because underneath we test require, that leads to t.FailNow() that in turn call runtime.Goexit() + // so we need to be wrapped in a Goroutine. + runAssertionInGoroutineAndWaitForCompletion := func(assertion func()) { + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + assertion() + }() + wg.Wait() + } + + t.Run("test if env does not exist", func(t *testing.T) { + t.Setenv(string(testenvs.User), "") + + tut1 := &testing.T{} + runAssertionInGoroutineAndWaitForCompletion(func() { testenvs.AssertEnvNotSet(tut1, string(testenvs.User)) }) + + tut2 := &testing.T{} + runAssertionInGoroutineAndWaitForCompletion(func() { testenvs.AssertEnvSet(tut2, string(testenvs.User)) }) + + require.False(t, tut1.Failed()) + require.True(t, tut2.Failed()) + }) + + t.Run("test if env exists", func(t *testing.T) { + t.Setenv(string(testenvs.User), "user") + + tut1 := &testing.T{} + runAssertionInGoroutineAndWaitForCompletion(func() { testenvs.AssertEnvNotSet(tut1, string(testenvs.User)) }) + + tut2 := &testing.T{} + runAssertionInGoroutineAndWaitForCompletion(func() { testenvs.AssertEnvSet(tut2, string(testenvs.User)) }) + + require.True(t, tut1.Failed()) + require.False(t, tut2.Failed()) + }) +} diff --git a/pkg/acceptance/testenvs/testing_environment_variables.go b/pkg/acceptance/testenvs/testing_environment_variables.go new file mode 100644 index 0000000000..11882e8715 --- /dev/null +++ b/pkg/acceptance/testenvs/testing_environment_variables.go @@ -0,0 +1,32 @@ +package testenvs + +import ( + "fmt" + "os" + "testing" +) + +type env string + +const ( + User env = "TEST_SF_TF_USER" + Password env = "TEST_SF_TF_PASSWORD" // #nosec G101 + Account env = "TEST_SF_TF_ACCOUNT" + Role env = "TEST_SF_TF_ROLE" + Host env = "TEST_SF_TF_HOST" +) + +func GetOrSkipTest(t *testing.T, envName Env) string { + t.Helper() + env := os.Getenv(fmt.Sprintf("%v", envName)) + if env == "" { + t.Skipf("Skipping %s, env %v missing", t.Name(), envName) + } + return env +} + +type Env interface { + xxxProtected() +} + +func (e env) xxxProtected() {} diff --git a/pkg/acceptance/testprofiles/testing_config_profiles.go b/pkg/acceptance/testprofiles/testing_config_profiles.go new file mode 100644 index 0000000000..0fbef504ca --- /dev/null +++ b/pkg/acceptance/testprofiles/testing_config_profiles.go @@ -0,0 +1,7 @@ +package testprofiles + +const ( + Default = "default" + Secondary = "secondary_test_account" + IncorrectUserAndPassword = "incorrect_test_profile" +) diff --git a/pkg/internal/snowflakeenvs/snowflake_environment_variables.go b/pkg/internal/snowflakeenvs/snowflake_environment_variables.go new file mode 100644 index 0000000000..8c6d21101c --- /dev/null +++ b/pkg/internal/snowflakeenvs/snowflake_environment_variables.go @@ -0,0 +1,10 @@ +package snowflakeenvs + +const ( + Account = "SNOWFLAKE_ACCOUNT" + User = "SNOWFLAKE_USER" + Password = "SNOWFLAKE_PASSWORD" + Role = "SNOWFLAKE_ROLE" + ConfigPath = "SNOWFLAKE_CONFIG_PATH" + Host = "SNOWFLAKE_HOST" +) diff --git a/pkg/provider/provider_acceptance_test.go b/pkg/provider/provider_acceptance_test.go new file mode 100644 index 0000000000..ef4ce1bf93 --- /dev/null +++ b/pkg/provider/provider_acceptance_test.go @@ -0,0 +1,160 @@ +package provider_test + +import ( + "fmt" + "os" + "regexp" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/snowflakeenvs" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/stretchr/testify/require" +) + +func TestAcc_Provider_configHierarchy(t *testing.T) { + user := testenvs.GetOrSkipTest(t, testenvs.User) + pass := testenvs.GetOrSkipTest(t, testenvs.Password) + account := testenvs.GetOrSkipTest(t, testenvs.Account) + role := testenvs.GetOrSkipTest(t, testenvs.Role) + host := testenvs.GetOrSkipTest(t, testenvs.Host) + + nonExistingUser := "non-existing-user" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { + acc.TestAccPreCheck(t) + testenvs.AssertEnvNotSet(t, snowflakeenvs.User) + testenvs.AssertEnvNotSet(t, snowflakeenvs.Password) + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + // make sure that we fail for incorrect profile + { + Config: providerConfig(testprofiles.IncorrectUserAndPassword), + ExpectError: regexp.MustCompile("Incorrect username or password was specified"), + }, + // incorrect user in provider config should not be rewritten by profile and cause error + { + Config: providerConfigWithUser(nonExistingUser, testprofiles.Default), + ExpectError: regexp.MustCompile("Incorrect username or password was specified"), + }, + // correct user and password in provider's config should not be rewritten by a faulty config + { + Config: providerConfigWithUserAndPassword(user, pass, testprofiles.IncorrectUserAndPassword), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_database.t", "name", acc.TestDatabaseName), + ), + }, + // incorrect user in env variable should not be rewritten by profile and cause error + { + PreConfig: func() { + t.Setenv(snowflakeenvs.User, nonExistingUser) + }, + Config: providerConfig(testprofiles.Default), + ExpectError: regexp.MustCompile("Incorrect username or password was specified"), + }, + // correct user and password in env should not be rewritten by a faulty config + { + PreConfig: func() { + t.Setenv(snowflakeenvs.User, user) + t.Setenv(snowflakeenvs.Password, pass) + }, + Config: providerConfig(testprofiles.IncorrectUserAndPassword), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_database.t", "name", acc.TestDatabaseName), + ), + }, + // user on provider level wins (it's incorrect - env and profile ones are) + { + Config: providerConfigWithUser(nonExistingUser, testprofiles.Default), + ExpectError: regexp.MustCompile("Incorrect username or password was specified"), + }, + // there is no config (by setting the dir to something different than .snowflake/config) + { + PreConfig: func() { + dir, err := os.UserHomeDir() + require.NoError(t, err) + t.Setenv(snowflakeenvs.ConfigPath, dir) + }, + Config: providerConfigWithUserAndPassword(user, pass, testprofiles.Default), + ExpectError: regexp.MustCompile("account is empty"), + }, + // provider's config should not be rewritten by env when there is no profile (incorrect user in config versus correct one in env) - proves #2242 + { + PreConfig: func() { + testenvs.AssertEnvSet(t, snowflakeenvs.ConfigPath) + t.Setenv(snowflakeenvs.User, user) + t.Setenv(snowflakeenvs.Password, pass) + t.Setenv(snowflakeenvs.Account, account) + t.Setenv(snowflakeenvs.Role, role) + t.Setenv(snowflakeenvs.Host, host) + }, + Config: providerConfigWithUser(nonExistingUser, testprofiles.Default), + ExpectError: regexp.MustCompile("Incorrect username or password was specified"), + }, + // make sure the teardown is fine by using a correct env config at the end + { + PreConfig: func() { + testenvs.AssertEnvSet(t, snowflakeenvs.ConfigPath) + testenvs.AssertEnvSet(t, snowflakeenvs.User) + testenvs.AssertEnvSet(t, snowflakeenvs.Password) + testenvs.AssertEnvSet(t, snowflakeenvs.Account) + testenvs.AssertEnvSet(t, snowflakeenvs.Role) + testenvs.AssertEnvSet(t, snowflakeenvs.Host) + }, + Config: emptyProviderConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_database.t", "name", acc.TestDatabaseName), + ), + }, + }, + }) +} + +func emptyProviderConfig() string { + return ` +provider "snowflake" { +}` + datasourceConfig() +} + +func providerConfig(profile string) string { + return fmt.Sprintf(` +provider "snowflake" { + profile = "%[1]s" +} +`, profile) + datasourceConfig() +} + +func providerConfigWithUser(user string, profile string) string { + return fmt.Sprintf(` +provider "snowflake" { + user = "%[1]s" + profile = "%[2]s" +} +`, user, profile) + datasourceConfig() +} + +func providerConfigWithUserAndPassword(user string, pass string, profile string) string { + return fmt.Sprintf(` +provider "snowflake" { + user = "%[1]s" + password = "%[2]s" + profile = "%[3]s" +} +`, user, pass, profile) + datasourceConfig() +} + +func datasourceConfig() string { + return fmt.Sprintf(` +data snowflake_database "t" { + name = "%s" +}`, acc.TestDatabaseName) +} diff --git a/pkg/resources/database_acceptance_test.go b/pkg/resources/database_acceptance_test.go index 45d6be2903..1f9a3c46e2 100644 --- a/pkg/resources/database_acceptance_test.go +++ b/pkg/resources/database_acceptance_test.go @@ -8,6 +8,7 @@ import ( acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" @@ -228,7 +229,7 @@ func dropDatabaseOutsideTerraform(t *testing.T, id string) { func getSecondaryAccount(t *testing.T) string { t.Helper() - secondaryConfig, err := sdk.ProfileConfig("secondary_test_account") + secondaryConfig, err := sdk.ProfileConfig(testprofiles.Secondary) require.NoError(t, err) secondaryClient, err := sdk.NewClient(secondaryConfig) diff --git a/pkg/resources/grant_privileges_to_account_role_acceptance_test.go b/pkg/resources/grant_privileges_to_account_role_acceptance_test.go index a2c99d1327..1f8e6d260b 100644 --- a/pkg/resources/grant_privileges_to_account_role_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_account_role_acceptance_test.go @@ -10,18 +10,18 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/hashicorp/terraform-plugin-testing/helper/acctest" - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAcc_GrantPrivilegesToAccountRole_OnAccount(t *testing.T) { @@ -931,7 +931,7 @@ func TestAcc_GrantPrivilegesToAccountRole_ImportedPrivileges(t *testing.T) { func getSecondaryAccountName(t *testing.T) (string, error) { t.Helper() - config, err := sdk.ProfileConfig("secondary_test_account") + config, err := sdk.ProfileConfig(testprofiles.Secondary) if err != nil { t.Fatal(err) } @@ -953,7 +953,7 @@ func getAccountName(t *testing.T) (string, error) { func createSharedDatabaseOnSecondaryAccount(t *testing.T, databaseName string, shareName string) error { t.Helper() - config, err := sdk.ProfileConfig("secondary_test_account") + config, err := sdk.ProfileConfig(testprofiles.Secondary) if err != nil { t.Fatal(err) } @@ -976,7 +976,7 @@ func createSharedDatabaseOnSecondaryAccount(t *testing.T, databaseName string, s func dropSharedDatabaseOnSecondaryAccount(t *testing.T, databaseName string, shareName string) error { t.Helper() - config, err := sdk.ProfileConfig("secondary_test_account") + config, err := sdk.ProfileConfig(testprofiles.Secondary) if err != nil { t.Fatal(err) } diff --git a/pkg/sdk/client_integration_test.go b/pkg/sdk/client_integration_test.go index fdc1ce666d..9498d6b2d7 100644 --- a/pkg/sdk/client_integration_test.go +++ b/pkg/sdk/client_integration_test.go @@ -6,6 +6,9 @@ import ( "os" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/snowflakeenvs" "github.com/snowflakedb/gosnowflake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,12 +21,36 @@ func TestClient_NewClient(t *testing.T) { require.NoError(t, err) }) - t.Run("uses env vars if values are missing", func(t *testing.T) { - cleanupEnvVars := setupEnvVars(t, "TEST_ACCOUNT", "TEST_USER", "abcd1234", "ACCOUNTADMIN", "") - t.Cleanup(cleanupEnvVars) - config := EnvConfig() - _, err := NewClient(config) - require.Error(t, err) + t.Run("with missing config", func(t *testing.T) { + dir, err := os.UserHomeDir() + require.NoError(t, err) + t.Setenv(snowflakeenvs.ConfigPath, dir) + + config := DefaultConfig() + _, err = NewClient(config) + require.ErrorContains(t, err, "260000: account is empty") + }) + + t.Run("with incorrect config", func(t *testing.T) { + config, err := ProfileConfig(testprofiles.IncorrectUserAndPassword) + require.NoError(t, err) + require.NotNil(t, config) + + _, err = NewClient(config) + require.ErrorContains(t, err, "Incorrect username or password was specified") + }) + + t.Run("with missing config - should not care about correct env variables", func(t *testing.T) { + account := testenvs.GetOrSkipTest(t, testenvs.Account) + t.Setenv(snowflakeenvs.Account, account) + + dir, err := os.UserHomeDir() + require.NoError(t, err) + t.Setenv(snowflakeenvs.ConfigPath, dir) + + config := DefaultConfig() + _, err = NewClient(config) + require.ErrorContains(t, err, "260000: account is empty") }) t.Run("registers snowflake-instrumented driver", func(t *testing.T) { diff --git a/pkg/sdk/config.go b/pkg/sdk/config.go index b30cb85acb..8c4441c330 100644 --- a/pkg/sdk/config.go +++ b/pkg/sdk/config.go @@ -12,8 +12,8 @@ import ( func DefaultConfig() *gosnowflake.Config { config, err := ProfileConfig("default") if err != nil || config == nil { - log.Printf("[DEBUG] No Snowflake config file found, falling back to environment variables: %v\n", err) - return EnvConfig() + log.Printf("[DEBUG] No Snowflake config file found, returning empty config: %v\n", err) + config = &gosnowflake.Config{} } return config } @@ -51,22 +51,22 @@ func MergeConfig(baseConfig *gosnowflake.Config, mergeConfig *gosnowflake.Config if baseConfig == nil { return mergeConfig } - if mergeConfig.Account != "" { + if baseConfig.Account == "" { baseConfig.Account = mergeConfig.Account } - if mergeConfig.User != "" { + if baseConfig.User == "" { baseConfig.User = mergeConfig.User } - if mergeConfig.Password != "" { + if baseConfig.Password == "" { baseConfig.Password = mergeConfig.Password } - if mergeConfig.Role != "" { + if baseConfig.Role == "" { baseConfig.Role = mergeConfig.Role } - if mergeConfig.Region != "" { + if baseConfig.Region == "" { baseConfig.Region = mergeConfig.Region } - if mergeConfig.Host != "" { + if baseConfig.Host == "" { baseConfig.Host = mergeConfig.Host } return baseConfig @@ -87,34 +87,6 @@ func configFile() (string, error) { return filepath.Join(dir, ".snowflake", "config"), nil } -func EnvConfig() *gosnowflake.Config { - config := &gosnowflake.Config{} - - if account, ok := os.LookupEnv("SNOWFLAKE_ACCOUNT"); ok { - config.Account = account - } - if user, ok := os.LookupEnv("SNOWFLAKE_USER"); ok { - config.User = user - } - if password, ok := os.LookupEnv("SNOWFLAKE_PASSWORD"); ok { - config.Password = password - } - if role, ok := os.LookupEnv("SNOWFLAKE_ROLE"); ok { - config.Role = role - } - if region, ok := os.LookupEnv("SNOWFLAKE_REGION"); ok { - config.Region = region - } - if host, ok := os.LookupEnv("SNOWFLAKE_HOST"); ok { - config.Host = host - } - if warehouse, ok := os.LookupEnv("SNOWFLAKE_WAREHOUSE"); ok { - config.Warehouse = warehouse - } - - return config -} - func loadConfigFile() (map[string]*gosnowflake.Config, error) { path, err := configFile() if err != nil { diff --git a/pkg/sdk/config_test.go b/pkg/sdk/config_test.go index 71a33f5533..d34d812a88 100644 --- a/pkg/sdk/config_test.go +++ b/pkg/sdk/config_test.go @@ -5,6 +5,8 @@ import ( "path/filepath" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/snowflakeenvs" + "github.com/snowflakedb/gosnowflake" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,8 +26,8 @@ func TestLoadConfigFile(t *testing.T) { role='SECURITYADMIN' ` configPath := testFile(t, "config", []byte(c)) - cleanupEnvVars := setupEnvVars(t, "", "", "", "", configPath) - t.Cleanup(cleanupEnvVars) + t.Setenv(snowflakeenvs.ConfigPath, configPath) + m, err := loadConfigFile() require.NoError(t, err) assert.Equal(t, "TEST_ACCOUNT", m["default"].Account) @@ -49,8 +51,8 @@ func TestProfileConfig(t *testing.T) { configPath := testFile(t, "config", []byte(c)) t.Run("with found profile", func(t *testing.T) { - cleanupEnvVars := setupEnvVars(t, "", "", "", "", configPath) - t.Cleanup(cleanupEnvVars) + t.Setenv(snowflakeenvs.ConfigPath, configPath) + config, err := ProfileConfig("securityadmin") require.NoError(t, err) assert.Equal(t, "TEST_ACCOUNT", config.Account) @@ -60,33 +62,72 @@ func TestProfileConfig(t *testing.T) { }) t.Run("with not found profile", func(t *testing.T) { - cleanupEnvVars := setupEnvVars(t, "", "", "", "", configPath) - t.Cleanup(cleanupEnvVars) + t.Setenv(snowflakeenvs.ConfigPath, configPath) + config, err := ProfileConfig("orgadmin") require.NoError(t, err) require.Nil(t, config) }) + + t.Run("with not found config", func(t *testing.T) { + dir, err := os.UserHomeDir() + require.NoError(t, err) + t.Setenv(snowflakeenvs.ConfigPath, dir) + + config, err := ProfileConfig("orgadmin") + require.Error(t, err) + require.Nil(t, config) + }) } -func TestEnvConfig(t *testing.T) { - t.Run("with no environment variables", func(t *testing.T) { - cleanupEnvVars := setupEnvVars(t, "", "", "", "", "") - t.Cleanup(cleanupEnvVars) - config := EnvConfig() - assert.Equal(t, "", config.Account) - assert.Equal(t, "", config.User) - assert.Equal(t, "", config.Password) - assert.Equal(t, "", config.Role) +func Test_MergeConfig(t *testing.T) { + createConfig := func(user string, password string, account string, region string) *gosnowflake.Config { + return &gosnowflake.Config{ + User: user, + Password: password, + Account: account, + Region: region, + } + } + + t.Run("merge configs", func(t *testing.T) { + config1 := createConfig("user", "password", "account", "") + config2 := createConfig("user2", "", "", "region2") + + config := MergeConfig(config1, config2) + + require.Equal(t, "user", config.User) + require.Equal(t, "password", config.Password) + require.Equal(t, "account", config.Account) + require.Equal(t, "region2", config.Region) + require.Equal(t, "", config.Role) + + require.Equal(t, config1, config) + require.Equal(t, "user", config1.User) + require.Equal(t, "password", config1.Password) + require.Equal(t, "account", config1.Account) + require.Equal(t, "region2", config1.Region) + require.Equal(t, "", config1.Role) }) - t.Run("with environment variables", func(t *testing.T) { - cleanupEnvVars := setupEnvVars(t, "TEST_ACCOUNT", "TEST_USER", "abcd1234", "ACCOUNTADMIN", "") - t.Cleanup(cleanupEnvVars) - config := EnvConfig() - assert.Equal(t, "TEST_ACCOUNT", config.Account) - assert.Equal(t, "TEST_USER", config.User) - assert.Equal(t, "abcd1234", config.Password) - assert.Equal(t, "ACCOUNTADMIN", config.Role) + t.Run("merge configs inverted", func(t *testing.T) { + config1 := createConfig("user", "password", "account", "") + config2 := createConfig("user2", "", "", "region2") + + config := MergeConfig(config2, config1) + + require.Equal(t, "user2", config.User) + require.Equal(t, "password", config.Password) + require.Equal(t, "account", config.Account) + require.Equal(t, "region2", config.Region) + require.Equal(t, "", config.Role) + + require.Equal(t, config2, config) + require.Equal(t, "user2", config2.User) + require.Equal(t, "password", config2.Password) + require.Equal(t, "account", config2.Account) + require.Equal(t, "region2", config2.Region) + require.Equal(t, "", config2.Role) }) } @@ -97,26 +138,3 @@ func testFile(t *testing.T, filename string, dat []byte) string { require.NoError(t, err) return path } - -func setupEnvVars(t *testing.T, account, user, password, role, configPath string) func() { - t.Helper() - orginalAccount := os.Getenv("SNOWFLAKE_ACCOUNT") - orginalUser := os.Getenv("SNOWFLAKE_USER") - originalPassword := os.Getenv("SNOWFLAKE_PASSWORD") - originalRole := os.Getenv("SNOWFLAKE_ROLE") - originalPath := os.Getenv("SNOWFLAKE_CONFIG_PATH") - - os.Setenv("SNOWFLAKE_ACCOUNT", account) - os.Setenv("SNOWFLAKE_USER", user) - os.Setenv("SNOWFLAKE_PASSWORD", password) - os.Setenv("SNOWFLAKE_ROLE", role) - os.Setenv("SNOWFLAKE_CONFIG_PATH", configPath) - - return func() { - os.Setenv("SNOWFLAKE_ACCOUNT", orginalAccount) - os.Setenv("SNOWFLAKE_USER", orginalUser) - os.Setenv("SNOWFLAKE_PASSWORD", originalPassword) - os.Setenv("SNOWFLAKE_ROLE", originalRole) - os.Setenv("SNOWFLAKE_CONFIG_PATH", originalPath) - } -} diff --git a/pkg/sdk/helper_test.go b/pkg/sdk/helper_test.go index c6b7c68f32..2de6670556 100644 --- a/pkg/sdk/helper_test.go +++ b/pkg/sdk/helper_test.go @@ -2,6 +2,8 @@ package sdk import ( "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" ) func testClient(t *testing.T) *Client { @@ -15,16 +17,12 @@ func testClient(t *testing.T) *Client { return client } -const ( - secondaryAccountProfile = "secondary_test_account" -) - func testSecondaryClient(t *testing.T) *Client { t.Helper() - client, err := testClientFromProfile(t, secondaryAccountProfile) + client, err := testClientFromProfile(t, testprofiles.Secondary) if err != nil { - t.Skipf("Snowflake secondary account not configured. Must be set in ~./snowflake/config.yml with profile name: %s", secondaryAccountProfile) + t.Skipf("Snowflake secondary account not configured. Must be set in ~./snowflake/config.yml with profile name: %s", testprofiles.Secondary) } return client diff --git a/pkg/sdk/testint/setup_test.go b/pkg/sdk/testint/setup_test.go index 2a0a8b5ee6..945073ff69 100644 --- a/pkg/sdk/testint/setup_test.go +++ b/pkg/sdk/testint/setup_test.go @@ -7,14 +7,11 @@ import ( "testing" "time" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" ) -const ( - secondaryAccountProfile = "secondary_test_account" -) - var ( TestWarehouseName = "int_test_wh_" + random.UUID() TestDatabaseName = "int_test_db_" + random.UUID() @@ -116,7 +113,7 @@ func (itc *integrationTestContext) initialize() error { itc.warehouse = wh itc.warehouseCleanup = whCleanup - config, err := sdk.ProfileConfig(secondaryAccountProfile) + config, err := sdk.ProfileConfig(testprofiles.Secondary) if err != nil { return err } diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 28bae7f604..bbd6ca78e1 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -7,7 +7,7 @@ description: Manage SnowflakeDB with Terraform. ~> **Disclaimer** the project is still in the 0.x.x version, which means it’s still in the experimental phase (check [Go module versioning](https://go.dev/doc/modules/version-numbers#v0-number) for more details). It can be used in production but makes no stability or backward compatibility guarantees. We do not provide backward bug fixes and, therefore, always suggest using the newest version. We are providing only limited support for the provider; priorities will be assigned on a case-by-case basis. Our main current goals are stabilization, addressing existing issues, and providing the missing features (prioritizing the GA features; supporting PrPr and PuPr features are not high priorities now). With all that in mind, we aim to reach V1 with a stable, reliable, and functional provider. V1 will be free of all the above limitations. --> **Note** Please check the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md) when changing the version of the provider. +~> **Note** Please check the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md) when changing the version of the provider. -> **Note** the current roadmap is available in our GitHub repository: [ROADMAP.md](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md).