Skip to content

Commit

Permalink
fix: schema name is optional for future snowflake_stage_grant (#1466)
Browse files Browse the repository at this point in the history
* schema_name is optional to enable future stage grant

* docs

* fmt

* refactor
  • Loading branch information
bennylu2 authored Jan 26, 2023
1 parent a1d6b6c commit 0b4d814
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 76 deletions.
4 changes: 2 additions & 2 deletions docs/resources/stage_grant.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ resource "snowflake_stage_grant" "grant" {
### Required

- `database_name` (String) The name of the database containing the current stage on which to grant privileges.
- `schema_name` (String) The name of the schema containing the current stage on which to grant privileges.
- `roles` (Set of String) Grants privilege to these roles.

### Optional

- `enable_multiple_grants` (Boolean) When this is set to true, multiple grants of the same type can be created. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform.
- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future stages in the given schema. When this is true and no schema_name is provided apply this grant on all future stages in the given database. The stage_name field must be unset in order to use on_future.
- `privilege` (String) The privilege to grant on the stage.
- `roles` (Set of String) Grants privilege to these roles.
- `schema_name` (String) The name of the schema containing the current stage on which to grant privileges.
- `stage_name` (String) The name of the stage on which to grant privilege (only valid if on_future is false).
- `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles.

Expand Down
165 changes: 91 additions & 74 deletions pkg/resources/stage_grant.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package resources

import (
"errors"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand All @@ -15,25 +17,27 @@ var validStagePrivileges = NewPrivilegeSet(
)

var stageGrantSchema = map[string]*schema.Schema{
"stage_name": {
Type: schema.TypeString,
Optional: true,
Description: "The name of the stage on which to grant privilege (only valid if on_future is false).",
ForceNew: true,
ConflictsWith: []string{"on_future"},
},
"schema_name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the schema containing the current stage on which to grant privileges.",
ForceNew: true,
},
"database_name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the database containing the current stage on which to grant privileges.",
ForceNew: true,
},
"enable_multiple_grants": {
Type: schema.TypeBool,
Optional: true,
Description: "When this is set to true, multiple grants of the same type can be created. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform.",
Default: false,
ForceNew: true,
},
"on_future": {
Type: schema.TypeBool,
Optional: true,
Description: "When this is set to true and a schema_name is provided, apply this grant on all future stages in the given schema. When this is true and no schema_name is provided apply this grant on all future stages in the given database. The stage_name field must be unset in order to use on_future.",
Default: false,
ForceNew: true,
ConflictsWith: []string{"stage_name"},
},
"privilege": {
Type: schema.TypeString,
Optional: true,
Expand All @@ -44,17 +48,22 @@ var stageGrantSchema = map[string]*schema.Schema{
},
"roles": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "Grants privilege to these roles.",
},
"on_future": {
Type: schema.TypeBool,
"schema_name": {
Type: schema.TypeString,
Optional: true,
Description: "The name of the schema containing the current stage on which to grant privileges.",
ForceNew: true,
},
"stage_name": {
Type: schema.TypeString,
Optional: true,
Description: "When this is set to true and a schema_name is provided, apply this grant on all future stages in the given schema. When this is true and no schema_name is provided apply this grant on all future stages in the given database. The stage_name field must be unset in order to use on_future.",
Default: false,
Description: "The name of the stage on which to grant privilege (only valid if on_future is false).",
ForceNew: true,
ConflictsWith: []string{"stage_name"},
ConflictsWith: []string{"on_future"},
},
"with_grant_option": {
Type: schema.TypeBool,
Expand All @@ -63,13 +72,6 @@ var stageGrantSchema = map[string]*schema.Schema{
Default: false,
ForceNew: true,
},
"enable_multiple_grants": {
Type: schema.TypeBool,
Optional: true,
Description: "When this is set to true, multiple grants of the same type can be created. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform.",
Default: false,
ForceNew: true,
},
}

// StageGrant returns a pointer to the resource representing a stage grant.
Expand All @@ -92,18 +94,32 @@ func StageGrant() *TerraformGrantResource {

// CreateStageGrant implements schema.CreateFunc.
func CreateStageGrant(d *schema.ResourceData, meta interface{}) error {
var stageName string
if name, ok := d.GetOk("stage_name"); ok {
stageName = name.(string)
}
var (
schemaName string
stageName string
)

dbName := d.Get("database_name").(string)
schemaName := d.Get("schema_name").(string)
onFuture := d.Get("on_future").(bool)
priv := d.Get("privilege").(string)
futureStages := d.Get("on_future").(bool)
grantOption := d.Get("with_grant_option").(bool)

if name, ok := d.GetOk("schema_name"); ok {
schemaName = name.(string)
}
if name, ok := d.GetOk("stage_name"); ok {
stageName = name.(string)
}

if (schemaName == "") && !onFuture {
return errors.New("schema_name must be set unless on_future is true")
}
if (stageName == "") && !onFuture {
return errors.New("stage_name must be set unless on_future is true")
}

var builder snowflake.GrantBuilder
if futureStages {
if onFuture {
builder = snowflake.FutureStageGrant(dbName, schemaName)
} else {
builder = snowflake.StageGrant(dbName, schemaName, stageName)
Expand Down Expand Up @@ -135,64 +151,41 @@ func ReadStageGrant(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return err
}

dbName := grantID.ResourceName
schemaName := grantID.SchemaName
stageName := grantID.ObjectName
priv := grantID.Privilege

onFuture := (stageName == "")

if err := d.Set("database_name", dbName); err != nil {
return err
}
if err := d.Set("schema_name", schemaName); err != nil {
if err := d.Set("on_future", onFuture); err != nil {
return err
}
futureStagesEnabled := false
if stageName == "" {
futureStagesEnabled = true
}
if err := d.Set("stage_name", stageName); err != nil {
if err := d.Set("privilege", priv); err != nil {
return err
}
if err := d.Set("on_future", futureStagesEnabled); err != nil {
if err := d.Set("schema_name", schemaName); err != nil {
return err
}
if err := d.Set("privilege", priv); err != nil {
if err := d.Set("stage_name", stageName); err != nil {
return err
}
if err := d.Set("with_grant_option", grantID.GrantOption); err != nil {
return err
}

var builder snowflake.GrantBuilder
if futureStagesEnabled {
if onFuture {
builder = snowflake.FutureStageGrant(dbName, schemaName)
} else {
builder = snowflake.StageGrant(dbName, schemaName, stageName)
}

return readGenericGrant(d, meta, stageGrantSchema, builder, futureStagesEnabled, validStagePrivileges)
}

// DeleteStageGrant implements schema.DeleteFunc.
func DeleteStageGrant(d *schema.ResourceData, meta interface{}) error {
grantID, err := grantIDFromString(d.Id())
if err != nil {
return err
}
dbName := grantID.ResourceName
schemaName := grantID.SchemaName
stageName := grantID.ObjectName

futureStages := (stageName == "")

var builder snowflake.GrantBuilder
if futureStages {
builder = snowflake.FutureStageGrant(dbName, schemaName)
} else {
builder = snowflake.StageGrant(dbName, schemaName, stageName)
}

return deleteGenericGrant(d, meta, builder)
return readGenericGrant(d, meta, stageGrantSchema, builder, onFuture, validStagePrivileges)
}

// UpdateStageGrant implements schema.UpdateFunc.
Expand All @@ -203,13 +196,6 @@ func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error {
return nil
}

rolesToAdd := []string{}
rolesToRevoke := []string{}

if d.HasChange("roles") {
rolesToAdd, rolesToRevoke = changeDiff(d, "roles")
}

grantID, err := grantIDFromString(d.Id())
if err != nil {
return err
Expand All @@ -218,10 +204,18 @@ func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error {
dbName := grantID.ResourceName
schemaName := grantID.SchemaName
stageName := grantID.ObjectName
futureStages := (stageName == "")

onFuture := (stageName == "")

rolesToAdd := []string{}
rolesToRevoke := []string{}

if d.HasChange("roles") {
rolesToAdd, rolesToRevoke = changeDiff(d, "roles")
}

var builder snowflake.GrantBuilder
if futureStages {
if onFuture {
builder = snowflake.FutureStageGrant(dbName, schemaName)
} else {
builder = snowflake.StageGrant(dbName, schemaName, stageName)
Expand All @@ -243,3 +237,26 @@ func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error {
// Done, refresh state
return ReadStageGrant(d, meta)
}

// DeleteStageGrant implements schema.DeleteFunc.
func DeleteStageGrant(d *schema.ResourceData, meta interface{}) error {
grantID, err := grantIDFromString(d.Id())
if err != nil {
return err
}

dbName := grantID.ResourceName
schemaName := grantID.SchemaName
stageName := grantID.ObjectName

onFuture := (stageName == "")

var builder snowflake.GrantBuilder
if onFuture {
builder = snowflake.FutureStageGrant(dbName, schemaName)
} else {
builder = snowflake.StageGrant(dbName, schemaName, stageName)
}

return deleteGenericGrant(d, meta, builder)
}

0 comments on commit 0b4d814

Please sign in to comment.