Skip to content

Commit

Permalink
feat: Use database role from SDK (#2012)
Browse files Browse the repository at this point in the history
* Use SDK for database role create

* Use SDK for database role read

* Use SDK for database role update

* Use SDK for database role delete

* Remove unused methods from database role resource

* Use SDK database role for datasource

* Remove old database role builder

* Simplify database role builder

* Use DatabaseObjectIdentifier

* Make database required in database role datasource

* Fix docs
  • Loading branch information
sfc-gh-asawicki committed Aug 21, 2023
1 parent 4b42848 commit 294075a
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 261 deletions.
2 changes: 1 addition & 1 deletion docs/data-sources/database_roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description: |-
<!-- schema generated by tfplugindocs -->
## Schema

### Optional
### Required

- `database` (String) The database from which to return the database roles from.

Expand Down
42 changes: 19 additions & 23 deletions pkg/datasources/database_roles.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package datasources

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

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

var databaseRolesSchema = map[string]*schema.Schema{
"database": {
Type: schema.TypeString,
Optional: true,
Required: true,
Description: "The database from which to return the database roles from.",
},
"database_roles": {
Expand Down Expand Up @@ -52,34 +52,30 @@ func DatabaseRoles() *schema.Resource {
// ReadDatabaseRoles Reads the database metadata information.
func ReadDatabaseRoles(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
client := sdk.NewClientFromDB(db)
d.SetId("database_roles_read")

databaseName := d.Get("database").(string)

listRoles, err := snowflake.ListDatabaseRoles(databaseName, db)
if errors.Is(err, sql.ErrNoRows) {
log.Printf("[DEBUG] no roles found in database (%s)", databaseName)
d.SetId("")
return nil
} else if err != nil {
log.Println("[DEBUG] failed to list roles")
ctx := context.Background()
showRequest := sdk.NewShowDatabaseRoleRequest(sdk.NewAccountObjectIdentifier(databaseName))
extractedDatabaseRoles, err := client.DatabaseRoles.Show(ctx, showRequest)
if err != nil {
log.Printf("[DEBUG] unable to show database roles in db (%s)", databaseName)
d.SetId("")
return nil
return err
}

log.Printf("[DEBUG] list roles: %v", listRoles)
databaseRoles := make([]map[string]any, 0, len(extractedDatabaseRoles))
for _, databaseRole := range extractedDatabaseRoles {
databaseRoleMap := map[string]any{}

roles := []map[string]interface{}{}
for _, role := range listRoles {
roleMap := map[string]interface{}{}
databaseRoleMap["name"] = databaseRole.Name
databaseRoleMap["comment"] = databaseRole.Comment
databaseRoleMap["owner"] = databaseRole.Owner

roleMap["name"] = role.Name
roleMap["comment"] = role.Comment
roleMap["owner"] = role.Owner
roles = append(roles, roleMap)
databaseRoles = append(databaseRoles, databaseRoleMap)
}

if err := d.Set("database_roles", roles); err != nil {
return err
}
return nil
return d.Set("database_roles", databaseRoles)
}
144 changes: 36 additions & 108 deletions pkg/resources/database_role.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
package resources

import (
"bytes"
"context"
"database/sql"
"encoding/csv"
"fmt"
"log"
"strings"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake"
"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"
)

const (
databaseRoleIDDelimiter = '|'
)

var databaseRoleSchema = map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand All @@ -36,48 +31,6 @@ var databaseRoleSchema = map[string]*schema.Schema{
},
}

type databaseRoleID struct {
DatabaseName string
RoleName string
}

// String() takes in a databaseRoleID object and returns a pipe-delimited string:
// DatabaseName|RoleName.
func (id *databaseRoleID) String() (string, error) {
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
csvWriter.Comma = databaseRoleIDDelimiter
dataIdentifiers := [][]string{{id.DatabaseName, id.RoleName}}
if err := csvWriter.WriteAll(dataIdentifiers); err != nil {
return "", err
}
strDbRoleID := strings.TrimSpace(buf.String())
return strDbRoleID, nil
}

// databaseRoleIDFromString() takes in a pipe-delimited string: DatabaseName|RoleName
// and returns a databaseRoleID object.
func databaseRoleIDFromString(stringID string) (*databaseRoleID, error) {
reader := csv.NewReader(strings.NewReader(stringID))
reader.Comma = pipeIDDelimiter
lines, err := reader.ReadAll()
if err != nil {
return nil, fmt.Errorf("not CSV compatible")
}
if len(lines) != 1 {
return nil, fmt.Errorf("1 line per database role")
}
if len(lines[0]) != 2 {
return nil, fmt.Errorf("2 fields allowed")
}

dbRoleResult := &databaseRoleID{
DatabaseName: lines[0][0],
RoleName: lines[0][1],
}
return dbRoleResult, nil
}

// DatabaseRole returns a pointer to the resource representing a database role.
func DatabaseRole() *schema.Resource {
return &schema.Resource{
Expand All @@ -96,31 +49,15 @@ func DatabaseRole() *schema.Resource {
// ReadDatabaseRole implements schema.ReadFunc.
func ReadDatabaseRole(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
dbRoleID, err := databaseRoleIDFromString(d.Id())
if err != nil {
return err
}
client := sdk.NewClientFromDB(db)

databaseName := dbRoleID.DatabaseName
roleName := dbRoleID.RoleName
objectIdentifier := helpers.DecodeSnowflakeID(d.Id()).(sdk.DatabaseObjectIdentifier)

roles, err := snowflake.ListDatabaseRoles(databaseName, db)
ctx := context.Background()
databaseRole, err := client.DatabaseRoles.ShowByID(ctx, objectIdentifier)
if err != nil {
return fmt.Errorf("error listing database roles err = %w", err)
}

var databaseRole snowflake.DatabaseRole
// find the db role we are looking for by matching the name and db name
for _, dbRole := range roles {
if strings.EqualFold(dbRole.Name, roleName) {
databaseRole = dbRole
databaseRole.DatabaseName = databaseName // needs to be set as it's not included in the SHOW stmt results
break
}
}

if databaseRole.Name == "" {
log.Printf("[DEBUG] database role (%v) not found when listing all database roles in database (%v)", roleName, databaseName)
// If not found, mark resource to be removed from state file during apply or refresh
log.Printf("[DEBUG] database role (%s) not found", d.Id())
d.SetId("")
return nil
}
Expand All @@ -129,7 +66,7 @@ func ReadDatabaseRole(d *schema.ResourceData, meta interface{}) error {
return err
}

if err := d.Set("database", databaseRole.DatabaseName); err != nil {
if err := d.Set("database", objectIdentifier.DatabaseName()); err != nil {
return err
}

Expand All @@ -141,52 +78,45 @@ func ReadDatabaseRole(d *schema.ResourceData, meta interface{}) error {

// CreateDatabaseRole implements schema.CreateFunc.
func CreateDatabaseRole(d *schema.ResourceData, meta interface{}) error {
var err error
db := meta.(*sql.DB)
client := sdk.NewClientFromDB(db)

databaseName := d.Get("database").(string)
roleName := d.Get("name").(string)

builder := snowflake.NewDatabaseRoleBuilder(roleName, databaseName)
if v, ok := d.GetOk("comment"); ok {
builder.WithComment(v.(string))
}
objectIdentifier := sdk.NewDatabaseObjectIdentifier(databaseName, roleName)
createRequest := sdk.NewCreateDatabaseRoleRequest(objectIdentifier)

qry := builder.Create()
if err := snowflake.Exec(db, qry); err != nil {
return fmt.Errorf("error creating database role %v err = %w", roleName, err)
if v, ok := d.GetOk("comment"); ok {
createRequest.WithComment(sdk.String(v.(string)))
}

dbRoleID := &databaseRoleID{
DatabaseName: databaseName,
RoleName: roleName,
}
dataIDInput, err := dbRoleID.String()
ctx := context.Background()
err := client.DatabaseRoles.Create(ctx, createRequest)
if err != nil {
return err
}
d.SetId(dataIDInput)

d.SetId(helpers.EncodeSnowflakeID(objectIdentifier))

return ReadDatabaseRole(d, meta)
}

// UpdateDatabaseRole implements schema.UpdateFunc.
func UpdateDatabaseRole(d *schema.ResourceData, meta interface{}) error {
dbRoleID, err := databaseRoleIDFromString(d.Id())
if err != nil {
return err
}

db := meta.(*sql.DB)
databaseName := dbRoleID.DatabaseName
roleName := dbRoleID.RoleName
builder := snowflake.NewDatabaseRoleBuilder(roleName, databaseName)
client := sdk.NewClientFromDB(db)

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

if d.HasChange("comment") {
var q string
_, newVal := d.GetChange("comment")
q = builder.ChangeComment(newVal.(string))
if err := snowflake.Exec(db, q); err != nil {
return fmt.Errorf("error updating comment on database role %v", d.Id())

ctx := context.Background()
alterRequest := sdk.NewAlterDatabaseRoleRequest(objectIdentifier).WithSetComment(newVal.(string))
err := client.DatabaseRoles.Alter(ctx, alterRequest)
if err != nil {
return fmt.Errorf("error updating database role %v: %w", objectIdentifier.Name(), err)
}
}

Expand All @@ -196,17 +126,15 @@ func UpdateDatabaseRole(d *schema.ResourceData, meta interface{}) error {
// DeleteDatabaseRole implements schema.DeleteFunc.
func DeleteDatabaseRole(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
dbRoleID, err := databaseRoleIDFromString(d.Id())
if err != nil {
return err
}
client := sdk.NewClientFromDB(db)

roleName := dbRoleID.RoleName
databaseName := dbRoleID.DatabaseName
objectIdentifier := helpers.DecodeSnowflakeID(d.Id()).(sdk.DatabaseObjectIdentifier)

q := snowflake.NewDatabaseRoleBuilder(roleName, databaseName).Drop()
if err := snowflake.Exec(db, q); err != nil {
return fmt.Errorf("error deleting database role %v err = %w", d.Id(), err)
ctx := context.Background()
dropRequest := sdk.NewDropDatabaseRoleRequest(objectIdentifier)
err := client.DatabaseRoles.Drop(ctx, dropRequest)
if err != nil {
return err
}

d.SetId("")
Expand Down

0 comments on commit 294075a

Please sign in to comment.