From f27a9e454102709e1134d796f1594b4932670ae8 Mon Sep 17 00:00:00 2001 From: Arkadius Schuchhardt <34404734+Relativity74205@users.noreply.github.com> Date: Tue, 11 Apr 2023 23:46:04 +0200 Subject: [PATCH] feat: added on_all grants for view, stage, schema and materialized view resource (#1686) * feat: added all view grant * fix: fixed docs to all table grant * feat: added all stage grant * feat: added all schema grant * fix: fixed copy and paste errors in with all schema and all stage grant * feat: added materialized view all grant * docs: fixing typos * test: fixed test after correcting the underlying query * docs: fixed copy & paste error * fix: minor fixes after code review * docs: fixes * test: added acceptance test for view_grants * fix: looking into importing on_future or on_all grants * fix: added logic to distinguish on_future and on_all grants during reading * fix: added getOnFutureOnAll function * fix: fixed on_future import for table_grant * fix: went back to simpler on_all/on_future read-check for view grant * fix: updated read-logic for stage_grant and added acceptance test * fix: updated read-logic for materialized_view_grant and schema_grant and added acceptance test * docs: update * style: fmt linter * style: fmt linter --------- Co-authored-by: Arkadius Schuchhardt Co-authored-by: Scott Winkler --- docs/resources/materialized_view_grant.md | 9 +- docs/resources/schema_grant.md | 5 +- docs/resources/stage_grant.md | 5 +- docs/resources/table_grant.md | 8 +- docs/resources/view_grant.md | 7 +- pkg/resources/helpers_test.go | 8 ++ pkg/resources/materialized_view_grant.go | 87 +++++++++----- ...materialized_view_grant_acceptance_test.go | 82 ++++++++----- pkg/resources/schema_grant.go | 75 +++++++----- pkg/resources/schema_grant_acceptance_test.go | 98 ++++----------- pkg/resources/stage_grant.go | 77 ++++++++---- pkg/resources/stage_grant_acceptance_test.go | 113 +++++++++--------- pkg/resources/table_grant.go | 20 ++-- pkg/resources/table_grant_acceptance_test.go | 104 ++++++++-------- pkg/resources/view_grant.go | 98 +++++++++------ pkg/resources/view_grant_acceptance_test.go | 101 +++++++++------- pkg/snowflake/all_grant.go | 23 ++-- pkg/snowflake/all_grant_test.go | 4 +- 18 files changed, 512 insertions(+), 412 deletions(-) diff --git a/docs/resources/materialized_view_grant.md b/docs/resources/materialized_view_grant.md index f35c3baf..74af323f 100644 --- a/docs/resources/materialized_view_grant.md +++ b/docs/resources/materialized_view_grant.md @@ -38,12 +38,13 @@ resource "snowflake_materialized_view_grant" "grant" { ### 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. -- `materialized_view_name` (String) The name of the materialized view on which to grant privileges immediately (only valid if on_future is false). -- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all future materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_future. -- `privilege` (String) The privilege to grant on the current or future materialized view view. +- `materialized_view_name` (String) The name of the materialized view on which to grant privileges immediately (only valid if on_future and on_all are false). +- `on_all` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported. +- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all future materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all. +- `privilege` (String) The privilege to grant on the current or future materialized view. - `roles` (Set of String) Grants privilege to these roles. - `schema_name` (String) The name of the schema containing the current or future materialized views on which to grant privileges. -- `shares` (Set of String) Grants privilege to these shares (only valid if on_future is false). +- `shares` (Set of String) Grants privilege to these shares (only valid if on_future and on_all are false). - `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. ### Read-Only diff --git a/docs/resources/schema_grant.md b/docs/resources/schema_grant.md index f9e58888..b1968f8a 100644 --- a/docs/resources/schema_grant.md +++ b/docs/resources/schema_grant.md @@ -36,11 +36,12 @@ resource "snowflake_schema_grant" "grant" { ### 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, apply this grant on all future schemas in the given database. The schema_name and shares fields must be unset in order to use on_future. +- `on_all` (Boolean) When this is set to true, apply this grant on all schemas in the given database. The schema_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported. +- `on_future` (Boolean) When this is set to true, apply this grant on all future schemas in the given database. The schema_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all. - `privilege` (String) The privilege to grant on the current or future schema. Note that if "OWNERSHIP" is specified, ensure that the role that terraform is using is granted access. - `roles` (Set of String) Grants privilege to these roles. - `schema_name` (String) The name of the schema on which to grant privileges. -- `shares` (Set of String) Grants privilege to these shares (only valid if on_future is unset). +- `shares` (Set of String) Grants privilege to these shares (only valid if on_future and on_all are unset). - `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. ### Read-Only diff --git a/docs/resources/stage_grant.md b/docs/resources/stage_grant.md index 82612ffb..370feb1c 100644 --- a/docs/resources/stage_grant.md +++ b/docs/resources/stage_grant.md @@ -38,10 +38,11 @@ resource "snowflake_stage_grant" "grant" { ### 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. +- `on_all` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all stages in the given schema. When this is true and no schema_name is provided apply this grant on all stages in the given database. The stage_name field must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported. +- `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. Cannot be used together with on_all. - `privilege` (String) The privilege to grant on the stage. - `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). +- `stage_name` (String) The name of the stage on which to grant privilege (only valid if on_future and on_all are false). - `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. ### Read-Only diff --git a/docs/resources/table_grant.md b/docs/resources/table_grant.md index 970cd577..d8a876fe 100644 --- a/docs/resources/table_grant.md +++ b/docs/resources/table_grant.md @@ -37,13 +37,13 @@ resource "snowflake_table_grant" "grant" { ### 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_all` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all all tables in the given schema. When this is true and no schema_name is provided apply this grant on all all tables in the given database. The table_name and shares fields must be unset in order to use on_all. -- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future tables in the given schema. When this is true and no schema_name is provided apply this grant on all future tables in the given database. The table_name and shares fields must be unset in order to use on_future. +- `on_all` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all tables in the given schema. When this is true and no schema_name is provided apply this grant on all tables in the given database. The table_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported. +- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future tables in the given schema. When this is true and no schema_name is provided apply this grant on all future tables in the given database. The table_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all. - `privilege` (String) The privilege to grant on the current or future table. - `roles` (Set of String) Grants privilege to these roles. - `schema_name` (String) The name of the schema containing the current or future tables on which to grant privileges. -- `shares` (Set of String) Grants privilege to these shares (only valid if on_future or on_all is unset). -- `table_name` (String) The name of the table on which to grant privileges immediately (only valid if on_future or on_all is unset). +- `shares` (Set of String) Grants privilege to these shares (only valid if on_future or on_all are unset). +- `table_name` (String) The name of the table on which to grant privileges immediately (only valid if on_future or on_all are unset). - `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. ### Read-Only diff --git a/docs/resources/view_grant.md b/docs/resources/view_grant.md index cc917eb9..93a323ac 100644 --- a/docs/resources/view_grant.md +++ b/docs/resources/view_grant.md @@ -51,12 +51,13 @@ resource "snowflake_schema_grant" "grant" { ### 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 views in the given schema. When this is true and no schema_name is provided apply this grant on all future views in the given database. The view_name and shares fields must be unset in order to use on_future. +- `on_all` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all views in the given schema. When this is true and no schema_name is provided apply this grant on all views in the given database. The view_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported. +- `on_future` (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future views in the given schema. When this is true and no schema_name is provided apply this grant on all future views in the given database. The view_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all. - `privilege` (String) The privilege to grant on the current or future view. - `roles` (Set of String) Grants privilege to these roles. - `schema_name` (String) The name of the schema containing the current or future views on which to grant privileges. -- `shares` (Set of String) Grants privilege to these shares (only valid if on_future is unset). -- `view_name` (String) The name of the view on which to grant privileges immediately (only valid if on_future is unset). +- `shares` (Set of String) Grants privilege to these shares (only valid if on_future and on_all are unset). +- `view_name` (String) The name of the view on which to grant privileges immediately (only valid if on_future and on_all are unset). - `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. ### Read-Only diff --git a/pkg/resources/helpers_test.go b/pkg/resources/helpers_test.go index 94b0be1b..0e26d5b6 100644 --- a/pkg/resources/helpers_test.go +++ b/pkg/resources/helpers_test.go @@ -10,6 +10,14 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" ) +type grantType int + +const ( + normal grantType = iota + onFuture + onAll +) + func database(t *testing.T, id string, params map[string]interface{}) *schema.ResourceData { t.Helper() r := require.New(t) diff --git a/pkg/resources/materialized_view_grant.go b/pkg/resources/materialized_view_grant.go index 40d7c2fb..eede049c 100644 --- a/pkg/resources/materialized_view_grant.go +++ b/pkg/resources/materialized_view_grant.go @@ -13,7 +13,7 @@ import ( ) /* -NewPriviligeSet creates a set of privileges that are allowed +NewPrivilegeSet creates a set of privileges that are allowed They are used for validation in the schema object below. */ @@ -28,7 +28,7 @@ var materializedViewGrantSchema = map[string]*schema.Schema{ "materialized_view_name": { Type: schema.TypeString, Optional: true, - Description: "The name of the materialized view on which to grant privileges immediately (only valid if on_future is false).", + Description: "The name of the materialized view on which to grant privileges immediately (only valid if on_future and on_all are false).", ForceNew: true, }, "schema_name": { @@ -46,7 +46,7 @@ var materializedViewGrantSchema = map[string]*schema.Schema{ "privilege": { Type: schema.TypeString, Optional: true, - Description: "The privilege to grant on the current or future materialized view view.", + Description: "The privilege to grant on the current or future materialized view.", Default: "SELECT", ValidateFunc: validation.StringInSlice(validMaterializedViewPrivileges.ToList(), true), ForceNew: true, @@ -61,12 +61,19 @@ var materializedViewGrantSchema = map[string]*schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Description: "Grants privilege to these shares (only valid if on_future is false).", + Description: "Grants privilege to these shares (only valid if on_future and on_all are false).", }, "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 materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all future materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_future.", + Description: "When this is set to true and a schema_name is provided, apply this grant on all future materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all future materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all.", + Default: false, + ForceNew: true, + }, + "on_all": { + Type: schema.TypeBool, + Optional: true, + Description: "When this is set to true and a schema_name is provided, apply this grant on all materialized views in the given schema. When this is true and no schema_name is provided apply this grant on all materialized views in the given database. The materialized_view_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported.", Default: false, ForceNew: true, }, @@ -140,26 +147,30 @@ func CreateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error databaseName := d.Get("database_name").(string) schemaName := d.Get("schema_name").(string) privilege := d.Get("privilege").(string) - futureMaterializedViews := d.Get("on_future").(bool) + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) withGrantOption := d.Get("with_grant_option").(bool) roles := expandStringList(d.Get("roles").(*schema.Set).List()) shares := expandStringList(d.Get("shares").(*schema.Set).List()) - if (schemaName == "") && !futureMaterializedViews { - return errors.New("schema_name must be set unless on_future is true") + if (schemaName == "") && !onFuture && !onAll { + return errors.New("schema_name must be set unless on_future or on_all is true") } - if (materializedViewName == "") && !futureMaterializedViews { - return errors.New("materialized_view_name must be set unless on_future is true") + if (materializedViewName == "") && !onFuture && !onAll { + return errors.New("materialized_view_name must be set unless on_future or on_all is true") } - if (materializedViewName != "") && futureMaterializedViews { - return errors.New("materialized_view_name must be empty if on_future is true") + if (materializedViewName != "") && onFuture && onAll { + return errors.New("materialized_view_name must be empty if on_future and on_all is true") } var builder snowflake.GrantBuilder - if futureMaterializedViews { + switch { + case onFuture: builder = snowflake.FutureMaterializedViewGrant(databaseName, schemaName) - } else { + case onAll: + builder = snowflake.AllMaterializedViewGrant(databaseName, schemaName) + default: builder = snowflake.MaterializedViewGrant(databaseName, schemaName, materializedViewName) } @@ -185,16 +196,9 @@ func ReadMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error { if err := d.Set("schema_name", grantID.SchemaName); err != nil { return err } - futureMaterializedViewsEnabled := false - if grantID.ObjectName == "" { - futureMaterializedViewsEnabled = true - } if err := d.Set("materialized_view_name", grantID.ObjectName); err != nil { return err } - if err := d.Set("on_future", futureMaterializedViewsEnabled); err != nil { - return err - } if err := d.Set("privilege", grantID.Privilege); err != nil { return err } @@ -202,16 +206,25 @@ func ReadMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error { return err } + onAll := d.Get("on_all").(bool) // importing on_all is not supported as there is no way to determine on_all state in snowflake + onFuture := false + if grantID.ObjectName == "" && !onAll { + onFuture = true + } + if err = d.Set("on_future", onFuture); err != nil { + return err + } var builder snowflake.GrantBuilder - if futureMaterializedViewsEnabled { + switch { + case onFuture: builder = snowflake.FutureMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.MaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } - // TODO - onAll := false - return readGenericGrant(d, meta, materializedViewGrantSchema, builder, futureMaterializedViewsEnabled, onAll, validMaterializedViewPrivileges) + return readGenericGrant(d, meta, materializedViewGrantSchema, builder, onFuture, onAll, validMaterializedViewPrivileges) } // DeleteMaterializedViewGrant implements schema.DeleteFunc. @@ -221,12 +234,16 @@ func DeleteMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error return err } - futureMaterializedViews := (grantID.ObjectName == "") + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) var builder snowflake.GrantBuilder - if futureMaterializedViews { + switch { + case onFuture: builder = snowflake.FutureMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.MaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } return deleteGenericGrant(d, meta, builder) @@ -235,7 +252,7 @@ func DeleteMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error // UpdateMaterializedViewGrant implements schema.UpdateFunc. func UpdateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error { // for now the only thing we can update are roles or shares - // if nothing changed, nothing to update and we're done + // if nothing changed, nothing to update, and we're done if !d.HasChanges("roles", "shares") { return nil } @@ -255,13 +272,17 @@ func UpdateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error return err } - futureMaterializedViews := (grantID.ObjectName == "") + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) // create the builder var builder snowflake.GrantBuilder - if futureMaterializedViews { + switch { + case onFuture: builder = snowflake.FutureMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllMaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.MaterializedViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } diff --git a/pkg/resources/materialized_view_grant_acceptance_test.go b/pkg/resources/materialized_view_grant_acceptance_test.go index 6105d2c2..f422e4af 100644 --- a/pkg/resources/materialized_view_grant_acceptance_test.go +++ b/pkg/resources/materialized_view_grant_acceptance_test.go @@ -1,75 +1,97 @@ package resources_test import ( - "bytes" + "fmt" "strings" "testing" - "text/template" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/stretchr/testify/require" ) func TestAcc_MaterializedViewFutureGrant(t *testing.T) { - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: materializedViewGrantConfigFuture(t, databaseName, schemaName, roleName), + Config: materializedViewGrantConfigFuture(name, onFuture), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "database_name", databaseName), - resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "schema_name", schemaName), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "database_name", name), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "schema_name", name), resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "materialized_view_name", ""), resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "with_grant_option", "false"), resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "on_future", "true"), resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "privilege", "SELECT"), ), }, + // IMPORT + { + ResourceName: "snowflake_materialized_view_grant.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported + }, + }, + }, + }) +} + +func TestAcc_MaterializedViewAllGrant(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: materializedViewGrantConfigFuture(name, onAll), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "database_name", name), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "materialized_view_name", ""), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "with_grant_option", "false"), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "on_all", "true"), + resource.TestCheckResourceAttr("snowflake_materialized_view_grant.test", "privilege", "SELECT"), + ), + }, }, }) } -func materializedViewGrantConfigFuture(t *testing.T, databaseName, schemaName, role string) string { - t.Helper() - r := require.New(t) +func materializedViewGrantConfigFuture(name string, grantType grantType) string { + var materializedViewNameConfig string + switch grantType { + case onFuture: + materializedViewNameConfig = "on_future = true" + case onAll: + materializedViewNameConfig = "on_all = true" + } - config := ` + return fmt.Sprintf(` resource "snowflake_database" "test" { - name = "{{ .database_name }}" + name = "%s" } resource "snowflake_schema" "test" { - name = "{{ .schema_name }}" + name = "%s" database = snowflake_database.test.name } resource "snowflake_role" "test" { - name = "{{.role_name}}" + name = "%s" } resource "snowflake_materialized_view_grant" "test" { database_name = snowflake_database.test.name roles = [snowflake_role.test.name] schema_name = snowflake_schema.test.name - on_future = true + %s privilege = "SELECT" } -` - - out := bytes.NewBuffer(nil) - tmpl := template.Must(template.New("view)").Parse(config)) - err := tmpl.Execute(out, map[string]string{ - "database_name": databaseName, - "schema_name": schemaName, - "role_name": role, - }) - r.NoError(err) - - return out.String() +`, name, name, name, materializedViewNameConfig) } diff --git a/pkg/resources/schema_grant.go b/pkg/resources/schema_grant.go index 6812b6ce..48902dd0 100644 --- a/pkg/resources/schema_grant.go +++ b/pkg/resources/schema_grant.go @@ -68,12 +68,20 @@ var schemaGrantSchema = map[string]*schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Description: "Grants privilege to these shares (only valid if on_future is unset).", + Description: "Grants privilege to these shares (only valid if on_future and on_all are unset).", }, "on_future": { Type: schema.TypeBool, Optional: true, - Description: "When this is set to true, apply this grant on all future schemas in the given database. The schema_name and shares fields must be unset in order to use on_future.", + Description: "When this is set to true, apply this grant on all future schemas in the given database. The schema_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all.", + Default: false, + ForceNew: true, + ConflictsWith: []string{"schema_name", "shares"}, + }, + "on_all": { + Type: schema.TypeBool, + Optional: true, + Description: "When this is set to true, apply this grant on all schemas in the given database. The schema_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported.", Default: false, ForceNew: true, ConflictsWith: []string{"schema_name", "shares"}, @@ -144,18 +152,25 @@ func CreateSchemaGrant(d *schema.ResourceData, meta interface{}) error { databaseName := d.Get("database_name").(string) privilege := d.Get("privilege").(string) onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + if onFuture && onAll { + return errors.New("on_future and on_all cannot both be true") + } withGrantOption := d.Get("with_grant_option").(bool) roles := expandStringList(d.Get("roles").(*schema.Set).List()) shares := expandStringList(d.Get("shares").(*schema.Set).List()) - if (schemaName == "") && !onFuture { - return errors.New("schema_name must be set unless on_future is true") + if (schemaName == "") && !onFuture && !onAll { + return errors.New("schema_name must be set unless on_future or on_all is true") } var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureSchemaGrant(databaseName) - } else { + case onAll: + builder = snowflake.AllSchemaGrant(databaseName) + default: builder = snowflake.SchemaGrant(databaseName, schemaName) } @@ -172,7 +187,7 @@ func CreateSchemaGrant(d *schema.ResourceData, meta interface{}) error { // UpdateSchemaGrant implements schema.UpdateFunc. func UpdateSchemaGrant(d *schema.ResourceData, meta interface{}) error { // for now the only thing we can update are roles or shares - // if nothing changed, nothing to update and we're done + // if nothing changed, nothing to update, and we're done if !d.HasChanges("roles", "shares") { return nil } @@ -194,12 +209,16 @@ func UpdateSchemaGrant(d *schema.ResourceData, meta interface{}) error { } onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) // create the builder var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureSchemaGrant(grantID.DatabaseName) - } else { + case onAll: + builder = snowflake.AllSchemaGrant(grantID.DatabaseName) + default: builder = snowflake.SchemaGrant(grantID.DatabaseName, grantID.SchemaName) } @@ -243,13 +262,6 @@ func ReadSchemaGrant(d *schema.ResourceData, meta interface{}) error { if err := d.Set("schema_name", grantID.SchemaName); err != nil { return err } - onFuture := false - if grantID.SchemaName == "" { - onFuture = true - } - if err := d.Set("on_future", onFuture); err != nil { - return err - } if err := d.Set("privilege", grantID.Privilege); err != nil { return err } @@ -257,14 +269,23 @@ func ReadSchemaGrant(d *schema.ResourceData, meta interface{}) error { return err } + onAll := d.Get("on_all").(bool) // importing on_all is not supported as there is no way to determine on_all state in snowflake + onFuture := false + if grantID.SchemaName == "" && !onAll { + onFuture = true + } + if err = d.Set("on_future", onFuture); err != nil { + return err + } var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureSchemaGrant(grantID.DatabaseName) - } else { + case onAll: + builder = snowflake.AllSchemaGrant(grantID.DatabaseName) + default: builder = snowflake.SchemaGrant(grantID.DatabaseName, grantID.SchemaName) } - // TODO - onAll := false return readGenericGrant(d, meta, schemaGrantSchema, builder, onFuture, onAll, validSchemaPrivileges) } @@ -276,15 +297,15 @@ func DeleteSchemaGrant(d *schema.ResourceData, meta interface{}) error { return err } - onFuture := false - if grantID.SchemaName == "" { - onFuture = true - } - + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureSchemaGrant(grantID.DatabaseName) - } else { + case onAll: + builder = snowflake.AllSchemaGrant(grantID.DatabaseName) + default: builder = snowflake.SchemaGrant(grantID.DatabaseName, grantID.SchemaName) } return deleteGenericGrant(d, meta, builder) diff --git a/pkg/resources/schema_grant_acceptance_test.go b/pkg/resources/schema_grant_acceptance_test.go index 70c5f94c..49c35f77 100644 --- a/pkg/resources/schema_grant_acceptance_test.go +++ b/pkg/resources/schema_grant_acceptance_test.go @@ -10,27 +10,27 @@ import ( ) func TestAcc_SchemaGrant(t *testing.T) { - sName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: schemaGrantConfig(sName, roleName, shareName, false), + Config: schemaGrantConfig(name, normal), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_schema_grant.test", "schema_name", sName), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_all", "false"), resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_future", "false"), resource.TestCheckResourceAttr("snowflake_schema_grant.test", "privilege", "USAGE"), ), }, // FUTURE SHARES { - Config: schemaGrantConfig(sName, roleName, shareName, true), + Config: schemaGrantConfig(name, onFuture), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_schema_grant.test", "schema_name", ""), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_all", "false"), resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_future", "true"), resource.TestCheckResourceAttr("snowflake_schema_grant.test", "privilege", "USAGE"), ), @@ -42,90 +42,42 @@ func TestAcc_SchemaGrant(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{ "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported }, }, }, }) } -func TestAcc_SchemaFutureGrants(t *testing.T) { - sName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleNameTable := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleNameView := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) +func TestAcc_SchemaGrantOnAll(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ - // TABLE AND VIEW FUTURE GRANTS { - Config: futureTableAndViewGrantConfig(sName, roleNameTable, roleNameView), + Config: schemaGrantConfig(name, onAll), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_view_grant.select_on_future_views", "roles.#", "1"), - resource.TestCheckResourceAttr("snowflake_table_grant.select_on_future_tables", "roles.#", "1"), - resource.TestCheckResourceAttr("snowflake_view_grant.select_on_future_views", "privilege", "SELECT"), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "schema_name", ""), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_all", "true"), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "on_future", "false"), + resource.TestCheckResourceAttr("snowflake_schema_grant.test", "privilege", "USAGE"), ), }, - // IMPORT - { - ResourceName: "snowflake_view_grant.select_on_future_views", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{ - "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported - }, - }, }, }) } -func futureTableAndViewGrantConfig(n, roleTable, roleView string) string { - return fmt.Sprintf(` -resource "snowflake_database" "test" { - name = "%v" -} - -resource "snowflake_schema" "test" { - name = "%v" - database = snowflake_database.test.name - comment = "Terraform acceptance test" -} - -resource "snowflake_role" "table_reader" { - name = "%v" -} - -resource "snowflake_role" "view_reader" { - name = "%v" -} - -resource "snowflake_table_grant" "select_on_future_tables" { - database_name = snowflake_database.test.name - schema_name = snowflake_schema.test.name - privilege = "SELECT" - on_future = true - roles = [snowflake_role.table_reader.name] - depends_on = [snowflake_schema.test, snowflake_role.table_reader] -} - -resource "snowflake_view_grant" "select_on_future_views" { - database_name = snowflake_database.test.name - schema_name = snowflake_schema.test.name - privilege = "SELECT" - on_future = true - roles = [snowflake_role.view_reader.name] - depends_on = [snowflake_schema.test, snowflake_role.view_reader] -} - -`, n, n, roleTable, roleView) -} - -func schemaGrantConfig(n, role, share string, future bool) string { - schemaNameConfig := `schema_name = snowflake_schema.test.name - shares = [snowflake_share.test.name]` - - if future { - schemaNameConfig = "on_future = true" +func schemaGrantConfig(name string, grantType grantType) string { + var schemaNameConfig string + switch grantType { + case normal: + schemaNameConfig = "schema_name = snowflake_schema.test.name" + case onFuture: + schemaNameConfig = "on_future = true" + case onAll: + schemaNameConfig = "on_all = true" } return fmt.Sprintf(` @@ -156,8 +108,6 @@ resource "snowflake_schema_grant" "test" { database_name = snowflake_schema.test.database %v roles = [snowflake_role.test.name] - - depends_on = [snowflake_database_grant.test] } -`, n, n, role, share, schemaNameConfig) +`, name, name, name, name, schemaNameConfig) } diff --git a/pkg/resources/stage_grant.go b/pkg/resources/stage_grant.go index dbc4de6b..693f2d21 100644 --- a/pkg/resources/stage_grant.go +++ b/pkg/resources/stage_grant.go @@ -37,7 +37,15 @@ var stageGrantSchema = map[string]*schema.Schema{ "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.", + 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. Cannot be used together with on_all.", + Default: false, + ForceNew: true, + ConflictsWith: []string{"stage_name"}, + }, + "on_all": { + Type: schema.TypeBool, + Optional: true, + Description: "When this is set to true and a schema_name is provided, apply this grant on all stages in the given schema. When this is true and no schema_name is provided apply this grant on all stages in the given database. The stage_name field must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported.", Default: false, ForceNew: true, ConflictsWith: []string{"stage_name"}, @@ -65,7 +73,7 @@ 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).", + Description: "The name of the stage on which to grant privilege (only valid if on_future and on_all are false).", ForceNew: true, ConflictsWith: []string{"on_future"}, }, @@ -124,6 +132,10 @@ func StageGrant() *TerraformGrantResource { func CreateStageGrant(d *schema.ResourceData, meta interface{}) error { databaseName := d.Get("database_name").(string) onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + if onFuture && onAll { + return errors.New("on_future and on_all cannot both be true") + } privilege := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) @@ -136,17 +148,20 @@ func CreateStageGrant(d *schema.ResourceData, meta interface{}) error { stageName = name.(string) } - if (schemaName == "") && !onFuture { - return errors.New("schema_name must be set unless on_future is true") + if (schemaName == "") && !onFuture && !onAll { + return errors.New("schema_name must be set unless on_future or on_all is true") } - if (stageName == "") && !onFuture { - return errors.New("stage_name must be set unless on_future is true") + if (stageName == "") && !onFuture && !onAll { + return errors.New("stage_name must be set unless on_future or on_all is true") } var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureStageGrant(databaseName, schemaName) - } else { + case onAll: + builder = snowflake.AllStageGrant(databaseName, schemaName) + default: builder = snowflake.StageGrant(databaseName, schemaName, stageName) } @@ -167,14 +182,9 @@ func ReadStageGrant(d *schema.ResourceData, meta interface{}) error { return err } - onFuture := (grantID.ObjectName == "") - if err := d.Set("database_name", grantID.DatabaseName); err != nil { return err } - if err := d.Set("on_future", onFuture); err != nil { - return err - } if err := d.Set("privilege", grantID.Privilege); err != nil { return err } @@ -188,14 +198,23 @@ func ReadStageGrant(d *schema.ResourceData, meta interface{}) error { return err } + onAll := d.Get("on_all").(bool) // importing on_all is not supported as there is no way to determine on_all state in snowflake + onFuture := false + if grantID.ObjectName == "" && !onAll { + onFuture = true + } + if err = d.Set("on_future", onFuture); err != nil { + return err + } var builder snowflake.GrantBuilder - if onFuture { + switch { + case onFuture: builder = snowflake.FutureStageGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllStageGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.StageGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } - // TODO - onAll := false return readGenericGrant(d, meta, stageGrantSchema, builder, onFuture, onAll, validStagePrivileges) } @@ -203,7 +222,7 @@ func ReadStageGrant(d *schema.ResourceData, meta interface{}) error { // UpdateStageGrant implements schema.UpdateFunc. func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error { // for now the only thing we can update are roles or shares - // if nothing changed, nothing to update and we're done + // if nothing changed, nothing to update, and we're done if !d.HasChanges("roles") { return nil } @@ -213,8 +232,6 @@ func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error { return err } - onFuture := (grantID.ObjectName == "") - rolesToAdd := []string{} rolesToRevoke := []string{} @@ -223,9 +240,14 @@ func UpdateStageGrant(d *schema.ResourceData, meta interface{}) error { } var builder snowflake.GrantBuilder - if onFuture { + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + switch { + case onFuture: builder = snowflake.FutureStageGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllStageGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.StageGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } @@ -253,12 +275,15 @@ func DeleteStageGrant(d *schema.ResourceData, meta interface{}) error { return err } - onFuture := (grantID.ObjectName == "") - var builder snowflake.GrantBuilder - if onFuture { + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + switch { + case onFuture: builder = snowflake.FutureStageGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllStageGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.StageGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } diff --git a/pkg/resources/stage_grant_acceptance_test.go b/pkg/resources/stage_grant_acceptance_test.go index 957c0d28..3ac0def9 100644 --- a/pkg/resources/stage_grant_acceptance_test.go +++ b/pkg/resources/stage_grant_acceptance_test.go @@ -1,15 +1,12 @@ package resources_test import ( - "bytes" "fmt" "strings" "testing" - "text/template" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/stretchr/testify/require" ) func TestAcc_StageGrant_defaults(t *testing.T) { @@ -20,7 +17,7 @@ func TestAcc_StageGrant_defaults(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: stageGrantConfig(name), + Config: stageGrantConfig(name, normal), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_database.d", "name", name), resource.TestCheckResourceAttr("snowflake_schema.s", "name", name), @@ -28,6 +25,7 @@ func TestAcc_StageGrant_defaults(t *testing.T) { resource.TestCheckResourceAttr("snowflake_role.r", "name", name), resource.TestCheckResourceAttr("snowflake_stage_grant.g", "database_name", name), resource.TestCheckResourceAttr("snowflake_stage_grant.g", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "privilege", "READ"), testRolesAndShares(t, "snowflake_stage_grant.g", []string{name}), ), }, @@ -38,27 +36,38 @@ func TestAcc_StageGrant_defaults(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{ "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported }, }, }, }) } -func stageGrantConfig(n string) string { +func stageGrantConfig(name string, grantType grantType) string { + var stageNameConfig string + switch grantType { + case normal: + stageNameConfig = "stage_name = snowflake_stage.s.name" + case onFuture: + stageNameConfig = "on_future = true" + case onAll: + stageNameConfig = "on_all = true" + } + return fmt.Sprintf(` resource snowflake_database d { - name = "%v" + name = "%s" comment = "Terraform acceptance test" } resource snowflake_schema s { - name = "%v" + name = "%s" database = snowflake_database.d.name comment = "Terraform acceptance test" } resource snowflake_stage s { - name = "%v" + name = "%s" database = snowflake_database.d.name schema = snowflake_schema.s.name comment = "Terraform acceptance test" @@ -71,7 +80,7 @@ func stageGrantConfig(n string) string { resource snowflake_stage_grant g { database_name = snowflake_database.d.name schema_name = snowflake_schema.s.name - stage_name = snowflake_stage.s.name + %s privilege = "READ" @@ -79,69 +88,59 @@ func stageGrantConfig(n string) string { snowflake_role.r.name ] } -`, n, n, n, n) +`, name, name, name, name, stageNameConfig) } func TestAcc_StageFutureGrant(t *testing.T) { - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: stageGrantConfigFuture(t, databaseName, schemaName, roleName), + Config: stageGrantConfig(name, onFuture), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "database_name", databaseName), - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "schema_name", schemaName), - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "stage_name", ""), - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "with_grant_option", "false"), - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "on_future", "true"), - resource.TestCheckResourceAttr("snowflake_stage_grant.test", "privilege", "USAGE"), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "database_name", name), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "stage_name", ""), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "with_grant_option", "false"), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "on_future", "true"), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "privilege", "READ"), ), }, + // IMPORT + { + ResourceName: "snowflake_stage_grant.g", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported + }, + }, }, }) } -func stageGrantConfigFuture(t *testing.T, databaseName, schemaName, role string) string { - t.Helper() - r := require.New(t) - - config := ` -resource "snowflake_database" "test" { - name = "{{ .database_name }}" -} +func TestAcc_StageGrantOnAll(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) -resource "snowflake_schema" "test" { - name = "{{ .schema_name }}" - database = snowflake_database.test.name -} - -resource "snowflake_role" "test" { - name = "{{.role_name}}" -} - -resource "snowflake_stage_grant" "test" { - database_name = snowflake_database.test.name - roles = ["{{.role_name}}"] - schema_name = snowflake_schema.test.name - on_future = true - depends_on = [snowflake_role.test] - privilege = "USAGE" -} -` - - out := bytes.NewBuffer(nil) - tmpl := template.Must(template.New("view)").Parse(config)) - err := tmpl.Execute(out, map[string]string{ - "database_name": databaseName, - "schema_name": schemaName, - "role_name": role, + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: stageGrantConfig(name, onAll), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "database_name", name), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "stage_name", ""), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "with_grant_option", "false"), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "on_all", "true"), + resource.TestCheckResourceAttr("snowflake_stage_grant.g", "privilege", "READ"), + ), + }, + }, }) - r.NoError(err) - - return out.String() } diff --git a/pkg/resources/table_grant.go b/pkg/resources/table_grant.go index 48cc01f5..b6a9042a 100644 --- a/pkg/resources/table_grant.go +++ b/pkg/resources/table_grant.go @@ -27,7 +27,7 @@ var tableGrantSchema = map[string]*schema.Schema{ "table_name": { Type: schema.TypeString, Optional: true, - Description: "The name of the table on which to grant privileges immediately (only valid if on_future or on_all is unset).", + Description: "The name of the table on which to grant privileges immediately (only valid if on_future or on_all are unset).", ForceNew: true, }, "schema_name": { @@ -60,12 +60,12 @@ var tableGrantSchema = map[string]*schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Description: "Grants privilege to these shares (only valid if on_future or on_all is unset).", + Description: "Grants privilege to these shares (only valid if on_future or on_all are unset).", }, "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 tables in the given schema. When this is true and no schema_name is provided apply this grant on all future tables in the given database. The table_name and shares fields must be unset in order to use on_future.", + Description: "When this is set to true and a schema_name is provided, apply this grant on all future tables in the given schema. When this is true and no schema_name is provided apply this grant on all future tables in the given database. The table_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all.", Default: false, ForceNew: true, ConflictsWith: []string{"table_name", "shares"}, @@ -73,7 +73,7 @@ var tableGrantSchema = map[string]*schema.Schema{ "on_all": { Type: schema.TypeBool, Optional: true, - Description: "When this is set to true and a schema_name is provided, apply this grant on all all tables in the given schema. When this is true and no schema_name is provided apply this grant on all all tables in the given database. The table_name and shares fields must be unset in order to use on_all.", + Description: "When this is set to true and a schema_name is provided, apply this grant on all tables in the given schema. When this is true and no schema_name is provided apply this grant on all tables in the given database. The table_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported.", Default: false, ForceNew: true, ConflictsWith: []string{"table_name", "shares"}, @@ -209,8 +209,14 @@ func ReadTableGrant(d *schema.ResourceData, meta interface{}) error { return err } - onFuture := d.Get("on_future").(bool) - onAll := d.Get("on_all").(bool) + onAll := d.Get("on_all").(bool) // importing on_all is not supported as there is no way to determine on_all state in snowflake + onFuture := false + if grantID.ObjectName == "" && !onAll { + onFuture = true + } + if err = d.Set("on_future", onFuture); err != nil { + return err + } var builder snowflake.GrantBuilder switch { case onFuture: @@ -247,7 +253,7 @@ func DeleteTableGrant(d *schema.ResourceData, meta interface{}) error { // UpdateTableGrant implements schema.UpdateFunc. func UpdateTableGrant(d *schema.ResourceData, meta interface{}) error { // for now the only thing we can update are roles or shares - // if nothing changed, nothing to update and we're done + // if nothing changed, nothing to update, and we're done if !d.HasChanges("roles", "shares") { return nil } diff --git a/pkg/resources/table_grant_acceptance_test.go b/pkg/resources/table_grant_acceptance_test.go index 963c8278..158a567f 100644 --- a/pkg/resources/table_grant_acceptance_test.go +++ b/pkg/resources/table_grant_acceptance_test.go @@ -2,6 +2,7 @@ package resources_test import ( "fmt" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -9,14 +10,14 @@ import ( ) func TestAccTableGrant_onAll(t *testing.T) { - name := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: tableGrantConfigOnAll(name), + Config: tableGrantConfig(name, onAll), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_table_grant.g", "database_name", name), @@ -34,15 +35,51 @@ func TestAccTableGrant_onAll(t *testing.T) { }) } +func TestAccTableGrant_onFuture(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: tableGrantConfig(name, onFuture), + + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_table_grant.g", "database_name", name), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "on_future", "true"), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "privilege", "SELECT"), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "with_grant_option", "false"), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "roles.#", "1"), + resource.TestCheckResourceAttr("snowflake_table_grant.g", "roles.0", name), + + testRolesAndShares(t, "snowflake_table_grant.g", []string{name}), + ), + }, + // IMPORT + { + ResourceName: "snowflake_table_grant.g", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported + }, + }, + }, + }) +} + func TestAccTableGrant_defaults(t *testing.T) { - name := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: tableGrantConfig(name), + Config: tableGrantConfig(name, normal), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_database.d", "name", name), @@ -63,56 +100,24 @@ func TestAccTableGrant_defaults(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{ "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported - "with_grant_option", - "on_future", - "on_all", + "on_all", // not defined in Snowflake, can't be imported }, }, }, }) } -func tableGrantConfig(n string) string { - return fmt.Sprintf(` - -resource snowflake_database d { - name = "%s" -} - -resource snowflake_schema s { - name = "%s" - database = snowflake_database.d.name -} - -resource snowflake_role r { - name = "%s" -} - -resource snowflake_table t { - database = snowflake_database.d.name - schema = snowflake_schema.s.name - name = "%s" - - column { - name = "id" - type = "NUMBER(38,0)" +func tableGrantConfig(name string, grantType grantType) string { + var tableNameConfig string + switch grantType { + case normal: + tableNameConfig = "table_name = snowflake_table.t.name" + case onFuture: + tableNameConfig = "on_future = true" + case onAll: + tableNameConfig = "on_all = true" } -} -resource snowflake_table_grant g { - database_name = snowflake_database.d.name - schema_name = snowflake_schema.s.name - table_name = snowflake_table.t.name - privilege = "SELECT" - roles = [ - snowflake_role.r.name - ] -} - -`, n, n, n, n) -} - -func tableGrantConfigOnAll(n string) string { return fmt.Sprintf(` resource snowflake_database d { @@ -140,15 +145,14 @@ resource snowflake_table t { } resource snowflake_table_grant g { + %s database_name = snowflake_database.d.name schema_name = snowflake_schema.s.name - + privilege = "SELECT" roles = [ snowflake_role.r.name ] - privilege = "SELECT" - on_all = true } -`, n, n, n, n) +`, name, name, name, name, tableNameConfig) } diff --git a/pkg/resources/view_grant.go b/pkg/resources/view_grant.go index 0c826f17..ec35a3a6 100644 --- a/pkg/resources/view_grant.go +++ b/pkg/resources/view_grant.go @@ -22,7 +22,7 @@ var viewGrantSchema = map[string]*schema.Schema{ "view_name": { Type: schema.TypeString, Optional: true, - Description: "The name of the view on which to grant privileges immediately (only valid if on_future is unset).", + Description: "The name of the view on which to grant privileges immediately (only valid if on_future and on_all are unset).", ForceNew: true, }, "schema_name": { @@ -55,12 +55,20 @@ var viewGrantSchema = map[string]*schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Description: "Grants privilege to these shares (only valid if on_future is unset).", + Description: "Grants privilege to these shares (only valid if on_future and on_all are unset).", }, "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 views in the given schema. When this is true and no schema_name is provided apply this grant on all future views in the given database. The view_name and shares fields must be unset in order to use on_future.", + Description: "When this is set to true and a schema_name is provided, apply this grant on all future views in the given schema. When this is true and no schema_name is provided apply this grant on all future views in the given database. The view_name and shares fields must be unset in order to use on_future. Cannot be used together with on_all.", + Default: false, + ForceNew: true, + ConflictsWith: []string{"view_name", "shares"}, + }, + "on_all": { + Type: schema.TypeBool, + Optional: true, + Description: "When this is set to true and a schema_name is provided, apply this grant on all views in the given schema. When this is true and no schema_name is provided apply this grant on all views in the given database. The view_name and shares fields must be unset in order to use on_all. Cannot be used together with on_future. Importing the resource with the on_all=true option is not supported.", Default: false, ForceNew: true, ConflictsWith: []string{"view_name", "shares"}, @@ -137,24 +145,31 @@ func CreateViewGrant(d *schema.ResourceData, meta interface{}) error { } databaseName := d.Get("database_name").(string) privilege := d.Get("privilege").(string) - futureViews := d.Get("on_future").(bool) + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + if onFuture && onAll { + return errors.New("on_future and on_all cannot both be true") + } withGrantOption := d.Get("with_grant_option").(bool) roles := expandStringList(d.Get("roles").(*schema.Set).List()) shares := expandStringList(d.Get("shares").(*schema.Set).List()) - if (schemaName == "") && !futureViews { - return errors.New("schema_name must be set unless on_future is true") + if (schemaName == "") && !onFuture && !onAll { + return errors.New("schema_name must be set unless on_future or on_all is true") } - if (viewName == "") && !futureViews { - return errors.New("view_name must be set unless on_future is true") + if (viewName == "") && !onFuture && !onAll { + return errors.New("view_name must be set unless on_future or on_all is true") } - if (viewName != "") && futureViews { - return errors.New("view_name must be empty if on_future is true") + if (viewName != "") && onFuture && onAll { + return errors.New("view_name must be empty if on_future and on_all are true") } var builder snowflake.GrantBuilder - if futureViews { + switch { + case onFuture: builder = snowflake.FutureViewGrant(databaseName, schemaName) - } else { + case onAll: + builder = snowflake.AllViewGrant(databaseName, schemaName) + default: builder = snowflake.ViewGrant(databaseName, schemaName, viewName) } @@ -179,42 +194,39 @@ func ReadViewGrant(d *schema.ResourceData, meta interface{}) error { if err := d.Set("database_name", grantID.DatabaseName); err != nil { return err } - if err := d.Set("schema_name", grantID.SchemaName); err != nil { return err } - - futureViewsEnabled := false - if grantID.ObjectName == "" { - futureViewsEnabled = true - } - err = d.Set("view_name", grantID.ObjectName) - if err != nil { + if err := d.Set("view_name", grantID.ObjectName); err != nil { return err } - err = d.Set("on_future", futureViewsEnabled) - if err != nil { + if err := d.Set("privilege", grantID.Privilege); err != nil { return err } - err = d.Set("privilege", grantID.Privilege) - if err != nil { + if err := d.Set("with_grant_option", grantID.WithGrantOption); err != nil { return err } - err = d.Set("with_grant_option", grantID.WithGrantOption) - if err != nil { + + onAll := d.Get("on_all").(bool) // importing on_all is not supported as there is no way to determine on_all state in snowflake + onFuture := false + if grantID.ObjectName == "" && !onAll { + onFuture = true + } + if err = d.Set("on_future", onFuture); err != nil { return err } var builder snowflake.GrantBuilder - if futureViewsEnabled { + switch { + case onFuture: builder = snowflake.FutureViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.ViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } - // TODO - onAll := false - return readGenericGrant(d, meta, viewGrantSchema, builder, futureViewsEnabled, onAll, validViewPrivileges) + return readGenericGrant(d, meta, viewGrantSchema, builder, onFuture, onAll, validViewPrivileges) } // DeleteViewGrant implements schema.DeleteFunc. @@ -224,21 +236,26 @@ func DeleteViewGrant(d *schema.ResourceData, meta interface{}) error { return err } - futureViews := (grantID.ObjectName == "") + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) var builder snowflake.GrantBuilder - if futureViews { + switch { + case onFuture: builder = snowflake.FutureViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.ViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } + return deleteGenericGrant(d, meta, builder) } // UpdateViewGrant implements schema.UpdateFunc. func UpdateViewGrant(d *schema.ResourceData, meta interface{}) error { // for now the only thing we can update are roles or shares - // if nothing changed, nothing to update and we're done + // if nothing changed, nothing to update, and we're done if !d.HasChanges("roles", "shares") { return nil } @@ -258,13 +275,16 @@ func UpdateViewGrant(d *schema.ResourceData, meta interface{}) error { return err } - futureViews := (grantID.ObjectName == "") - // create the builder var builder snowflake.GrantBuilder - if futureViews { + onFuture := d.Get("on_future").(bool) + onAll := d.Get("on_all").(bool) + switch { + case onFuture: builder = snowflake.FutureViewGrant(grantID.DatabaseName, grantID.SchemaName) - } else { + case onAll: + builder = snowflake.AllViewGrant(grantID.DatabaseName, grantID.SchemaName) + default: builder = snowflake.ViewGrant(grantID.DatabaseName, grantID.SchemaName, grantID.ObjectName) } diff --git a/pkg/resources/view_grant_acceptance_test.go b/pkg/resources/view_grant_acceptance_test.go index 1fe3e0bb..83922e27 100644 --- a/pkg/resources/view_grant_acceptance_test.go +++ b/pkg/resources/view_grant_acceptance_test.go @@ -2,6 +2,7 @@ package resources_test import ( "bytes" + "fmt" "strings" "testing" "text/template" @@ -12,21 +13,28 @@ import ( ) func TestAcc_ViewGrantBasic(t *testing.T) { - viewName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: viewGrantConfigFuture(t, databaseName, viewName, roleName, false), + Config: viewGrantConfig(name, normal), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_view_grant.test", "view_name", viewName), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "view_name", name), resource.TestCheckResourceAttr("snowflake_view_grant.test", "privilege", "SELECT"), ), }, + { + ResourceName: "snowflake_view_grant.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported + }, + }, }, }) } @@ -53,25 +61,23 @@ func TestAcc_ViewGrantShares(t *testing.T) { } func TestAcc_FutureViewGrantChange(t *testing.T) { - viewName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.ParallelTest(t, resource.TestCase{ Providers: providers(), CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: viewGrantConfigFuture(t, databaseName, viewName, roleName, false), + Config: viewGrantConfig(name, normal), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_view_grant.test", "view_name", viewName), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "view_name", name), resource.TestCheckResourceAttr("snowflake_view_grant.test", "on_future", "false"), resource.TestCheckResourceAttr("snowflake_view_grant.test", "privilege", "SELECT"), ), }, // CHANGE FROM CURRENT TO FUTURE VIEWS { - Config: viewGrantConfigFuture(t, databaseName, viewName, roleName, true), + Config: viewGrantConfig(name, onFuture), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_view_grant.test", "view_name", ""), resource.TestCheckResourceAttr("snowflake_view_grant.test", "on_future", "true"), @@ -85,6 +91,7 @@ func TestAcc_FutureViewGrantChange(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{ "enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported + "on_all", // not defined in Snowflake, can't be imported }, }, }, @@ -157,57 +164,69 @@ resource "snowflake_view_grant" "test" { return out.String() } -func viewGrantConfigFuture(t *testing.T, databaseName, viewName string, role string, future bool) string { - t.Helper() - r := require.New(t) - - viewNameConfig := "view_name = snowflake_view.test.name" - if future { +func viewGrantConfig(name string, grantType grantType) string { + var viewNameConfig string + switch grantType { + case normal: + viewNameConfig = "view_name = snowflake_view.test.name" + case onFuture: viewNameConfig = "on_future = true" + case onAll: + viewNameConfig = "on_all = true" } - config := ` + return fmt.Sprintf(` resource "snowflake_database" "test" { - name = "{{ .database_name }}" + name = "%s" } resource "snowflake_schema" "test" { - name = "{{ .schema_name }}" + name = "%s" database = snowflake_database.test.name } resource "snowflake_view" "test" { - name = "{{.view_name}}" - database = snowflake_database.test.name - schema = snowflake_schema.test.name + name = "%s" + database = snowflake_database.test.name + schema = snowflake_schema.test.name statement = "SELECT ROLE_NAME, ROLE_OWNER FROM INFORMATION_SCHEMA.APPLICABLE_ROLES" is_secure = true } resource "snowflake_role" "test" { - name = "{{.role_name}}" + name = "%s" } resource "snowflake_view_grant" "test" { - {{.view_name_config}} + %s database_name = snowflake_view.test.database - roles = ["{{.role_name}}"] - schema_name = snowflake_schema.test.name - depends_on = [snowflake_role.test] - privilege = "SELECT" + roles = [snowflake_role.test.name] + schema_name = snowflake_schema.test.name + privilege = "SELECT" +} +`, name, name, name, name, viewNameConfig) } -` - out := bytes.NewBuffer(nil) - tmpl := template.Must(template.New("view)").Parse(config)) - err := tmpl.Execute(out, map[string]string{ - "database_name": databaseName, - "schema_name": databaseName, - "view_name": viewName, - "role_name": role, - "view_name_config": viewNameConfig, - }) - r.NoError(err) +func TestAcc_ViewGrantOnAll(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - return out.String() + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: viewGrantConfig(name, onAll), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_view_grant.test", "database_name", name), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "schema_name", name), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "on_all", "true"), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "privilege", "SELECT"), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "with_grant_option", "false"), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "roles.#", "1"), + resource.TestCheckResourceAttr("snowflake_view_grant.test", "roles.0", name), + testRolesAndShares(t, "snowflake_view_grant.test", []string{name}), + ), + }, + }, + }) } diff --git a/pkg/snowflake/all_grant.go b/pkg/snowflake/all_grant.go index 07a7465c..8351f654 100644 --- a/pkg/snowflake/all_grant.go +++ b/pkg/snowflake/all_grant.go @@ -61,8 +61,8 @@ func (fgb *AllGrantBuilder) GrantType() string { return string(fgb.allGrantType) } -// ExistingSchemaGrant returns a pointer to a AllGrantBuilder for a schema. -func ExistingSchemaGrant(db string) GrantBuilder { +// AllSchemaGrant returns a pointer to a AllGrantBuilder for a schema. +func AllSchemaGrant(db string) GrantBuilder { return &AllGrantBuilder{ name: db, qualifiedName: fmt.Sprintf(`"%v"`, db), @@ -82,8 +82,8 @@ func AllTableGrant(db, schema string) GrantBuilder { } } -// ExistingViewGrant returns a pointer to a AllGrantBuilder for a view. -func ExistingViewGrant(db, schema string) GrantBuilder { +// AllViewGrant returns a pointer to a AllGrantBuilder for a view. +func AllViewGrant(db, schema string) GrantBuilder { name, qualifiedName, target := getNameAndQualifiedNameForAllGrants(db, schema) return &AllGrantBuilder{ name: name, @@ -93,8 +93,8 @@ func ExistingViewGrant(db, schema string) GrantBuilder { } } -// ExistingMaterializedViewGrant returns a pointer to a AllGrantBuilder for a view. -func ExistingMaterializedViewGrant(db, schema string) GrantBuilder { +// AllMaterializedViewGrant returns a pointer to a AllGrantBuilder for a view. +func AllMaterializedViewGrant(db, schema string) GrantBuilder { name, qualifiedName, target := getNameAndQualifiedNameForAllGrants(db, schema) return &AllGrantBuilder{ name: name, @@ -104,8 +104,8 @@ func ExistingMaterializedViewGrant(db, schema string) GrantBuilder { } } -// ExistingStageGrant returns a pointer to a AllGrantBuilder for a stage. -func ExistingStageGrant(db, schema string) GrantBuilder { +// AllStageGrant returns a pointer to a AllGrantBuilder for a stage. +func AllStageGrant(db, schema string) GrantBuilder { name, qualifiedName, target := getNameAndQualifiedNameForAllGrants(db, schema) return &AllGrantBuilder{ name: name, @@ -246,14 +246,15 @@ func (fge *ExistingGrantExecutable) Grant(p string, w bool) string { // Revoke returns the SQL that will revoke all privileges on the grant from the grantee. func (fge *ExistingGrantExecutable) Revoke(p string) []string { - // TODO: has no effect for ALL GRANTS + // Note: has no effect for ALL GRANTS return []string{ fmt.Sprintf(`REVOKE %v ON ALL %vS IN %v %v FROM ROLE "%v"`, p, fge.allGrantType, fge.allGrantTarget, fge.grantName, fge.granteeName), } } -// Show returns the SQL that will show all all grants on the schema. +// Show returns the SQL that will show all grants on the schema. func (fge *ExistingGrantExecutable) Show() string { - return fmt.Sprintf(`SHOW ALL GRANTS IN %v %v`, fge.allGrantTarget, fge.grantName) + // Note: There is no `SHOW ALL GRANTS IN \"test_db\"`, therefore changed the query to `SHOW ALL GRANTS IN \"test_db\"` to have a command, which runs in snowflake. + return fmt.Sprintf(`SHOW GRANTS ON %v %v`, fge.allGrantTarget, fge.grantName) } diff --git a/pkg/snowflake/all_grant_test.go b/pkg/snowflake/all_grant_test.go index 6fddbe2b..84fd341b 100644 --- a/pkg/snowflake/all_grant_test.go +++ b/pkg/snowflake/all_grant_test.go @@ -9,14 +9,14 @@ import ( func TestExistingSchemaGrant(t *testing.T) { r := require.New(t) - builder := snowflake.ExistingSchemaGrant("test_db") + builder := snowflake.AllSchemaGrant("test_db") r.Equal("test_db", builder.Name()) r.Equal(string(snowflake.AllGrantTypeSchema), builder.GrantType()) eb := builder.Role("bob") - r.Equal("SHOW ALL GRANTS IN DATABASE \"test_db\"", eb.Show()) + r.Equal("SHOW GRANTS ON DATABASE \"test_db\"", eb.Show()) r.Equal(`GRANT USAGE ON ALL SCHEMAS IN DATABASE "test_db" TO ROLE "bob"`, eb.Grant("USAGE", false)) r.Equal([]string{`REVOKE USAGE ON ALL SCHEMAS IN DATABASE "test_db" FROM ROLE "bob"`}, eb.Revoke("USAGE")) }