From 167de4be15530d481b6e46953a7984d1b2777899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Budzy=C5=84ski?= Date: Fri, 8 Nov 2024 16:09:57 +0100 Subject: [PATCH] fix: Connection and secret-datasource tests (#3177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes * fix secrets datasource acceptance tests * added tests for `ParseCommaSeparatedAccountIdentifierArray` * added customDiff and tests for re-creating connection resources * adjusted migration-guide and documentation for connection resources * added 'TestAccPreCheck' to acceptance tests ## Test Plan * [x] unit tests * [x] integration tests * [x] acceptance tests * [x] acceptance tests for secrets-datasource ## References * https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/3162 --------- Co-authored-by: Jan Cieślak --- MIGRATION_GUIDE.md | 17 +++-- docs/resources/primary_connection.md | 14 ++++- docs/resources/secondary_connection.md | 15 +++-- .../objectassert/connection_snowflake_gen.go | 13 ---- pkg/datasources/secrets_acceptance_test.go | 49 ++++++++------- pkg/resources/custom_diffs.go | 16 ++--- pkg/resources/custom_diffs_test.go | 44 ++++++++++--- pkg/resources/primary_connection.go | 11 +++- .../primary_connection_acceptance_test.go | 6 ++ pkg/resources/secondary_connection.go | 8 +-- .../secondary_connection_acceptance_test.go | 3 + pkg/schemas/connection_gen.go | 1 - pkg/sdk/parsers_test.go | 62 +++++++++++++++++++ .../resources/primary_connection.md.tmpl | 9 ++- .../resources/secondary_connection.md.tmpl | 9 ++- 15 files changed, 205 insertions(+), 72 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 33f518b6ff..44079dfc95 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -19,17 +19,24 @@ Added a new datasource enabling querying and filtering connections. Notes: ### *(new feature)* connection resources -Added a new resources for managing connections. We decided to split connection into two separate resources based on whether the connection is primary or a replica (secondary). i.e.: +Added a new resources for managing connections. We decided to split connection into two separate resources based on whether the connection is a primary or replicated (secondary). i.e.: -- `snowflake_connection` is used as primary connection, with ability to enable failover to other accounts. -- `snowflake_secondary_connection` is used as replica (secondary) connection. +- `snowflake_primary_connection` is used to manage primary connection, with ability to enable failover to other accounts. +- `snowflake_secondary_connection` is used to manage replicated (secondary) connection. -In order to promote secondary_connection to primary, resources need to be migrated (check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md)) or re-created and imported using the following SQL statements on Snowflake Worksheet: +To promote `snowflake_secondary_connection` to `snowflake_primary_connection`, resources need to be removed from the state, altered manually using: +``` +ALTER CONNECTION PRIMARY; +``` +and then imported again, now as `snowflake_primary_connection`. +To demote `snowflake_primary_connection` back to `snowflake_secondary_connection`, resources need to be removed from the state, re-created manually using: ``` CREATE CONNECTION AS REPLICA OF ..; -ALTER CONNECTION PRIMARY; ``` +and then imported as `snowflake_secondary_connection`. + +For guidance on removing and importing resources into the state check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). See reference [docs](https://docs.snowflake.com/en/sql-reference/sql/create-connection). diff --git a/docs/resources/primary_connection.md b/docs/resources/primary_connection.md index cb7bca2221..c1efe4617b 100644 --- a/docs/resources/primary_connection.md +++ b/docs/resources/primary_connection.md @@ -2,14 +2,14 @@ page_title: "snowflake_primary_connection Resource - terraform-provider-snowflake" subcategory: "" description: |- - Resource used to manage primary (not replicated) connections. For more information, check connection documentation https://docs.snowflake.com/en/sql-reference/sql/create-connection.html. + Resource used to manage primary connections. For managing replicated connection check resource snowflakesecondaryconnection ./secondary_connection. For more information, check connection documentation https://docs.snowflake.com/en/sql-reference/sql/create-connection.html. --- !> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0970--v0980) to use it. # snowflake_primary_connection (Resource) -Resource used to manage primary (not replicated) connections. For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html). +Resource used to manage primary connections. For managing replicated connection check resource [snowflake_secondary_connection](./secondary_connection). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html). ## Example Usage @@ -28,7 +28,14 @@ resource "snowflake_primary_connection" "complete" { ] } ``` --> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](./docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** To demote [`snowflake_primary_connection`](./primary_connection) to [`snowflake_secondary_connection`](./secondary_connection), resources need to be migrated manually. For guidance on removing and importing resources into the state check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). Remove the resource from the state, then recreate it in manually using: + ``` + CREATE CONNECTION AS REPLICA OF ..; + ``` + and then import it as the `snowflake_secondary_connection`. @@ -47,6 +54,7 @@ resource "snowflake_primary_connection" "complete" { - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. +- `is_primary` (Boolean) Indicates if the connection is primary. When Terraform detects that the connection is not primary, the resource is recreated. - `show_output` (List of Object) Outputs the result of `SHOW CONNECTIONS` for the given connection. (see [below for nested schema](#nestedatt--show_output)) diff --git a/docs/resources/secondary_connection.md b/docs/resources/secondary_connection.md index e1a6a4d969..dcc5a76da0 100644 --- a/docs/resources/secondary_connection.md +++ b/docs/resources/secondary_connection.md @@ -2,14 +2,14 @@ page_title: "snowflake_secondary_connection Resource - terraform-provider-snowflake" subcategory: "" description: |- - Resource used to manage secondary connections. To promote secondary connection to primary check migraton guide https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#connection-resources. For more information, check connection documentation https://docs.snowflake.com/en/sql-reference/sql/create-connection.html. + Resource used to manage secondary (replicated) connections. To manage primary connection check resource snowflakeprimaryconnection ./primary_connection. For more information, check connection documentation https://docs.snowflake.com/en/sql-reference/sql/create-connection.html. --- !> **V1 release candidate** This resource is a release candidate for the V1. It is on the list of remaining GA objects for V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0970--v0980) to use it. # snowflake_secondary_connection (Resource) -Resource used to manage secondary connections. To promote secondary connection to primary check [migraton guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#connection-resources). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html). +Resource used to manage secondary (replicated) connections. To manage primary connection check resource [snowflake_primary_connection](./primary_connection). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html). ## Example Usage @@ -27,7 +27,14 @@ resource "snowflake_secondary_connection" "complete" { comment = "my complete secondary connection" } ``` --> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](../guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** To promote [`snowflake_secondary_connection`](./secondary_connection) to [`snowflake_primary_connection`](./primary_connection), resources need to be migrated manually. For guidance on removing and importing resources into the state check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). Remove the resource from the state, then promote it manually using: + ``` + ALTER CONNECTION PRIMARY; + ``` + and then import it as the `snowflake_primary_connection`. @@ -46,7 +53,7 @@ resource "snowflake_secondary_connection" "complete" { - `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution). - `id` (String) The ID of this resource. -- `is_primary` (Boolean) Indicates if the connection has been changed to primary. If change is detected, the secondary connection will be recreated. +- `is_primary` (Boolean) Indicates if the connection primary status has been changed. If change is detected, resource will be recreated. - `show_output` (List of Object) Outputs the result of `SHOW CONNECTIONS` for the given connection. (see [below for nested schema](#nestedatt--show_output)) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/connection_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/connection_snowflake_gen.go index c6e8e2361e..80b165243e 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/connection_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/connection_snowflake_gen.go @@ -125,19 +125,6 @@ func (c *ConnectionAssert) HasPrimary(expected sdk.ExternalObjectIdentifier) *Co return c } -/* -func (c *ConnectionAssert) HasFailoverAllowedToAccounts(expected []sdk.AccountIdentifier) *ConnectionAssert { - c.AddAssertion(func(t *testing.T, o *sdk.Connection) error { - t.Helper() - if o.FailoverAllowedToAccounts != expected { - return fmt.Errorf("expected failover allowed to accounts: %v; got: %v", expected, o.FailoverAllowedToAccounts) - } - return nil - }) - return c -} -*/ - func (c *ConnectionAssert) HasConnectionUrl(expected string) *ConnectionAssert { c.AddAssertion(func(t *testing.T, o *sdk.Connection) error { t.Helper() diff --git a/pkg/datasources/secrets_acceptance_test.go b/pkg/datasources/secrets_acceptance_test.go index 41d454a29a..f77b6e27ee 100644 --- a/pkg/datasources/secrets_acceptance_test.go +++ b/pkg/datasources/secrets_acceptance_test.go @@ -42,7 +42,7 @@ func TestAcc_Secrets_WithClientCredentials(t *testing.T) { secretModel := model.SecretWithClientCredentials("test", integrationId.Name(), id.DatabaseName(), id.SchemaName(), id.Name(), []string{"username", "test_scope"}) - dataSecretsClientCredentials := accConfig.FromModel(t, secretModel) + secretsData(secretWithClientCredentials) + dataSecretsClientCredentials := accConfig.FromModel(t, secretModel) + secretsData(secretWithClientCredentials, id.DatabaseId().FullyQualifiedName()) dsName := "data.snowflake_secrets.test" resource.Test(t, resource.TestCase{ @@ -97,7 +97,7 @@ func TestAcc_Secrets_WithAuthorizationCodeGrant(t *testing.T) { secretModel := model.SecretWithAuthorizationCodeGrant("test", integrationId.Name(), id.DatabaseName(), id.SchemaName(), id.Name(), "test_token", time.Now().Add(24*time.Hour).Format(time.DateTime)).WithComment("test_comment") - dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithAuthorizationCodeGrant) + dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithAuthorizationCodeGrant, id.DatabaseId().FullyQualifiedName()) dsName := "data.snowflake_secrets.test" resource.Test(t, resource.TestCase{ @@ -138,7 +138,7 @@ func TestAcc_Secrets_WithBasicAuthentication(t *testing.T) { id := acc.TestClient().Ids.RandomSchemaObjectIdentifier() secretModel := model.SecretWithBasicAuthentication("test", id.DatabaseName(), id.Name(), "test_passwd", id.SchemaName(), "test_username") - dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithBasicAuthentication) + dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithBasicAuthentication, id.DatabaseId().FullyQualifiedName()) dsName := "data.snowflake_secrets.test" resource.Test(t, resource.TestCase{ @@ -179,7 +179,7 @@ func TestAcc_Secrets_WithGenericString(t *testing.T) { secretModel := model.SecretWithGenericString("test", id.DatabaseName(), id.Name(), id.SchemaName(), "test_secret_string") - dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithGenericString) + dataSecretsAuthorizationCode := accConfig.FromModel(t, secretModel) + secretsData(secretWithGenericString, id.DatabaseId().FullyQualifiedName()) dsName := "data.snowflake_secrets.test" resource.Test(t, resource.TestCase{ @@ -216,11 +216,14 @@ func TestAcc_Secrets_WithGenericString(t *testing.T) { }) } -func secretsData(secretResourceName string) string { +func secretsData(secretResourceName string, inDatabaseName string) string { return fmt.Sprintf(` data "snowflake_secrets" "test" { depends_on = [%s.test] - }`, secretResourceName) + in { + database = %s + } + }`, secretResourceName, inDatabaseName) } func TestAcc_Secrets_Filtering(t *testing.T) { @@ -309,6 +312,23 @@ func TestAcc_Secrets_Filtering(t *testing.T) { }) } +func TestAcc_Secrets_EmptyIn(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: secretDatasourceEmptyIn(), + ExpectError: regexp.MustCompile("Invalid combination of arguments"), + }, + }, + }) +} + func datasourceWithLikeMultipleSecretTypes(like string) string { return fmt.Sprintf(` data "snowflake_secrets" "test" { @@ -340,23 +360,6 @@ func secretDatasourceInAccountWithLike(prefix string) string { `, prefix) } -func TestAcc_Secrets_EmptyIn(t *testing.T) { - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - CheckDestroy: nil, - Steps: []resource.TestStep{ - { - Config: secretDatasourceEmptyIn(), - ExpectError: regexp.MustCompile("Invalid combination of arguments"), - }, - }, - }) -} - func secretDatasourceEmptyIn() string { return ` data "snowflake_secrets" "test" { diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go index e4cfe35eae..872c7ed24f 100644 --- a/pkg/resources/custom_diffs.go +++ b/pkg/resources/custom_diffs.go @@ -271,13 +271,15 @@ func RecreateWhenStreamIsStale() schema.CustomizeDiffFunc { } } -// TODO: [SNOW-1763442] unable to test now, as there is no test accounts with different regions -// RecreateWhenSecondaryConnectionChangedExternally detects if the secondary connection was promoted externally to serve as primary. -// If so, it sets the `is_primary` field to `false` which is our desired value for secondary_connection -func RecreateWhenSecondaryConnectionPromotedExternally() schema.CustomizeDiffFunc { - return func(_ context.Context, diff *schema.ResourceDiff, _ any) error { - if _, newValue := diff.GetChange("is_primary"); newValue.(bool) { - return diff.SetNew("is_primary", false) +// RecreateWhenResourceBoolFieldChangedExternally recreates a resource when wantValue is different than value in boolField. +func RecreateWhenResourceBoolFieldChangedExternally(boolField string, wantValue bool) schema.CustomizeDiffFunc { + return func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { + if n := diff.Get(boolField); n != nil { + logging.DebugLogger.Printf("[DEBUG] new external value for %v: %v\n", boolField, n.(bool)) + + if n.(bool) != wantValue { + return errors.Join(diff.SetNew(boolField, wantValue), diff.ForceNew(boolField)) + } } return nil } diff --git a/pkg/resources/custom_diffs_test.go b/pkg/resources/custom_diffs_test.go index eeab92071c..9fcdd32071 100644 --- a/pkg/resources/custom_diffs_test.go +++ b/pkg/resources/custom_diffs_test.go @@ -5,6 +5,8 @@ import ( "strings" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -1082,24 +1084,50 @@ func Test_RecreateWhenSecretTypeChangedExternallyForOAuth2(t *testing.T) { } } -func Test_RecreateWhenSecondaryConnectionChangedExternally(t *testing.T) { +func Test_RecreateWhenResourceBoolFieldChangedExternally(t *testing.T) { tests := []struct { - name string - expectedIsPrimary string - stateValue map[string]string + name string + isPrimary bool + stateValue map[string]string + wantForceNew bool }{ { - name: "changed from is_primary from false to true", - expectedIsPrimary: "false", + name: "changed is_primary from false to true", + isPrimary: false, stateValue: map[string]string{ "is_primary": "true", }, + wantForceNew: true, + }, + { + name: "changed is_primary from true to false", + isPrimary: true, + stateValue: map[string]string{ + "is_primary": "false", + }, + wantForceNew: true, + }, + { + name: "no change in is_primary - true to true", + isPrimary: true, + stateValue: map[string]string{ + "is_primary": "true", + }, + wantForceNew: false, + }, + { + name: "no change in is_primary - false to false", + isPrimary: false, + stateValue: map[string]string{ + "is_primary": "false", + }, + wantForceNew: false, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - customDiff := resources.RecreateWhenSecondaryConnectionPromotedExternally() + customDiff := resources.RecreateWhenResourceBoolFieldChangedExternally("is_primary", tt.isPrimary) testProvider := createProviderWithCustomSchemaAndCustomDiff(t, map[string]*schema.Schema{ "is_primary": { @@ -1114,7 +1142,7 @@ func Test_RecreateWhenSecondaryConnectionChangedExternally(t *testing.T) { tt.stateValue, map[string]any{}, ) - assert.Equal(t, tt.expectedIsPrimary, diff.Attributes["is_primary"].New) + assert.Equal(t, tt.wantForceNew, diff.RequiresNew()) }) } } diff --git a/pkg/resources/primary_connection.go b/pkg/resources/primary_connection.go index ca55ce1af0..0966e6dc5a 100644 --- a/pkg/resources/primary_connection.go +++ b/pkg/resources/primary_connection.go @@ -22,6 +22,11 @@ var primaryConnectionSchema = map[string]*schema.Schema{ Description: blocklistedCharactersFieldDescription("String that specifies the identifier (i.e. name) for the connection. Must start with an alphabetic character and may only contain letters, decimal digits (0-9), and underscores (_). For a primary connection, the name must be unique across connection names and account names in the organization. "), DiffSuppressFunc: suppressIdentifierQuoting, }, + "is_primary": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates if the connection is primary. When Terraform detects that the connection is not primary, the resource is recreated.", + }, "enable_failover_to_accounts": { Type: schema.TypeList, Optional: true, @@ -55,10 +60,11 @@ func PrimaryConnection() *schema.Resource { DeleteContext: DeleteContextPrimaryConnection, CustomizeDiff: customdiff.All( - ComputedIfAnyAttributeChanged(primaryConnectionSchema, ShowOutputAttributeName, "comment", "failover_allowed_to_accounts"), + ComputedIfAnyAttributeChanged(primaryConnectionSchema, ShowOutputAttributeName, "comment", "is_primary", "enable_failover_to_accounts"), + RecreateWhenResourceBoolFieldChangedExternally("is_primary", true), ), - Description: "Resource used to manage primary (not replicated) connections. For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html).", + Description: "Resource used to manage primary connections. For managing replicated connection check resource [snowflake_secondary_connection](./secondary_connection). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html).", Schema: primaryConnectionSchema, Importer: &schema.ResourceImporter{ StateContext: ImportName[sdk.AccountObjectIdentifier], @@ -137,6 +143,7 @@ func ReadContextPrimaryConnection(ctx context.Context, d *schema.ResourceData, m } errs := errors.Join( + d.Set("is_primary", connection.IsPrimary), d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()), d.Set(ShowOutputAttributeName, []map[string]any{schemas.ConnectionToSchema(connection)}), d.Set("comment", connection.Comment), diff --git a/pkg/resources/primary_connection_acceptance_test.go b/pkg/resources/primary_connection_acceptance_test.go index 6273621c9b..8c2241a328 100644 --- a/pkg/resources/primary_connection_acceptance_test.go +++ b/pkg/resources/primary_connection_acceptance_test.go @@ -24,6 +24,9 @@ import ( ) func TestAcc_PrimaryConnection_Basic(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups) @@ -149,6 +152,9 @@ func TestAcc_PrimaryConnection_Basic(t *testing.T) { } func TestAcc_PrimaryConnection_ExternalChanges(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups) diff --git a/pkg/resources/secondary_connection.go b/pkg/resources/secondary_connection.go index 711a6074ec..c58b7a8ed3 100644 --- a/pkg/resources/secondary_connection.go +++ b/pkg/resources/secondary_connection.go @@ -25,7 +25,7 @@ var secondaryConnectionSchema = map[string]*schema.Schema{ "is_primary": { Type: schema.TypeBool, Computed: true, - Description: "Indicates if the connection has been changed to primary. If change is detected, the secondary connection will be recreated.", + Description: "Indicates if the connection primary status has been changed. If change is detected, resource will be recreated.", }, "as_replica_of": { Type: schema.TypeString, @@ -56,11 +56,11 @@ func SecondaryConnection() *schema.Resource { ReadContext: ReadContextSecondaryConnection, UpdateContext: UpdateContextSecondaryConnection, DeleteContext: DeleteContextSecondaryConnection, - Description: "Resource used to manage secondary connections. To promote secondary connection to primary check [migraton guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#connection-resources). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html).", + Description: "Resource used to manage secondary (replicated) connections. To manage primary connection check resource [snowflake_primary_connection](./primary_connection). For more information, check [connection documentation](https://docs.snowflake.com/en/sql-reference/sql/create-connection.html).", CustomizeDiff: customdiff.All( - ComputedIfAnyAttributeChanged(secondaryConnectionSchema, ShowOutputAttributeName, "comment", "is_primary", "failover_allowed_to_accounts"), - RecreateWhenSecondaryConnectionPromotedExternally(), + ComputedIfAnyAttributeChanged(secondaryConnectionSchema, ShowOutputAttributeName, "comment", "is_primary"), + RecreateWhenResourceBoolFieldChangedExternally("is_primary", false), ), Schema: secondaryConnectionSchema, diff --git a/pkg/resources/secondary_connection_acceptance_test.go b/pkg/resources/secondary_connection_acceptance_test.go index c0d8b47dbb..380f3b49b5 100644 --- a/pkg/resources/secondary_connection_acceptance_test.go +++ b/pkg/resources/secondary_connection_acceptance_test.go @@ -20,6 +20,9 @@ import ( ) func TestAcc_SecondaryConnection_Basic(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups) diff --git a/pkg/schemas/connection_gen.go b/pkg/schemas/connection_gen.go index 530dea13a2..0977ae88f5 100644 --- a/pkg/schemas/connection_gen.go +++ b/pkg/schemas/connection_gen.go @@ -36,7 +36,6 @@ var ShowConnectionSchema = map[string]*schema.Schema{ "is_primary": { Type: schema.TypeBool, Computed: true, - ForceNew: true, }, "primary": { Type: schema.TypeString, diff --git a/pkg/sdk/parsers_test.go b/pkg/sdk/parsers_test.go index 314ef9892b..71faf01c23 100644 --- a/pkg/sdk/parsers_test.go +++ b/pkg/sdk/parsers_test.go @@ -168,6 +168,68 @@ func TestParseCommaSeparatedSchemaObjectIdentifierArray(t *testing.T) { } } +func TestParseCommaSeparatedAccountIdentifierArray(t *testing.T) { + testCases := []struct { + Name string + Value string + Result []AccountIdentifier + }{ + { + Name: "empty list", + Value: "[]", + Result: []AccountIdentifier{}, + }, + { + Name: "empty string", + Value: "", + Result: []AccountIdentifier{}, + }, + { + Name: "one element in list", + Value: "[A.B]", + Result: []AccountIdentifier{NewAccountIdentifier("A", "B")}, + }, + { + Name: "one element in list - with mixed cases", + Value: `[A."b"]`, + Result: []AccountIdentifier{NewAccountIdentifier("A", "b")}, + }, + { + Name: "multiple elements in list", + Value: "[A.B, C.D]", + Result: []AccountIdentifier{NewAccountIdentifier("A", "B"), NewAccountIdentifier("C", "D")}, + }, + { + Name: "multiple elements in list - with mixed cases", + Value: `[A."b", "c"."d"]`, + Result: []AccountIdentifier{NewAccountIdentifier("A", "b"), NewAccountIdentifier("c", "d")}, + }, + { + Name: "multiple elements in list - packed", + Value: "[A.B,C.D]", + Result: []AccountIdentifier{NewAccountIdentifier("A", "B"), NewAccountIdentifier("C", "D")}, + }, + { + Name: "multiple elements in list - additional spaces", + Value: "[A.B, C.D]", + Result: []AccountIdentifier{NewAccountIdentifier("A", "B"), NewAccountIdentifier("C", "D")}, + }, + { + Name: "list without brackets", + Value: "A.B, C.D", + Result: []AccountIdentifier{NewAccountIdentifier("A", "B"), NewAccountIdentifier("C", "D")}, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + ids, err := ParseCommaSeparatedAccountIdentifierArray(tc.Value) + require.NoError(t, err) + require.Equal(t, tc.Result, ids) + }) + } +} + func TestParseCommaSeparatedSchemaObjectIdentifierArray_Invalid(t *testing.T) { testCases := []struct { Name string diff --git a/templates/resources/primary_connection.md.tmpl b/templates/resources/primary_connection.md.tmpl index bbc5e20afb..e4841ffbd6 100644 --- a/templates/resources/primary_connection.md.tmpl +++ b/templates/resources/primary_connection.md.tmpl @@ -19,7 +19,14 @@ description: |- ## Example Usage {{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} --> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](./docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** To demote [`snowflake_primary_connection`](./primary_connection) to [`snowflake_secondary_connection`](./secondary_connection), resources need to be migrated manually. For guidance on removing and importing resources into the state check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). Remove the resource from the state, then recreate it in manually using: + ``` + CREATE CONNECTION AS REPLICA OF ..; + ``` + and then import it as the `snowflake_secondary_connection`. {{- end }} diff --git a/templates/resources/secondary_connection.md.tmpl b/templates/resources/secondary_connection.md.tmpl index bbc5e20afb..4efb28c63b 100644 --- a/templates/resources/secondary_connection.md.tmpl +++ b/templates/resources/secondary_connection.md.tmpl @@ -19,7 +19,14 @@ description: |- ## Example Usage {{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} --> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](../guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + +-> **Note** To promote [`snowflake_secondary_connection`](./secondary_connection) to [`snowflake_primary_connection`](./primary_connection), resources need to be migrated manually. For guidance on removing and importing resources into the state check [resource migration](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md). Remove the resource from the state, then promote it manually using: + ``` + ALTER CONNECTION PRIMARY; + ``` + and then import it as the `snowflake_primary_connection`. {{- end }}