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: Removing force new and adding update for data base replication config #1105

Merged
merged 4 commits into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
47 changes: 45 additions & 2 deletions pkg/resources/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"strconv"
"strings"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"

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

var databaseSchema = map[string]*schema.Schema{
Expand Down Expand Up @@ -54,13 +55,13 @@ var databaseSchema = map[string]*schema.Schema{
Type: schema.TypeList,
Description: "When set, specifies the configurations for database replication.",
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"accounts": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
Elem: &schema.Schema{Type: schema.TypeString},
},
"ignore_edition_check": {
Expand Down Expand Up @@ -226,9 +227,51 @@ func ReadDatabase(d *schema.ResourceData, meta interface{}) error {
}

func UpdateDatabase(d *schema.ResourceData, meta interface{}) error {
dbName := d.Get("name").(string)
builder := snowflake.Database(dbName)
db := meta.(*sql.DB)

// If replication configuration changes, need to update accounts that have permission to replicate database
if d.HasChange("replication_configuration") {
oldConfig, newConfig := d.GetChange("replication_configuration")
newConfigLength := len(newConfig.([]interface{}))
oldConfigLength := len(oldConfig.([]interface{}))
// Enable replication for any new accounts and disable replication for removed accounts
if newConfigLength > 0 {
newAccounts := extractInterfaceFromAttribute(newConfig, "accounts")
enableQuery := builder.EnableReplicationAccounts(dbName, strings.Join(expandStringList(newAccounts), ", "))
err := snowflake.Exec(db, enableQuery)
if err != nil {
return errors.Wrapf(err, "error enabling replication configuration with statement %v", enableQuery)
}
}

if oldConfigLength > 0 {
oldAccounts := extractInterfaceFromAttribute(oldConfig, "accounts")
var accountsToDisableReplication []interface{}
if newConfigLength > 0 {
newAccounts := extractInterfaceFromAttribute(newConfig, "accounts")
accountsToDisableReplication = builder.GetRemovedAccountsFromReplicationConfiguration(oldAccounts, newAccounts)
} else {
accountsToDisableReplication = builder.GetRemovedAccountsFromReplicationConfiguration(oldAccounts, nil)
}
// If accounts were found to be removed, disable replication
if len(accountsToDisableReplication) > 0 {
disableQuery := builder.DisableReplicationAccounts(dbName, strings.Join(expandStringList(accountsToDisableReplication), ", "))
err := snowflake.Exec(db, disableQuery)
if err != nil {
return errors.Wrapf(err, "error disabling replication configuration with statement %v", disableQuery)
}
}
}
}
return UpdateResource("database", databaseProperties, databaseSchema, snowflake.Database, ReadDatabase)(d, meta)
}

func DeleteDatabase(d *schema.ResourceData, meta interface{}) error {
return DeleteResource("database", snowflake.Database)(d, meta)
}

func extractInterfaceFromAttribute(config interface{}, attribute string) []interface{} {
return config.([]interface{})[0].(map[string]interface{})[attribute].([]interface{})
}
33 changes: 30 additions & 3 deletions pkg/snowflake/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,21 @@ func (dsb *DatabaseCloneBuilder) Create() string {
return fmt.Sprintf(`CREATE DATABASE "%v" CLONE "%v"`, dsb.name, dsb.database)
}

// DatabaseReplicaBuilder is a basic builder that just creates databases from an avilable replication source
// DatabaseReplicaBuilder is a basic builder that just creates databases from an available replication source
type DatabaseReplicaBuilder struct {
name string
replica string
}

// DatabaseFromReplica returns a pointer to a builder that can create a database from an avilable replication source
// DatabaseFromReplica returns a pointer to a builder that can create a database from an available replication source
func DatabaseFromReplica(name, replica string) *DatabaseReplicaBuilder {
return &DatabaseReplicaBuilder{
name: name,
replica: replica,
}
}

// Create returns the SQL statement required to create a database from an avilable replication source
// Create returns the SQL statement required to create a database from an available replication source
func (dsb *DatabaseReplicaBuilder) Create() string {
return fmt.Sprintf(`CREATE DATABASE "%v" AS REPLICA OF "%v"`, dsb.name, dsb.replica)
}
Expand Down Expand Up @@ -134,3 +134,30 @@ func ListDatabase(sdb *sqlx.DB, databaseName string) (*database, error) {
}
return db, errors.Wrapf(err, "unable to scan row for %s", stmt)
}

// EnableReplicationAccounts returns the SQL query that will enable replication to provided accounts
func (db *Builder) EnableReplicationAccounts(dbName string, accounts string) string {
return fmt.Sprintf(`ALTER DATABASE "%v" ENABLE REPLICATION TO ACCOUNTS %v`, dbName, accounts)
}

// DisableReplicationAccounts returns the SQL query that will disable replication to provided accounts
func (db *Builder) DisableReplicationAccounts(dbName string, accounts string) string {
return fmt.Sprintf(`ALTER DATABASE "%v" DISABLE REPLICATION TO ACCOUNTS %v`, dbName, accounts)
}

// GetRemovedAccountsFromReplicationConfiguration compares two old and new configurations and returns any values that
// were deleted from the old configuration.
func (db *Builder) GetRemovedAccountsFromReplicationConfiguration(oldAcc []interface{}, newAcc []interface{}) []interface{} {
accountMap := make(map[string]bool)
var removedAccounts []interface{}
// insert all values from new configuration into mapping
for _, v := range newAcc {
accountMap[v.(string)] = true
}
for _, v := range oldAcc {
if accountMap[v.(string)] == false {
sfc-gh-jalin marked this conversation as resolved.
Show resolved Hide resolved
removedAccounts = append(removedAccounts, v.(string))
}
}
return removedAccounts
}