Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix failover group alter syntax and suppression for pipe statement #2562

Merged
merged 6 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/acceptance/testenvs/testing_environment_variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const (
Account env = "TEST_SF_TF_ACCOUNT"
Role env = "TEST_SF_TF_ROLE"
Host env = "TEST_SF_TF_HOST"

BusinessCriticalAccount env = "SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT"
)

func GetOrSkipTest(t *testing.T, envName Env) string {
Expand Down
53 changes: 30 additions & 23 deletions pkg/resources/failover_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,29 +423,6 @@ func UpdateFailoverGroup(d *schema.ResourceData, meta interface{}) error {
runSet = true
}

if d.HasChange("replication_schedule") {
_, n := d.GetChange("replication_schedule")
replicationSchedule := n.([]interface{})[0].(map[string]interface{})
c := replicationSchedule["cron"].([]interface{})
if len(c) > 0 {
if len(c) > 0 {
cron := c[0].(map[string]interface{})
cronExpression := cron["expression"].(string)
cronExpression = "USING CRON " + cronExpression
if v, ok := cron["time_zone"]; ok {
timeZone := v.(string)
if timeZone != "" {
cronExpression = cronExpression + " " + timeZone
}
}
opts.Set.ReplicationSchedule = &cronExpression
}
} else {
opts.Set.ReplicationSchedule = sdk.String(fmt.Sprintf("%d MINUTE", replicationSchedule["interval"].(int)))
}
runSet = true
}

if d.HasChange("allowed_integration_types") {
_, n := d.GetChange("allowed_integration_types")
newAllowedIntegrationTypes := expandStringList(n.(*schema.Set).List())
Expand All @@ -462,6 +439,36 @@ func UpdateFailoverGroup(d *schema.ResourceData, meta interface{}) error {
}
}

if d.HasChange("replication_schedule") {
_, n := d.GetChange("replication_schedule")
replicationSchedule := n.([]interface{})[0].(map[string]interface{})
c := replicationSchedule["cron"].([]interface{})
var updatedReplicationSchedule string
if len(c) > 0 {
cron := c[0].(map[string]interface{})
cronExpression := cron["expression"].(string)
cronExpression = "USING CRON " + cronExpression
if v, ok := cron["time_zone"]; ok {
timeZone := v.(string)
if timeZone != "" {
cronExpression = cronExpression + " " + timeZone
}
}
updatedReplicationSchedule = cronExpression
} else {
updatedReplicationSchedule = fmt.Sprintf("%d MINUTE", replicationSchedule["interval"].(int))
}

err := client.FailoverGroups.AlterSource(ctx, id, &sdk.AlterSourceFailoverGroupOptions{
Set: &sdk.FailoverGroupSet{
ReplicationSchedule: sdk.String(updatedReplicationSchedule),
},
})
if err != nil {
return err
}
}

if d.HasChange("allowed_databases") {
o, n := d.GetChange("allowed_databases")
oad := expandStringList(o.(*schema.Set).List())
Expand Down
64 changes: 47 additions & 17 deletions pkg/resources/failover_group_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package resources_test

import (
"fmt"
"os"
"strings"
"testing"

acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
Expand All @@ -16,10 +16,7 @@ import (
func TestAcc_FailoverGroupBasic(t *testing.T) {
randomCharacters := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

if _, ok := os.LookupEnv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT"); !ok {
t.Skip("Skipping TestAcc_FailoverGroup since not a business critical account")
}
accountName := os.Getenv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT")
accountName := testenvs.GetOrSkipTest(t, testenvs.BusinessCriticalAccount)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
Expand Down Expand Up @@ -54,10 +51,7 @@ func TestAcc_FailoverGroupBasic(t *testing.T) {
func TestAcc_FailoverGroupRemoveObjectTypes(t *testing.T) {
randomCharacters := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

if _, ok := os.LookupEnv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT"); !ok {
t.Skip("Skipping TestAcc_FailoverGroup since not a business critical account")
}
accountName := os.Getenv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT")
accountName := testenvs.GetOrSkipTest(t, testenvs.BusinessCriticalAccount)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
Expand Down Expand Up @@ -95,10 +89,7 @@ func TestAcc_FailoverGroupRemoveObjectTypes(t *testing.T) {
func TestAcc_FailoverGroupInterval(t *testing.T) {
randomCharacters := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

if _, ok := os.LookupEnv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT"); !ok {
t.Skip("Skipping TestAcc_FailoverGroup since not a business critical account")
}
accountName := os.Getenv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT")
accountName := testenvs.GetOrSkipTest(t, testenvs.BusinessCriticalAccount)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
Expand Down Expand Up @@ -194,10 +185,7 @@ func TestAcc_FailoverGroupInterval(t *testing.T) {
func TestAcc_FailoverGroup_issue2517(t *testing.T) {
randomCharacters := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

if _, ok := os.LookupEnv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT"); !ok {
t.Skip("Skipping TestAcc_FailoverGroup since not a business critical account")
}
accountName := os.Getenv("SNOWFLAKE_BUSINESS_CRITICAL_ACCOUNT")
accountName := testenvs.GetOrSkipTest(t, testenvs.BusinessCriticalAccount)
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
Expand All @@ -222,6 +210,34 @@ func TestAcc_FailoverGroup_issue2517(t *testing.T) {
})
}

func TestAcc_FailoverGroup_issue2544(t *testing.T) {
randomCharacters := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

accountName := testenvs.GetOrSkipTest(t, testenvs.BusinessCriticalAccount)
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: failoverGroupBasic(randomCharacters, accountName, acc.TestDatabaseName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_failover_group.fg", "name", randomCharacters),
),
},
{
Config: failoverGroupWithChanges(randomCharacters, accountName, 20),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_failover_group.fg", "name", randomCharacters),
),
},
},
})
}

func failoverGroupBasic(randomCharacters, accountName, databaseName string) string {
return fmt.Sprintf(`
resource "snowflake_failover_group" "fg" {
Expand Down Expand Up @@ -304,3 +320,17 @@ resource "snowflake_failover_group" "fg" {
}
`, randomCharacters, accountName, databaseName)
}

func failoverGroupWithChanges(randomCharacters string, accountName string, interval int) string {
return fmt.Sprintf(`
resource "snowflake_failover_group" "fg" {
name = "%[1]s"
object_types = ["DATABASES", "INTEGRATIONS"]
allowed_accounts= ["%[2]s"]
allowed_integration_types = ["NOTIFICATION INTEGRATIONS"]
replication_schedule {
interval = %d
}
}
`, randomCharacters, accountName, interval)
}
4 changes: 2 additions & 2 deletions pkg/resources/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ func pipeCopyStatementDiffSuppress(_, o, n string, _ *schema.ResourceData) bool
o = strings.ReplaceAll(o, "\r\n", "\n")
n = strings.ReplaceAll(n, "\r\n", "\n")

// trim off any trailing line endings
return strings.TrimRight(o, ";\r\n") == strings.TrimRight(n, ";\r\n")
// trim off any trailing line endings and leading/trailing whitespace
return strings.TrimSpace(strings.TrimRight(o, ";\r\n")) == strings.TrimSpace(strings.TrimRight(n, ";\r\n"))
}

// CreatePipe implements schema.CreateFunc.
Expand Down
16 changes: 9 additions & 7 deletions pkg/resources/pipe_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ package resources_test

import (
"fmt"
"os"
"strings"
"testing"

acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"

"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)

func TestAcc_Pipe(t *testing.T) {
if _, ok := os.LookupEnv("SKIP_PIPE_TESTS"); ok {
t.Skip("Skipping TestAcc_Pipe")
}
accName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

resource.Test(t, resource.TestCase{
Providers: acc.TestAccProviders(),
PreCheck: func() { acc.TestAccPreCheck(t) },
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_5_0),
},
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Expand All @@ -37,6 +38,7 @@ func TestAcc_Pipe(t *testing.T) {
})
}

// whitespace in copy_statement matters for the tests, change with caution!
func pipeConfig(name string, databaseName string, schemaName string) string {
s := `
resource "snowflake_table" "test" {
Expand Down Expand Up @@ -69,7 +71,7 @@ resource "snowflake_pipe" "test" {
name = "%s"
comment = "Terraform acceptance test"
copy_statement = <<CMD
COPY INTO "${snowflake_table.test.database}"."${snowflake_table.test.schema}"."${snowflake_table.test.name}"
COPY INTO "${snowflake_table.test.database}"."${snowflake_table.test.schema}"."${snowflake_table.test.name}"
FROM @"${snowflake_stage.test.database}"."${snowflake_stage.test.schema}"."${snowflake_stage.test.name}"
FILE_FORMAT = (TYPE = CSV)
CMD
Expand Down
10 changes: 6 additions & 4 deletions pkg/sdk/failover_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ func (opts *AlterSourceFailoverGroupOptions) validate() error {

type FailoverGroupSet struct {
ObjectTypes []PluralObjectType `ddl:"parameter" sql:"OBJECT_TYPES"`
ReplicationSchedule *string `ddl:"parameter,single_quotes" sql:"REPLICATION_SCHEDULE"`
AllowedIntegrationTypes []IntegrationType `ddl:"parameter" sql:"ALLOWED_INTEGRATION_TYPES"`
ReplicationSchedule *string `ddl:"parameter,single_quotes" sql:"REPLICATION_SCHEDULE"`
}

func (v *FailoverGroupSet) validate() error {
Expand Down Expand Up @@ -574,15 +574,17 @@ func (v *failoverGroups) ShowShares(ctx context.Context, id AccountObjectIdentif
return nil, err
}
dest := []struct {
Name string `db:"name"`
Name string `db:"name"`
OwnerAccount string `db:"owner_account"`
}{}
err = v.client.query(ctx, &dest, sql)
if err != nil {
return nil, err
}
resultList := make([]AccountObjectIdentifier, len(dest))
for i, row := range dest {
resultList[i] = NewExternalObjectIdentifierFromFullyQualifiedName(row.Name).objectIdentifier.(AccountObjectIdentifier)
for i, r := range dest {
// TODO [SNOW-999049]: this was not working correctly with identifiers containing `.` character
resultList[i] = NewExternalObjectIdentifier(NewAccountIdentifierFromFullyQualifiedName(r.OwnerAccount), NewAccountObjectIdentifier(r.Name)).objectIdentifier.(AccountObjectIdentifier)
}
return resultList, nil
}
Loading
Loading