diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 0082d9442c..30898f1c5b 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -461,7 +461,9 @@ resource "snowflake_database" "test" { } ``` -If you had `from_database` set, it should migrate automatically. +If you had `from_database` set, you should follow our [resource migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md) to remove +the database from state to later import it in the newer version of the provider. +Otherwise, it may cause issues when migrating to v0.93.0. For now, we're dropping the possibility to create a clone database from other databases. The only way will be to clone a database manually and import it as `snowflake_database`, but if cloned databases diverge in behavior from standard databases, it may cause issues. diff --git a/docs/resources/database.md b/docs/resources/database.md index 8a8ca5f778..8371e9d137 100644 --- a/docs/resources/database.md +++ b/docs/resources/database.md @@ -90,6 +90,7 @@ resource "snowflake_database" "primary" { - `comment` (String) Specifies a comment for the database. - `data_retention_time_in_days` (Number) Specifies the number of days for which Time Travel actions (CLONE and UNDROP) can be performed on the database, as well as specifying the default Time Travel retention time for all schemas created in the database. For more details, see [Understanding & Using Time Travel](https://docs.snowflake.com/en/user-guide/data-time-travel). - `default_ddl_collation` (String) Specifies a default collation specification for all schemas and tables added to the database. It can be overridden on schema or table level. For more information, see [collation specification](https://docs.snowflake.com/en/sql-reference/collation#label-collation-specification). +- `drop_public_schema_on_creation` (Boolean) Specifies whether to drop public schema on creation or not. Modifying the parameter after database is already created won't have any effect. - `enable_console_output` (Boolean) If true, enables stdout/stderr fast path logging for anonymous stored procedures. - `external_volume` (String) The database parameter that specifies the default external volume to use for Iceberg tables. For more information, see [EXTERNAL_VOLUME](https://docs.snowflake.com/en/sql-reference/parameters#external-volume). - `is_transient` (Boolean) Specifies the database as transient. Transient databases do not have a Fail-safe period so they do not incur additional storage costs once they leave Time Travel; however, this means they are also not protected by Fail-safe in the event of a data loss. diff --git a/pkg/acceptance/helpers/database_client.go b/pkg/acceptance/helpers/database_client.go index bc657a1fa3..e0dc584dfd 100644 --- a/pkg/acceptance/helpers/database_client.go +++ b/pkg/acceptance/helpers/database_client.go @@ -146,3 +146,13 @@ func (c *DatabaseClient) Show(t *testing.T, id sdk.AccountObjectIdentifier) (*sd return c.client().ShowByID(ctx, id) } + +func (c *DatabaseClient) Describe(t *testing.T, id sdk.AccountObjectIdentifier) *sdk.DatabaseDetails { + t.Helper() + ctx := context.Background() + + details, err := c.client().Describe(ctx, id) + require.NoError(t, err) + + return details +} diff --git a/pkg/acceptance/snowflakechecks/database.go b/pkg/acceptance/snowflakechecks/database.go index f3630aa2e1..d43601fa66 100644 --- a/pkg/acceptance/snowflakechecks/database.go +++ b/pkg/acceptance/snowflakechecks/database.go @@ -3,6 +3,7 @@ package snowflakechecks import ( "errors" "fmt" + "slices" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" @@ -26,3 +27,23 @@ func CheckDatabaseDataRetentionTimeInDays(t *testing.T, databaseId sdk.AccountOb return errors.Join(errs...) } } + +func DoesNotContainPublicSchema(t *testing.T, id sdk.AccountObjectIdentifier) resource.TestCheckFunc { + t.Helper() + return func(state *terraform.State) error { + if slices.ContainsFunc(acc.TestClient().Database.Describe(t, id).Rows, func(row sdk.DatabaseDetailsRow) bool { return row.Name == "PUBLIC" && row.Kind == "SCHEMA" }) { + return fmt.Errorf("expected database %s to not contain public schema", id.FullyQualifiedName()) + } + return nil + } +} + +func ContainsPublicSchema(t *testing.T, id sdk.AccountObjectIdentifier) resource.TestCheckFunc { + t.Helper() + return func(state *terraform.State) error { + if !slices.ContainsFunc(acc.TestClient().Database.Describe(t, id).Rows, func(row sdk.DatabaseDetailsRow) bool { return row.Name == "PUBLIC" && row.Kind == "SCHEMA" }) { + return fmt.Errorf("expected database %s to contain public schema", id.FullyQualifiedName()) + } + return nil + } +} diff --git a/pkg/resources/database.go b/pkg/resources/database.go index 5044cb563d..63eb9e8271 100644 --- a/pkg/resources/database.go +++ b/pkg/resources/database.go @@ -6,6 +6,9 @@ import ( "fmt" "slices" "strings" + "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" "github.com/hashicorp/go-cty/cty" @@ -22,6 +25,12 @@ var databaseSchema = map[string]*schema.Schema{ Required: true, Description: "Specifies the identifier for the database; must be unique for your account. As a best practice for [Database Replication and Failover](https://docs.snowflake.com/en/user-guide/db-replication-intro), it is recommended to give each secondary database the same name as its primary database. This practice supports referencing fully-qualified objects (i.e. '..') by other objects in the same database, such as querying a fully-qualified table name in a view. If a secondary database has a different name from the primary database, then these object references would break in the secondary database.", }, + "drop_public_schema_on_creation": { + Type: schema.TypeBool, + Optional: true, + Description: "Specifies whether to drop public schema on creation or not. Modifying the parameter after database is already created won't have any effect.", + DiffSuppressFunc: IgnoreAfterCreation, + }, "is_transient": { Type: schema.TypeBool, Optional: true, @@ -124,6 +133,24 @@ func CreateDatabase(ctx context.Context, d *schema.ResourceData, meta any) diag. var diags diag.Diagnostics + if d.Get("drop_public_schema_on_creation").(bool) { + var dropSchemaErrs []error + err := util.Retry(3, time.Second, func() (error, bool) { + if err := client.Schemas.Drop(ctx, sdk.NewDatabaseObjectIdentifier(id.Name(), "PUBLIC"), &sdk.DropSchemaOptions{IfExists: sdk.Bool(true)}); err != nil { + dropSchemaErrs = append(dropSchemaErrs, err) + return nil, false + } + return nil, true + }) + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to drop public schema on creation (failed after 3 attempts)", + Detail: fmt.Sprintf("The '%s' database was created successfully, but the provider was not able to remove public schema on creation. Please drop the public schema manually. Original errors: %s", id.Name(), errors.Join(dropSchemaErrs...)), + }) + } + } + if v, ok := d.GetOk("replication"); ok { replicationConfiguration := v.([]any)[0].(map[string]any) diff --git a/pkg/resources/database_acceptance_test.go b/pkg/resources/database_acceptance_test.go index d79e3d4797..eed0d2691b 100644 --- a/pkg/resources/database_acceptance_test.go +++ b/pkg/resources/database_acceptance_test.go @@ -2,7 +2,6 @@ package resources_test import ( "fmt" - "regexp" "strconv" "testing" @@ -15,7 +14,6 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/snowflakechecks" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-testing/config" @@ -1160,233 +1158,81 @@ resource "snowflake_database" "test" { `, id.Name(), strconv.Quote(enableToAccount)) } -func TestAcc_Database_UpgradeFromShare(t *testing.T) { - _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) - +func TestAcc_Database_WithoutPublicSchema(t *testing.T) { id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - secondaryClientLocator := acc.SecondaryClient(t).GetAccountLocator() - - shareExternalId := createShareableDatabase(t) resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, + CheckDestroy: acc.CheckDestroy(t, resources.Database), Steps: []resource.TestStep{ { - ExternalProviders: map[string]resource.ExternalProvider{ - "snowflake": { - VersionConstraint: "=0.92.0", - Source: "Snowflake-Labs/snowflake", - }, - }, - Config: databaseStateUpgraderFromShareOld(id, secondaryClientLocator, shareExternalId), + Config: databaseWithDropPublicSchemaConfig(id, true), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_database.test", "id", id.Name()), - resource.TestCheckResourceAttr("snowflake_database.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_database.test", "from_share.provider", secondaryClientLocator), - resource.TestCheckResourceAttr("snowflake_database.test", "from_share.share", shareExternalId.Name()), + snowflakechecks.DoesNotContainPublicSchema(t, id), ), }, + // Change in parameter shouldn't change the state Snowflake { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromShareNewAfterUpgrade(id), - ExpectError: regexp.MustCompile("failed to upgrade the state with database created from share, please use snowflake_shared_database or deprecated snowflake_database_old instead"), - }, - { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromShareNew(id, shareExternalId), - ResourceName: "snowflake_shared_database.test", - ImportStateId: id.FullyQualifiedName(), - ImportState: true, - }, - }, - }) -} - -func databaseStateUpgraderFromShareOld(id sdk.AccountObjectIdentifier, secondaryClientLocator string, externalShare sdk.ExternalObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" - data_retention_time_in_days = 0 # to avoid in-place update to -1 - from_share = { - provider = "%s" - share = "%s" - } -} -`, id.Name(), secondaryClientLocator, externalShare.Name()) -} - -func databaseStateUpgraderFromShareNewAfterUpgrade(id sdk.AccountObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" - data_retention_time_in_days = 0 # to avoid in-place update to -1 -} -`, id.Name()) -} - -func databaseStateUpgraderFromShareNew(id sdk.AccountObjectIdentifier, externalShare sdk.ExternalObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_shared_database" "test" { - name = "%s" - from_share = %s -} -`, id.Name(), strconv.Quote(externalShare.FullyQualifiedName())) -} - -func TestAcc_Database_UpgradeFromReplica(t *testing.T) { - _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) - - id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - _, primaryDatabaseId, databaseCleanup := acc.SecondaryTestClient().Database.CreatePrimaryDatabase(t, []sdk.AccountIdentifier{ - acc.TestClient().Account.GetAccountIdentifier(t), - }) - t.Cleanup(databaseCleanup) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - Steps: []resource.TestStep{ - { - ExternalProviders: map[string]resource.ExternalProvider{ - "snowflake": { - VersionConstraint: "=0.92.0", - Source: "Snowflake-Labs/snowflake", + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionNoop), }, }, - Config: databaseStateUpgraderFromReplicaOld(id, primaryDatabaseId), + Config: databaseWithDropPublicSchemaConfig(id, false), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_database.test", "id", id.Name()), - resource.TestCheckResourceAttr("snowflake_database.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_database.test", "from_replica", primaryDatabaseId.FullyQualifiedName()), + snowflakechecks.DoesNotContainPublicSchema(t, id), ), }, - { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromReplicaNewAfterUpgrade(id), - ExpectError: regexp.MustCompile("failed to upgrade the state with database created from replica, please use snowflake_secondary_database or deprecated snowflake_database_old instead"), - }, - { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromReplicaNew(id, primaryDatabaseId), - ResourceName: "snowflake_secondary_database.test", - ImportStateId: id.FullyQualifiedName(), - ImportState: true, - }, }, }) } -func databaseStateUpgraderFromReplicaOld(id sdk.AccountObjectIdentifier, primaryDatabaseId sdk.ExternalObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" - data_retention_time_in_days = 0 # to avoid in-place update to -1 - from_replica = %s -} -`, id.Name(), strconv.Quote(primaryDatabaseId.FullyQualifiedName())) -} - -func databaseStateUpgraderFromReplicaNewAfterUpgrade(id sdk.AccountObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" - data_retention_time_in_days = 0 -} -`, id.Name()) -} - -func databaseStateUpgraderFromReplicaNew(id sdk.AccountObjectIdentifier, primaryDatabaseId sdk.ExternalObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_secondary_database" "test" { - name = "%s" - as_replica_of = %s -} -`, id.Name(), strconv.Quote(id.FullyQualifiedName())) -} - -func TestAcc_Database_UpgradeFromClonedDatabase(t *testing.T) { +func TestAcc_Database_WithPublicSchema(t *testing.T) { id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - cloneId := acc.TestClient().Ids.RandomAccountObjectIdentifier() resource.Test(t, resource.TestCase{ - PreCheck: func() { acc.TestAccPreCheck(t) }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, CheckDestroy: acc.CheckDestroy(t, resources.Database), Steps: []resource.TestStep{ { - ExternalProviders: map[string]resource.ExternalProvider{ - "snowflake": { - VersionConstraint: "=0.92.0", - Source: "Snowflake-Labs/snowflake", - }, - }, - Config: databaseStateUpgraderFromDatabaseOld(id, cloneId), + Config: databaseWithDropPublicSchemaConfig(id, false), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_database.cloned", "id", cloneId.Name()), - resource.TestCheckResourceAttr("snowflake_database.cloned", "name", cloneId.Name()), - resource.TestCheckResourceAttr("snowflake_database.cloned", "from_database", id.Name()), + resource.TestCheckResourceAttr("snowflake_database.test", "id", id.Name()), + snowflakechecks.ContainsPublicSchema(t, id), ), }, + // Change in parameter shouldn't change the state Snowflake { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromDatabaseNewAfterUpgrade(id, cloneId), - ExpectError: regexp.MustCompile("failed to upgrade the state with database created from database, please use snowflake_database or deprecated snowflake_database_old instead"), - }, - { - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - Config: databaseStateUpgraderFromDatabaseNew(id, cloneId), - ResourceName: "snowflake_database.cloned", - ImportStateId: cloneId.FullyQualifiedName(), - ImportState: true, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_database.test", plancheck.ResourceActionNoop), + }, + }, + Config: databaseWithDropPublicSchemaConfig(id, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_database.test", "id", id.Name()), + snowflakechecks.ContainsPublicSchema(t, id), + ), }, }, }) } -func databaseStateUpgraderFromDatabaseOld(id sdk.AccountObjectIdentifier, secondId sdk.AccountObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" - data_retention_time_in_days = 0 # to avoid in-place update to -1 -} - -resource "snowflake_database" "cloned" { - name = "%s" - data_retention_time_in_days = 0 # to avoid in-place update to -1 - from_database = snowflake_database.test.name -} -`, id.Name(), secondId.Name()) -} - -func databaseStateUpgraderFromDatabaseNewAfterUpgrade(id sdk.AccountObjectIdentifier, secondId sdk.AccountObjectIdentifier) string { +func databaseWithDropPublicSchemaConfig(id sdk.AccountObjectIdentifier, withDropPublicSchema bool) string { return fmt.Sprintf(` resource "snowflake_database" "test" { name = "%s" - data_retention_time_in_days = 0 -} - -resource "snowflake_database" "cloned" { - name = "%s" - data_retention_time_in_days = 0 -} -`, id.Name(), secondId.Name()) -} - -func databaseStateUpgraderFromDatabaseNew(id sdk.AccountObjectIdentifier, secondId sdk.AccountObjectIdentifier) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%s" -} - -resource "snowflake_database" "cloned" { - name = "%s" + drop_public_schema_on_creation = %s } -`, id.Name(), secondId.Name()) +`, id.Name(), strconv.FormatBool(withDropPublicSchema)) } diff --git a/pkg/resources/manual_tests/README.md b/pkg/resources/manual_tests/README.md new file mode 100644 index 0000000000..f9a06a475f --- /dev/null +++ b/pkg/resources/manual_tests/README.md @@ -0,0 +1,21 @@ +# Manual tests + +This directory is dedicated to hold steps for manual tests that are not possible to re-recreate in automated acceptance tests. +The main limitations come from using [terraform-plugin-testing](https://github.com/hashicorp/terraform-plugin-testing) which is +not supporting every action you are able to perform with Terraform CLI. + +Here's the list of cases we currently cannot reproduce and write acceptance tests for: +- When upgrading from version to version we need to remove the state of the deprecated object (terraform state rm) and import a new type representing the same object (terraform import) + - Specifically, `terraform state rm` is not possible. As an example we can have `snowflake_database` in version 0.92.0 which in version 0.93.0 could be represented as e.g. `snowflake_shared_database`. To fully test such upgrade path, it has to be done manually. + - Currently tests under this category: + - `upgrade_cloned_database` + - `upgrade_secondary_database` + - `upgrade_shared_database` + +## How to use manual tests +- Choose the test you want to run and go into the test folder. +- Take the first step from that test copy it into a separate folder (outside the project) to initialize terraform and start the first step. + - The tests contain provider configuration, but you have to make sure you have compliant configuration in your `~/snowflake/config` file. + - Please mind which commands should be run to perform a given test step correctly (instructions are at the top of the file; run after project is initialized). + - Also, please mind `TODO: Replace` comments that indicate lines where configuration should be changed before running any test command. +- To proceed with the test, take the content of the next test step file and replace the previous one (with the same rules as above). \ No newline at end of file diff --git a/pkg/resources/manual_tests/upgrade_cloned_database/step_1.tf b/pkg/resources/manual_tests/upgrade_cloned_database/step_1.tf new file mode 100644 index 0000000000..fc29a277dd --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_cloned_database/step_1.tf @@ -0,0 +1,24 @@ +# Commands to run +# - terraform apply + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = "0.92.0" + } + } +} + +provider "snowflake" {} + +resource "snowflake_database" "test" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} + +resource "snowflake_database" "cloned" { + name = "cloned" + from_database = snowflake_database.test.name + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} diff --git a/pkg/resources/manual_tests/upgrade_cloned_database/step_2.tf b/pkg/resources/manual_tests/upgrade_cloned_database/step_2.tf new file mode 100644 index 0000000000..f6446ce3a2 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_cloned_database/step_2.tf @@ -0,0 +1,26 @@ +# Commands to run +# - terraform init - upgrade +# - terraform plan (should observe upgrader errors similar to: failed to upgrade the state with database created from database, please use snowflake_database or deprecated snowflake_database_old instead...) +# - terraform state rm snowflake_database.cloned (remove cloned database from the state) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +resource "snowflake_database" "test" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} + +resource "snowflake_database" "cloned" { + name = "cloned" + from_database = snowflake_database.test.name + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} diff --git a/pkg/resources/manual_tests/upgrade_cloned_database/step_3.tf b/pkg/resources/manual_tests/upgrade_cloned_database/step_3.tf new file mode 100644 index 0000000000..1fa465dcc4 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_cloned_database/step_3.tf @@ -0,0 +1,24 @@ +# Commands to run +# - terraform import snowflake__database.cloned '"cloned"' (import cloned database into state) +# - terraform plan (expect empty plan) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +resource "snowflake_database" "test" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} + +resource "snowflake_database" "cloned" { + name = "cloned" + data_retention_time_in_days = 0 # to avoid in-place update to -1 +} diff --git a/pkg/resources/manual_tests/upgrade_secondary_database/step_1.tf b/pkg/resources/manual_tests/upgrade_secondary_database/step_1.tf new file mode 100644 index 0000000000..268b636c8a --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_secondary_database/step_1.tf @@ -0,0 +1,34 @@ +# Commands to run +# - terraform apply + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = "0.92.0" + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_database" "primary" { + provider = snowflake.second_account + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + replication_configuration { + accounts = [""] # TODO: Replace + ignore_edition_check = true + } +} + +resource "snowflake_database" "secondary" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + from_replica = ".\"${snowflake_database.primary.name}\"" # TODO: Replace +} diff --git a/pkg/resources/manual_tests/upgrade_secondary_database/step_2.tf b/pkg/resources/manual_tests/upgrade_secondary_database/step_2.tf new file mode 100644 index 0000000000..599e971a51 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_secondary_database/step_2.tf @@ -0,0 +1,36 @@ +# Commands to run +# - terraform init - upgrade +# - terraform plan (should observe upgrader errors similar to: failed to upgrade the state with database created from replica, please use snowflake_secondary_database or deprecated snowflake_database_old instead) +# - terraform state rm snowflake_database.secondary (remove secondary database from the state) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_database" "primary" { + provider = snowflake.second_account + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + replication_configuration { + accounts = [""] # TODO: Replace + ignore_edition_check = true + } +} + +resource "snowflake_database" "secondary" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + from_replica = ".\"${snowflake_database.primary.name}\"" # TODO: Replace +} diff --git a/pkg/resources/manual_tests/upgrade_secondary_database/step_3.tf b/pkg/resources/manual_tests/upgrade_secondary_database/step_3.tf new file mode 100644 index 0000000000..d112fe2927 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_secondary_database/step_3.tf @@ -0,0 +1,38 @@ +# Commands to run +# - terraform import snowflake_secondary_database.secondary '"test"' (import secondary database into state) +# - terraform plan (expect empty plan) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_database" "primary" { + provider = snowflake.second_account + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + replication { + enable_to_account { + account_identifier = "." # TODO: Replace + with_failover = true + } + ignore_edition_check = true + } +} + +resource "snowflake_secondary_database" "secondary" { + name = "test" + data_retention_time_in_days = 0 # to avoid in-place update to -1 + from_replica = "\"\".\"\".\"${snowflake_database.primary.name}\"" # TODO: Replace +} diff --git a/pkg/resources/manual_tests/upgrade_shared_database/step_1.tf b/pkg/resources/manual_tests/upgrade_shared_database/step_1.tf new file mode 100644 index 0000000000..f6a9bac28b --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_shared_database/step_1.tf @@ -0,0 +1,45 @@ +# Commands to run +# - terraform apply + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = "0.92.0" + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_share" "test" { + provider = snowflake.second_account + name = "test_share" + accounts = ["."] # TODO: Replace +} + +resource "snowflake_database" "test" { + provider = snowflake.second_account + name = "test_database" +} + +resource "snowflake_grant_privileges_to_share" "test" { + provider = snowflake.second_account + privileges = ["USAGE"] + on_database = snowflake_database.test.name + to_share = snowflake_share.test.name +} + +resource "snowflake_database" "from_share" { + depends_on = [ snowflake_grant_privileges_to_share.test ] + name = snowflake_database.test.name + from_share = { + provider = "" # TODO: Replace + share = snowflake_share.test.name + } +} diff --git a/pkg/resources/manual_tests/upgrade_shared_database/step_2.tf b/pkg/resources/manual_tests/upgrade_shared_database/step_2.tf new file mode 100644 index 0000000000..ed8bce3132 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_shared_database/step_2.tf @@ -0,0 +1,47 @@ +# Commands to run +# - terraform init - upgrade +# - terraform plan (should observe upgrader errors similar to: failed to upgrade the state with database created from share, please use snowflake_shared_database or deprecated snowflake_database_old instead) +# - terraform state rm snowflake_database.from_share (remove shared database from the state) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_share" "test" { + provider = snowflake.second_account + name = "test_share" + accounts = ["."] # TODO: Replace +} + +resource "snowflake_database" "test" { + provider = snowflake.second_account + name = "test_database" +} + +resource "snowflake_grant_privileges_to_share" "test" { + provider = snowflake.second_account + privileges = ["USAGE"] + on_database = snowflake_database.test.name + to_share = snowflake_share.test.name +} + +resource "snowflake_database" "from_share" { + depends_on = [ snowflake_grant_privileges_to_share.test ] + name = snowflake_database.test.name + from_share = { + provider = "" # TODO: Replace + share = snowflake_share.test.name + } +} diff --git a/pkg/resources/manual_tests/upgrade_shared_database/step_3.tf b/pkg/resources/manual_tests/upgrade_shared_database/step_3.tf new file mode 100644 index 0000000000..af4bee02e3 --- /dev/null +++ b/pkg/resources/manual_tests/upgrade_shared_database/step_3.tf @@ -0,0 +1,44 @@ +# Commands to run +# - terraform import snowflake_shared_database.from_share '"test_database"' (import shared database into state) +# - terraform plan (expect empty plan) + +terraform { + required_providers { + snowflake = { + source = "Snowflake-Labs/snowflake" + version = ">= 0.92.0" # latest + } + } +} + +provider "snowflake" {} + +provider "snowflake" { + profile = "secondary_test_account" + alias = second_account +} + +resource "snowflake_share" "test" { + provider = snowflake.second_account + name = "test_share" + accounts = ["."] # TODO: Replace +} + +resource "snowflake_database" "test" { + provider = snowflake.second_account + name = "test_database" +} + +resource "snowflake_grant_privileges_to_share" "test" { + provider = snowflake.second_account + privileges = ["USAGE"] + on_database = snowflake_database.test.name + to_share = snowflake_share.test.name +} + +# Changed old snowflake_database to the new snowflake_shared_database counter-part +resource "snowflake_shared_database" "from_share" { + depends_on = [ snowflake_grant_privileges_to_share.test ] + name = snowflake_database.test.name + from_share = "\"\".\"\".\"${snowflake_share.test.name}\"" # TODO: Replace +}