Skip to content

Commit

Permalink
feat: Use managed account from the SDK (#2420)
Browse files Browse the repository at this point in the history
Use managed account from the SDK
- acceptance test was run manually and was successful after the change
(it is still waiting for SNOW-1011985)
- initial read sleep was replaced with a retry (like in account
resource)
  • Loading branch information
sfc-gh-asawicki authored Jan 25, 2024
1 parent ebcd1bc commit 3aaa080
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 291 deletions.
115 changes: 68 additions & 47 deletions pkg/resources/managed_account.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package resources

import (
"context"
"database/sql"
"errors"
"fmt"
"log"
"time"

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

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)
Expand All @@ -17,13 +18,6 @@ const (
SnowflakeReaderAccountType = "READER"
)

var managedAccountProperties = []string{
"admin_name",
"admin_password",
"type",
"comment",
}

var managedAccountSchema = map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -102,82 +96,109 @@ func ManagedAccount() *schema.Resource {

// CreateManagedAccount implements schema.CreateFunc.
func CreateManagedAccount(d *schema.ResourceData, meta interface{}) error {
return CreateResource(
"this does not seem to be used",
managedAccountProperties,
managedAccountSchema,
snowflake.NewManagedAccountBuilder,
initialReadManagedAccount,
)(d, meta)
}
db := meta.(*sql.DB)
ctx := context.Background()
client := sdk.NewClientFromDB(db)

name := d.Get("name").(string)
id := sdk.NewAccountObjectIdentifier(name)

adminName := d.Get("admin_name").(string)
adminPassword := d.Get("admin_password").(string)
createParams := sdk.NewCreateManagedAccountParamsRequest(adminName, adminPassword)

if v, ok := d.GetOk("comment"); ok {
createParams.WithComment(sdk.String(v.(string)))
}

createRequest := sdk.NewCreateManagedAccountRequest(id, *createParams)

err := client.ManagedAccounts.Create(ctx, createRequest)
if err != nil {
return err
}

d.SetId(helpers.EncodeSnowflakeID(id))

// initialReadManagedAccount is used for the first read, since the locator takes
// some time to appear. This is currently implemented as a sleep. @TODO actually
// wait until the locator is generated.
func initialReadManagedAccount(d *schema.ResourceData, meta interface{}) error {
log.Println("[INFO] sleeping to give the locator a chance to be generated")
// lintignore:R018
time.Sleep(10 * time.Second)
return ReadManagedAccount(d, meta)
}

// ReadManagedAccount implements schema.ReadFunc.
func ReadManagedAccount(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
id := d.Id()

stmt := snowflake.NewManagedAccountBuilder(id).Show()
row := snowflake.QueryRow(db, stmt)
a, err := snowflake.ScanManagedAccount(row)

if errors.Is(err, sql.ErrNoRows) {
// If not found, remove resource from
log.Printf("[DEBUG] managed account (%s) not found", d.Id())
d.SetId("")
return nil
}
client := sdk.NewClientFromDB(db)

ctx := context.Background()
objectIdentifier := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)

// We have to wait during the first read, since the locator takes some time to appear.
// This approach has a downside of not handling correctly the situation where managed account was removed externally.
// TODO [SNOW-1003380]: discuss it as a provider-wide topic during resources redesign.
var managedAccount *sdk.ManagedAccount
var err error
err = helpers.Retry(5, 3*time.Second, func() (error, bool) {
managedAccount, err = client.ManagedAccounts.ShowByID(ctx, objectIdentifier)
if err != nil {
return nil, false
}
return nil, true
})
if err != nil {
return err
}

if err := d.Set("name", a.Name.String); err != nil {
if err := d.Set("name", managedAccount.Name); err != nil {
return err
}

if err := d.Set("cloud", a.Cloud.String); err != nil {
if err := d.Set("cloud", managedAccount.Cloud); err != nil {
return err
}

if err := d.Set("region", a.Region.String); err != nil {
if err := d.Set("region", managedAccount.Region); err != nil {
return err
}

if err := d.Set("locator", a.Locator.String); err != nil {
if err := d.Set("locator", managedAccount.Locator); err != nil {
return err
}

if err := d.Set("created_on", a.CreatedOn.String); err != nil {
if err := d.Set("created_on", managedAccount.CreatedOn); err != nil {
return err
}

if err := d.Set("url", a.URL.String); err != nil {
if err := d.Set("url", managedAccount.URL); err != nil {
return err
}

if a.IsReader {
if managedAccount.IsReader {
if err := d.Set("type", "READER"); err != nil {
return err
}
} else {
return fmt.Errorf("unable to determine the account type")
}

err = d.Set("comment", a.Comment.String)
if err := d.Set("comment", managedAccount.Comment); err != nil {
return err
}

return err
return nil
}

// DeleteManagedAccount implements schema.DeleteFunc.
func DeleteManagedAccount(d *schema.ResourceData, meta interface{}) error {
return DeleteResource("this does not seem to be used", snowflake.NewManagedAccountBuilder)(d, meta)
db := meta.(*sql.DB)
client := sdk.NewClientFromDB(db)
ctx := context.Background()

objectIdentifier := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)

err := client.ManagedAccounts.Drop(ctx, sdk.NewDropManagedAccountRequest(objectIdentifier))
if err != nil {
return err
}

d.SetId("")
return nil
}
64 changes: 0 additions & 64 deletions pkg/resources/managed_account_test.go

This file was deleted.

114 changes: 0 additions & 114 deletions pkg/resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,125 +3,11 @@ package resources
import (
"database/sql"
"fmt"
"log"

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

func CreateResource(
t string,
properties []string,
s map[string]*schema.Schema,
builder func(string) *snowflake.Builder,
read func(*schema.ResourceData, interface{}) error,
) func(*schema.ResourceData, interface{}) error {
return func(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
name := d.Get("name").(string)

qb := builder(name).Create()

for _, field := range properties {
val, ok := d.GetOk(field)
if ok {
switch s[field].Type {
case schema.TypeString:
valStr := val.(string)
qb.SetString(field, valStr)
case schema.TypeBool:
valBool := val.(bool)
qb.SetBool(field, valBool)
case schema.TypeInt:
valInt := val.(int)
qb.SetInt(field, valInt)
case schema.TypeSet:
valList := expandStringList(val.(*schema.Set).List())
qb.SetStringList(field, valList)
}
}
}
if v, ok := d.GetOk("tag"); ok {
tags := getTags(v)
qb.SetTags(tags.toSnowflakeTagValues())
}
if err := snowflake.Exec(db, qb.Statement()); err != nil {
return fmt.Errorf("error creating %s err = %w", t, err)
}

d.SetId(name)

return read(d, meta)
}
}

func UpdateResource(
t string,
properties []string,
s map[string]*schema.Schema,
builder func(string) *snowflake.Builder,
read func(*schema.ResourceData, interface{}) error,
) func(*schema.ResourceData, interface{}) error {
return func(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
if d.HasChange("name") {
// I wish this could be done on one line.
oldNameI, newNameI := d.GetChange("name")
oldName := oldNameI.(string)
newName := newNameI.(string)

stmt := builder(oldName).Rename(newName)

err := snowflake.Exec(db, stmt)
if err != nil {
return fmt.Errorf("error renaming %s %s to %s err = %w", t, oldName, newName, err)
}
d.SetId(newName)
}

changes := []string{}
for _, prop := range properties {
if d.HasChange(prop) {
changes = append(changes, prop)
}
}
if len(changes) > 0 {
name := d.Get("name").(string)
qb := builder(name).Alter()

for _, field := range changes {
val := d.Get(field)
switch s[field].Type {
case schema.TypeString:
valStr := val.(string)
qb.SetString(field, valStr)
case schema.TypeBool:
valBool := val.(bool)
qb.SetBool(field, valBool)
case schema.TypeInt:
valInt := val.(int)
qb.SetInt(field, valInt)
case schema.TypeSet:
valList := expandStringList(val.(*schema.Set).List())
qb.SetStringList(field, valList)
}
}
if d.HasChange("tag") {
log.Println("[DEBUG] updating tags")
v := d.Get("tag")
tags := getTags(v)
qb.SetTags(tags.toSnowflakeTagValues())
}

if err := snowflake.Exec(db, qb.Statement()); err != nil {
return fmt.Errorf("error altering %s err = %w", t, err)
}
}
log.Println("[DEBUG] performing read")
return read(d, meta)
}
}

func DeleteResource(t string, builder func(string) *snowflake.Builder) func(*schema.ResourceData, interface{}) error {
return func(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
Expand Down
Loading

0 comments on commit 3aaa080

Please sign in to comment.