From 7026f14ae26e56c7d9e6987fbede69f5741ddab5 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 14:27:05 +0100 Subject: [PATCH 01/27] Fix after review from previous PR --- pkg/resources/file_format_acceptance_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/resources/file_format_acceptance_test.go b/pkg/resources/file_format_acceptance_test.go index e2a0f7bc97..cb6281e911 100644 --- a/pkg/resources/file_format_acceptance_test.go +++ b/pkg/resources/file_format_acceptance_test.go @@ -70,10 +70,9 @@ func TestAcc_FileFormatCSV(t *testing.T) { }, // IMPORT { - ResourceName: "snowflake_file_format.test", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{}, + ResourceName: "snowflake_file_format.test", + ImportState: true, + ImportStateVerify: true, }, }, }) From cc6ca4af601d36e3c6406ca62cff4f76218f46c1 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 14:27:24 +0100 Subject: [PATCH 02/27] Adjust snowflake_grants schema according to docs --- pkg/datasources/grants.go | 178 ++++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 67 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 2afbe494d7..7bbf6af7e8 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -8,21 +8,19 @@ import ( var grantsSchema = map[string]*schema.Schema{ "grants_on": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_of", "grants_to", "future_grants_in", "future_grants_to"}, - Description: "Lists all privileges that have been granted on an object or account", - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Lists all privileges that have been granted on an object or on an account.", + ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "object_name": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"grants_on.0.object_type"}, - Description: "Name of object to list privileges on", - ConflictsWith: []string{"grants_on.0.account"}, - AtLeastOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"grants_on.0.object_type"}, + ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + Description: "Name of object to list privileges on.", }, "object_type": { Type: schema.TypeString, @@ -35,82 +33,123 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeBool, Optional: true, Description: "Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles.", - ConflictsWith: []string{"grants_on.0.object_name", "grants_on.0.object_type"}, - AtLeastOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + ConflictsWith: []string{"grants_on.0.object_type"}, }, }, }, }, "grants_to": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "future_grants_in", "future_grants_to"}, - Description: "Lists all privileges granted to the object", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges granted to the object.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "application": { Type: schema.TypeString, Optional: true, - Description: "Lists all privileges and roles granted to the role", - ConflictsWith: []string{ - "grants_to.0.user", - "grants_to.0.share", - }, + Description: "Lists all the privileges and roles granted to the application.", ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", "grants_to.0.role", "grants_to.0.user", "grants_to.0.share", }, }, - "user": { + "application_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed", - ConflictsWith: []string{ + Description: "Lists all the privileges and roles granted to the application role. Note: if fully qualified application role identifier is not specified, i.e. only the application role name is given, Snowflake uses the current application. If the application is not a database, this command does not return results. Consult with the proper section in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants).", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.user", "grants_to.0.share", }, + }, + "role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all privileges and roles granted to the role.", ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", "grants_to.0.role", "grants_to.0.user", "grants_to.0.share", }, }, - "share": { + "user": { Type: schema.TypeString, Optional: true, - Description: "Lists all the privileges granted to the share", - ConflictsWith: []string{ + Description: "Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed.", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", "grants_to.0.role", "grants_to.0.user", + "grants_to.0.share", }, + }, + "share": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Lists all the privileges granted to the share.", ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", "grants_to.0.role", "grants_to.0.user", "grants_to.0.share", }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "share_name": { + Type: schema.TypeString, + Required: true, + Description: "Lists all of the privileges and roles granted to the specified share.", + }, + "in_application_package": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all of the privileges and roles granted to a share in the specified application package.", + }, + }, + }, }, }, }, }, "grants_of": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_to", "future_grants_in", "future_grants_to"}, - Description: "Lists all objects to which the given object has been granted", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Description: "Lists all objects to which the given object has been granted.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "role": { Type: schema.TypeString, Optional: true, - Description: "Lists all users and roles to which the role has been granted", - ConflictsWith: []string{ + Description: "Lists all users and roles to which the role has been granted.", + ExactlyOneOf: []string{ + "grants_of.0.role", + "grants_of.0.application_role", "grants_of.0.share", }, + }, + "application_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all the users and roles to which the application role has been granted. Note: if fully qualified application role identifier is not specified, i.e. only the application role name is given, Snowflake uses the current application. If the application is not a database, this command does not return results. Consult with the proper section in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants).", ExactlyOneOf: []string{ "grants_of.0.role", + "grants_of.0.application_role", "grants_of.0.share", }, }, @@ -118,11 +157,9 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeString, Optional: true, Description: "Lists all the accounts for the share and indicates the accounts that are using the share.", - ConflictsWith: []string{ - "grants_of.0.role", - }, ExactlyOneOf: []string{ "grants_of.0.role", + "grants_of.0.application_role", "grants_of.0.share", }, }, @@ -130,20 +167,17 @@ var grantsSchema = map[string]*schema.Schema{ }, }, "future_grants_in": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "grants_to", "future_grants_to"}, - Description: "Lists all privileges on new (i.e. future) objects", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "database": { Type: schema.TypeString, Optional: true, Description: "Lists all privileges on new (i.e. future) objects of a specified type in the database granted to a role.", - ConflictsWith: []string{ - "future_grants_in.0.schema", - }, ExactlyOneOf: []string{ "future_grants_in.0.database", "future_grants_in.0.schema", @@ -154,9 +188,6 @@ var grantsSchema = map[string]*schema.Schema{ MaxItems: 1, Optional: true, Description: "Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role.", - ConflictsWith: []string{ - "future_grants_in.0.database", - }, ExactlyOneOf: []string{ "future_grants_in.0.database", "future_grants_in.0.schema", @@ -166,7 +197,7 @@ var grantsSchema = map[string]*schema.Schema{ "schema_name": { Type: schema.TypeString, Required: true, - Description: "The name of the schema to list all privileges of new (ie. future) objects granted to", + Description: "The name of the schema to list all privileges of new (ie. future) objects granted to.", }, "database_name": { Type: schema.TypeString, @@ -180,17 +211,30 @@ var grantsSchema = map[string]*schema.Schema{ }, }, "future_grants_to": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "grants_to", "future_grants_in"}, - Description: "Lists all privileges granted to the object on new (i.e. future) objects", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges granted to the object on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "role": { Type: schema.TypeString, - Required: true, + Optional: true, Description: "Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role.", + ExactlyOneOf: []string{ + "future_grants_to.0.role", + "future_grants_to.0.database_role", + }, + }, + "database_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all privileges on new (i.e. future) objects granted to the database role.", + ExactlyOneOf: []string{ + "future_grants_to.0.role", + "future_grants_to.0.database_role", + }, }, }, }, @@ -203,42 +247,42 @@ var grantsSchema = map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "created_on": { Type: schema.TypeString, - Description: "The date and time the grant was created", + Description: "The date and time the grant was created.", Computed: true, }, "privilege": { Type: schema.TypeString, - Description: "The privilege granted", + Description: "The privilege granted.", Computed: true, }, "granted_on": { Type: schema.TypeString, - Description: "The object on which the privilege was granted", + Description: "The object on which the privilege was granted.", Computed: true, }, "name": { Type: schema.TypeString, - Description: "The name of the object on which the privilege was granted", + Description: "The name of the object on which the privilege was granted.", Computed: true, }, "granted_to": { Type: schema.TypeString, - Description: "The role to which the privilege was granted", + Description: "The role to which the privilege was granted.", Computed: true, }, "grantee_name": { Type: schema.TypeString, - Description: "The name of the role to which the privilege was granted", + Description: "The name of the role to which the privilege was granted.", Computed: true, }, "grant_option": { Type: schema.TypeBool, - Description: "Whether the grantee can grant the privilege to others", + Description: "Whether the grantee can grant the privilege to others.", Computed: true, }, "granted_by": { Type: schema.TypeString, - Description: "The role that granted the privilege", + Description: "The role that granted the privilege.", Computed: true, }, }, From 9c167e2833e066b8da8e9528c937f7e3d080077f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 15:38:35 +0100 Subject: [PATCH 03/27] Use ReadContext --- pkg/datasources/grants.go | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 7bbf6af7e8..710336166f 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -1,8 +1,11 @@ package datasources import ( + "context" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -292,12 +295,12 @@ var grantsSchema = map[string]*schema.Schema{ func Grants() *schema.Resource { return &schema.Resource{ - Read: ReadGrants, - Schema: grantsSchema, + ReadContext: ReadGrants, + Schema: grantsSchema, } } -func ReadGrants(d *schema.ResourceData, meta interface{}) error { +func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client db := client.GetConn().DB @@ -312,12 +315,12 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { if account { grantDetails, err = snowflake.ShowGrantsOnAccount(db) if err != nil { - return err + return diag.FromErr(err) } } else if objectType != "" && objectName != "" { grantDetails, err = snowflake.ShowGrantsOn(db, objectType, objectName) if err != nil { - return err + return diag.FromErr(err) } } } @@ -328,21 +331,21 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { if role != "" { grantDetails, err = snowflake.ShowGrantsTo(db, "ROLE", role) if err != nil { - return err + return diag.FromErr(err) } } user := grantsTo["user"].(string) if user != "" { grantDetails, err = snowflake.ShowGrantsTo(db, "USER", user) if err != nil { - return err + return diag.FromErr(err) } } share := grantsTo["share"].(string) if share != "" { grantDetails, err = snowflake.ShowGrantsTo(db, "SHARE", share) if err != nil { - return err + return diag.FromErr(err) } } } @@ -353,14 +356,14 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { if role != "" { grantDetails, err = snowflake.ShowGrantsOf(db, "ROLE", role) if err != nil { - return err + return diag.FromErr(err) } } share := grantsOf["share"].(string) if share != "" { grantDetails, err = snowflake.ShowGrantsOf(db, "SHARE", share) if err != nil { - return err + return diag.FromErr(err) } } } @@ -371,7 +374,7 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { if database != "" { grantDetails, err = snowflake.ShowFutureGrantsIn(db, "DATABASE", database) if err != nil { - return err + return diag.FromErr(err) } } schema := futureGrantsIn["schema"].([]interface{}) @@ -385,7 +388,7 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { grantDetails, err = snowflake.ShowFutureGrantsIn(db, "SCHEMA", schemaName) if err != nil { - return err + return diag.FromErr(err) } } } @@ -396,14 +399,14 @@ func ReadGrants(d *schema.ResourceData, meta interface{}) error { if role != "" { grantDetails, err = snowflake.ShowFutureGrantsTo(db, "ROLE", role) if err != nil { - return err + return diag.FromErr(err) } } } err = d.Set("grants", flattenGrants(grantDetails)) if err != nil { - return err + return diag.FromErr(err) } d.SetId("grants") return nil From dad2260359832058ba1da8e42f1bc77304b6d21b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 15:50:58 +0100 Subject: [PATCH 04/27] Extract particular cases before changing to SDK --- pkg/datasources/grants.go | 219 +++++++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 87 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 710336166f..391c69008a 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -2,6 +2,7 @@ package datasources import ( "context" + "database/sql" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" @@ -15,7 +16,7 @@ var grantsSchema = map[string]*schema.Schema{ MaxItems: 1, Optional: true, Description: "Lists all privileges that have been granted on an object or on an account.", - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "object_name": { @@ -46,7 +47,7 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeList, MaxItems: 1, Optional: true, - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Description: "Lists all privileges granted to the object.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -132,7 +133,7 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeList, MaxItems: 1, Optional: true, - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Description: "Lists all objects to which the given object has been granted.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -173,7 +174,7 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeList, MaxItems: 1, Optional: true, - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Description: "Lists all privileges on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -217,7 +218,7 @@ var grantsSchema = map[string]*schema.Schema{ Type: schema.TypeList, MaxItems: 1, Optional: true, - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Description: "Lists all privileges granted to the object on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -307,109 +308,153 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d var grantDetails []snowflake.GrantDetail var err error if v, ok := d.GetOk("grants_on"); ok { - grantsOn := v.([]interface{})[0].(map[string]interface{}) - objectType := grantsOn["object_type"].(string) - objectName := grantsOn["object_name"].(string) - account := grantsOn["account"].(bool) + grantDetails, err = handleGrantsOn(v, db) + } + + if v, ok := d.GetOk("grants_to"); ok { + grantDetails, err = handleGrantsTo(v, db) + } + + if v, ok := d.GetOk("grants_of"); ok { + grantDetails, err = handleGrantsOf(v, db) + } + + if v, ok := d.GetOk("future_grants_in"); ok { + grantDetails, err = handleFutureGrantsIn(v, db) + } + + if v, ok := d.GetOk("future_grants_to"); ok { + grantDetails, err = handleFutureGrantsTo(v, db) + } + + if err != nil { + return diag.FromErr(err) + } - if account { - grantDetails, err = snowflake.ShowGrantsOnAccount(db) - if err != nil { - return diag.FromErr(err) - } - } else if objectType != "" && objectName != "" { - grantDetails, err = snowflake.ShowGrantsOn(db, objectType, objectName) - if err != nil { - return diag.FromErr(err) - } + err = d.Set("grants", flattenGrants(grantDetails)) + if err != nil { + return diag.FromErr(err) + } + d.SetId("grants") + return nil +} + +func handleGrantsOn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { + var grantDetails []snowflake.GrantDetail + var err error + + grantsOn := v.([]interface{})[0].(map[string]interface{}) + objectType := grantsOn["object_type"].(string) + objectName := grantsOn["object_name"].(string) + account := grantsOn["account"].(bool) + + if account { + grantDetails, err = snowflake.ShowGrantsOnAccount(db) + if err != nil { + return grantDetails, err + } + } else if objectType != "" && objectName != "" { + grantDetails, err = snowflake.ShowGrantsOn(db, objectType, objectName) + if err != nil { + return grantDetails, err } } + return grantDetails, nil +} - if v, ok := d.GetOk("grants_to"); ok { - grantsTo := v.([]interface{})[0].(map[string]interface{}) - role := grantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "ROLE", role) - if err != nil { - return diag.FromErr(err) - } +func handleGrantsTo(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { + var grantDetails []snowflake.GrantDetail + var err error + + grantsTo := v.([]interface{})[0].(map[string]interface{}) + role := grantsTo["role"].(string) + if role != "" { + grantDetails, err = snowflake.ShowGrantsTo(db, "ROLE", role) + if err != nil { + return grantDetails, err } - user := grantsTo["user"].(string) - if user != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "USER", user) - if err != nil { - return diag.FromErr(err) - } + } + user := grantsTo["user"].(string) + if user != "" { + grantDetails, err = snowflake.ShowGrantsTo(db, "USER", user) + if err != nil { + return grantDetails, err } - share := grantsTo["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "SHARE", share) - if err != nil { - return diag.FromErr(err) - } + } + share := grantsTo["share"].(string) + if share != "" { + grantDetails, err = snowflake.ShowGrantsTo(db, "SHARE", share) + if err != nil { + return grantDetails, err } } + return grantDetails, nil +} - if v, ok := d.GetOk("grants_of"); ok { - grantsOf := v.([]interface{})[0].(map[string]interface{}) - role := grantsOf["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "ROLE", role) - if err != nil { - return diag.FromErr(err) - } +func handleGrantsOf(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { + var grantDetails []snowflake.GrantDetail + var err error + + grantsOf := v.([]interface{})[0].(map[string]interface{}) + role := grantsOf["role"].(string) + if role != "" { + grantDetails, err = snowflake.ShowGrantsOf(db, "ROLE", role) + if err != nil { + return grantDetails, err } - share := grantsOf["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "SHARE", share) - if err != nil { - return diag.FromErr(err) - } + } + share := grantsOf["share"].(string) + if share != "" { + grantDetails, err = snowflake.ShowGrantsOf(db, "SHARE", share) + if err != nil { + return grantDetails, err } } + return grantDetails, nil +} - if v, ok := d.GetOk("future_grants_in"); ok { - futureGrantsIn := v.([]interface{})[0].(map[string]interface{}) - database := futureGrantsIn["database"].(string) - if database != "" { - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "DATABASE", database) - if err != nil { - return diag.FromErr(err) - } - } - schema := futureGrantsIn["schema"].([]interface{}) - if len(schema) > 0 { - schemaMap := schema[0].(map[string]interface{}) - schemaName := schemaMap["schema_name"].(string) - databaseName := schemaMap["database_name"].(string) - if databaseName != "" { - schemaName = databaseName + "." + schemaName - } +func handleFutureGrantsIn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { + var grantDetails []snowflake.GrantDetail + var err error - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "SCHEMA", schemaName) - if err != nil { - return diag.FromErr(err) - } + futureGrantsIn := v.([]interface{})[0].(map[string]interface{}) + database := futureGrantsIn["database"].(string) + if database != "" { + grantDetails, err = snowflake.ShowFutureGrantsIn(db, "DATABASE", database) + if err != nil { + return grantDetails, err } } + schema := futureGrantsIn["schema"].([]interface{}) + if len(schema) > 0 { + schemaMap := schema[0].(map[string]interface{}) + schemaName := schemaMap["schema_name"].(string) + databaseName := schemaMap["database_name"].(string) + if databaseName != "" { + schemaName = databaseName + "." + schemaName + } - if v, ok := d.GetOk("future_grants_to"); ok { - futureGrantsTo := v.([]interface{})[0].(map[string]interface{}) - role := futureGrantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowFutureGrantsTo(db, "ROLE", role) - if err != nil { - return diag.FromErr(err) - } + grantDetails, err = snowflake.ShowFutureGrantsIn(db, "SCHEMA", schemaName) + if err != nil { + return grantDetails, err } } + return grantDetails, nil +} - err = d.Set("grants", flattenGrants(grantDetails)) - if err != nil { - return diag.FromErr(err) +func handleFutureGrantsTo(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { + var grantDetails []snowflake.GrantDetail + var err error + + futureGrantsTo := v.([]interface{})[0].(map[string]interface{}) + role := futureGrantsTo["role"].(string) + if role != "" { + grantDetails, err = snowflake.ShowFutureGrantsTo(db, "ROLE", role) + if err != nil { + return grantDetails, err + } } - d.SetId("grants") - return nil + return grantDetails, nil } func flattenGrants(grants []snowflake.GrantDetail) []map[string]interface{} { From 4cfb866a39eeb6f7af35d8236dcca15f10fe38e6 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 16:34:29 +0100 Subject: [PATCH 05/27] Migrate part of the implementation to the SDK --- pkg/datasources/grants.go | 153 +++++++++++++++++++++++++++++--------- 1 file changed, 117 insertions(+), 36 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 391c69008a..2b6c8f18af 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -4,7 +4,9 @@ import ( "context" "database/sql" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -306,25 +308,27 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d db := client.GetConn().DB var grantDetails []snowflake.GrantDetail + var grants []sdk.Grant + _ = grants var err error if v, ok := d.GetOk("grants_on"); ok { - grantDetails, err = handleGrantsOn(v, db) + grants, err = handleGrantsOn(ctx, client, v) } if v, ok := d.GetOk("grants_to"); ok { - grantDetails, err = handleGrantsTo(v, db) + grants, err = handleGrantsTo(ctx, client, v) } if v, ok := d.GetOk("grants_of"); ok { - grantDetails, err = handleGrantsOf(v, db) + grantDetails, err = handleGrantsOf(ctx, client, v, db) } if v, ok := d.GetOk("future_grants_in"); ok { - grantDetails, err = handleFutureGrantsIn(v, db) + grantDetails, err = handleFutureGrantsIn(ctx, client, v, db) } if v, ok := d.GetOk("future_grants_to"); ok { - grantDetails, err = handleFutureGrantsTo(v, db) + grantDetails, err = handleFutureGrantsTo(ctx, client, v, db) } if err != nil { @@ -339,9 +343,9 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d return nil } -func handleGrantsOn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { - var grantDetails []snowflake.GrantDetail +func handleGrantsOn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { var err error + opts := new(sdk.ShowGrantOptions) grantsOn := v.([]interface{})[0].(map[string]interface{}) objectType := grantsOn["object_type"].(string) @@ -349,49 +353,59 @@ func handleGrantsOn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { account := grantsOn["account"].(bool) if account { - grantDetails, err = snowflake.ShowGrantsOnAccount(db) - if err != nil { - return grantDetails, err + opts.On = &sdk.ShowGrantsOn{ + Account: sdk.Bool(true), + } + } else { + if objectType == "" || objectName == "" { + return nil, err } - } else if objectType != "" && objectName != "" { - grantDetails, err = snowflake.ShowGrantsOn(db, objectType, objectName) + objectId, err := helpers.DecodeSnowflakeParameterID(objectName) if err != nil { - return grantDetails, err + return nil, err + } + opts.On = &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectType(objectType), + Name: objectId, + }, } } - return grantDetails, nil + return client.Grants.Show(ctx, opts) } -func handleGrantsTo(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { - var grantDetails []snowflake.GrantDetail - var err error +func handleGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { + opts := new(sdk.ShowGrantOptions) grantsTo := v.([]interface{})[0].(map[string]interface{}) - role := grantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "ROLE", role) - if err != nil { - return grantDetails, err + // TODO: add database role? + if application := grantsTo["application"].(string); application != "" { + // TODO: unsupported SHOW GRANTS TO APPLICATION + } + if applicationRole := grantsTo["application_role"].(string); applicationRole != "" { + // TODO: unsupported SHOW GRANTS TO APPLICATION + } + if role := grantsTo["role"].(string); role != "" { + opts.To = &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(role), } } - user := grantsTo["user"].(string) - if user != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "USER", user) - if err != nil { - return grantDetails, err + if user := grantsTo["user"].(string); user != "" { + opts.To = &sdk.ShowGrantsTo{ + User: sdk.NewAccountObjectIdentifier(user), } } - share := grantsTo["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "SHARE", share) - if err != nil { - return grantDetails, err + if share := grantsTo["share"]; share != nil { + shareMap := share.([]interface{})[0].(map[string]interface{}) + opts.To = &sdk.ShowGrantsTo{ + Share: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), } + // TODO: unsupported IN APPLICATION PACKAGE } - return grantDetails, nil + return client.Grants.Show(ctx, opts) } -func handleGrantsOf(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { +func handleGrantsOf(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { var grantDetails []snowflake.GrantDetail var err error @@ -413,7 +427,7 @@ func handleGrantsOf(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { return grantDetails, nil } -func handleFutureGrantsIn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { +func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { var grantDetails []snowflake.GrantDetail var err error @@ -442,7 +456,7 @@ func handleFutureGrantsIn(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { return grantDetails, nil } -func handleFutureGrantsTo(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { +func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { var grantDetails []snowflake.GrantDetail var err error @@ -457,6 +471,73 @@ func handleFutureGrantsTo(v any, db *sql.DB) ([]snowflake.GrantDetail, error) { return grantDetails, nil } +func aaa() (*sdk.ShowGrantOptions, sdk.ObjectType) { + opts := new(sdk.ShowGrantOptions) + var grantedOn sdk.ObjectType + + //switch id.Kind { + //case OnAccountObjectAccountRoleGrantKind: + // data := id.Data.(*OnAccountObjectGrantData) + // grantedOn = data.ObjectType + // opts.On = &sdk.ShowGrantsOn{ + // Object: &sdk.Object{ + // ObjectType: data.ObjectType, + // Name: data.ObjectName, + // }, + // } + //case OnSchemaAccountRoleGrantKind: + // grantedOn = sdk.ObjectTypeSchema + // data := id.Data.(*OnSchemaGrantData) + // + // switch data.Kind { + // case OnSchemaSchemaGrantKind: + // opts.On = &sdk.ShowGrantsOn{ + // Object: &sdk.Object{ + // ObjectType: sdk.ObjectTypeSchema, + // Name: data.SchemaName, + // }, + // } + // case OnAllSchemasInDatabaseSchemaGrantKind: + // log.Printf("[INFO] Show with on_schema.all_schemas_in_database option is skipped. No changes in privileges in Snowflake will be detected.") + // return nil, "" + // case OnFutureSchemasInDatabaseSchemaGrantKind: + // opts.Future = sdk.Bool(true) + // opts.In = &sdk.ShowGrantsIn{ + // Database: data.DatabaseName, + // } + // } + //case OnSchemaObjectAccountRoleGrantKind: + // data := id.Data.(*OnSchemaObjectGrantData) + // + // switch data.Kind { + // case OnObjectSchemaObjectGrantKind: + // grantedOn = data.Object.ObjectType + // opts.On = &sdk.ShowGrantsOn{ + // Object: data.Object, + // } + // case OnAllSchemaObjectGrantKind: + // log.Printf("[INFO] Show with on_schema_object.on_all option is skipped. No changes in privileges in Snowflake will be detected.") + // return nil, "" + // case OnFutureSchemaObjectGrantKind: + // grantedOn = data.OnAllOrFuture.ObjectNamePlural.Singular() + // opts.Future = sdk.Bool(true) + // + // switch data.OnAllOrFuture.Kind { + // case InDatabaseBulkOperationGrantKind: + // opts.In = &sdk.ShowGrantsIn{ + // Database: data.OnAllOrFuture.Database, + // } + // case InSchemaBulkOperationGrantKind: + // opts.In = &sdk.ShowGrantsIn{ + // Schema: data.OnAllOrFuture.Schema, + // } + // } + // } + //} + + return opts, grantedOn +} + func flattenGrants(grants []snowflake.GrantDetail) []map[string]interface{} { grantDetails := make([]map[string]interface{}, len(grants)) for i, grant := range grants { From f46dd95992530b919bd38303970577fae596dff5 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 16:54:00 +0100 Subject: [PATCH 06/27] Migrate next part of the implementation to the SDK --- pkg/datasources/grants.go | 74 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 2b6c8f18af..5be5d1c6b9 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -320,11 +320,11 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d } if v, ok := d.GetOk("grants_of"); ok { - grantDetails, err = handleGrantsOf(ctx, client, v, db) + grants, err = handleGrantsOf(ctx, client, v) } if v, ok := d.GetOk("future_grants_in"); ok { - grantDetails, err = handleFutureGrantsIn(ctx, client, v, db) + grants, err = handleFutureGrantsIn(ctx, client, v) } if v, ok := d.GetOk("future_grants_to"); ok { @@ -376,14 +376,14 @@ func handleGrantsOn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant func handleGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { opts := new(sdk.ShowGrantOptions) - grantsTo := v.([]interface{})[0].(map[string]interface{}) + // TODO: add database role? if application := grantsTo["application"].(string); application != "" { // TODO: unsupported SHOW GRANTS TO APPLICATION } if applicationRole := grantsTo["application_role"].(string); applicationRole != "" { - // TODO: unsupported SHOW GRANTS TO APPLICATION + // TODO: unsupported SHOW GRANTS TO APPLICATION ROLE } if role := grantsTo["role"].(string); role != "" { opts.To = &sdk.ShowGrantsTo{ @@ -405,55 +405,53 @@ func handleGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant return client.Grants.Show(ctx, opts) } -func handleGrantsOf(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { - var grantDetails []snowflake.GrantDetail - var err error - +func handleGrantsOf(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { + opts := new(sdk.ShowGrantOptions) grantsOf := v.([]interface{})[0].(map[string]interface{}) - role := grantsOf["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "ROLE", role) - if err != nil { - return grantDetails, err + + // TODO: add database role? + if role := grantsOf["role"].(string); role != "" { + opts.Of = &sdk.ShowGrantsOf{ + Role: sdk.NewAccountObjectIdentifier(role), } } - share := grantsOf["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "SHARE", share) - if err != nil { - return grantDetails, err + if applicationRole := grantsOf["application_role"].(string); applicationRole != "" { + // TODO: unsupported SHOW GRANTS OF APPLICATION ROLE + } + if share := grantsOf["share"].(string); share != "" { + opts.Of = &sdk.ShowGrantsOf{ + Share: sdk.NewAccountObjectIdentifier(share), } } - return grantDetails, nil + return client.Grants.Show(ctx, opts) } -func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { - var grantDetails []snowflake.GrantDetail - var err error - +func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { + opts := new(sdk.ShowGrantOptions) + opts.Future = sdk.Bool(true) futureGrantsIn := v.([]interface{})[0].(map[string]interface{}) - database := futureGrantsIn["database"].(string) - if database != "" { - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "DATABASE", database) - if err != nil { - return grantDetails, err + + if db := futureGrantsIn["database"].(string); db != "" { + opts.In = &sdk.ShowGrantsIn{ + Database: sdk.Pointer(sdk.NewAccountObjectIdentifier(db)), } } - schema := futureGrantsIn["schema"].([]interface{}) - if len(schema) > 0 { - schemaMap := schema[0].(map[string]interface{}) + if sc := futureGrantsIn["schema"].([]interface{}); len(sc) > 0 { + schemaMap := sc[0].(map[string]interface{}) schemaName := schemaMap["schema_name"].(string) databaseName := schemaMap["database_name"].(string) - if databaseName != "" { - schemaName = databaseName + "." + schemaName + if databaseName == "" { + current, err := client.ContextFunctions.CurrentDatabase(ctx) + if err != nil { + return nil, err + } + databaseName = current } - - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "SCHEMA", schemaName) - if err != nil { - return grantDetails, err + opts.In = &sdk.ShowGrantsIn{ + Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)), } } - return grantDetails, nil + return client.Grants.Show(ctx, opts) } func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { From 8bedff4afd520f957ce5f58729b7b9ea03189624 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:19:32 +0100 Subject: [PATCH 07/27] Migrate last part of the implementation to the SDK --- pkg/datasources/grants.go | 118 ++++++++++---------------------------- 1 file changed, 29 insertions(+), 89 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 5be5d1c6b9..ef5ea1278b 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -2,12 +2,11 @@ package datasources import ( "context" - "database/sql" + "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -305,11 +304,8 @@ func Grants() *schema.Resource { func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - db := client.GetConn().DB - var grantDetails []snowflake.GrantDetail var grants []sdk.Grant - _ = grants var err error if v, ok := d.GetOk("grants_on"); ok { grants, err = handleGrantsOn(ctx, client, v) @@ -328,14 +324,14 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d } if v, ok := d.GetOk("future_grants_to"); ok { - grantDetails, err = handleFutureGrantsTo(ctx, client, v, db) + grants, err = handleFutureGrantsTo(ctx, client, v) } if err != nil { return diag.FromErr(err) } - err = d.Set("grants", flattenGrants(grantDetails)) + err = d.Set("grants", flattenGrants(grants)) if err != nil { return diag.FromErr(err) } @@ -454,100 +450,44 @@ func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any) ([]sdk return client.Grants.Show(ctx, opts) } -func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any, db *sql.DB) ([]snowflake.GrantDetail, error) { - var grantDetails []snowflake.GrantDetail - var err error - +func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { + opts := new(sdk.ShowGrantOptions) + opts.Future = sdk.Bool(true) futureGrantsTo := v.([]interface{})[0].(map[string]interface{}) - role := futureGrantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowFutureGrantsTo(db, "ROLE", role) + + if role := futureGrantsTo["role"].(string); role != "" { + opts.To = &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(role), + } + } + if databaseRole := futureGrantsTo["database_role"].(string); databaseRole != "" { + databaseRoleId, err := helpers.DecodeSnowflakeParameterID(databaseRole) if err != nil { - return grantDetails, err + return nil, err + } + validDatabaseRoleId, ok := databaseRoleId.(sdk.DatabaseObjectIdentifier) + if !ok { + return nil, fmt.Errorf("incorrect database role identifier (%s)", databaseRole) + } + opts.To = &sdk.ShowGrantsTo{ + DatabaseRole: validDatabaseRoleId, } } - return grantDetails, nil -} - -func aaa() (*sdk.ShowGrantOptions, sdk.ObjectType) { - opts := new(sdk.ShowGrantOptions) - var grantedOn sdk.ObjectType - - //switch id.Kind { - //case OnAccountObjectAccountRoleGrantKind: - // data := id.Data.(*OnAccountObjectGrantData) - // grantedOn = data.ObjectType - // opts.On = &sdk.ShowGrantsOn{ - // Object: &sdk.Object{ - // ObjectType: data.ObjectType, - // Name: data.ObjectName, - // }, - // } - //case OnSchemaAccountRoleGrantKind: - // grantedOn = sdk.ObjectTypeSchema - // data := id.Data.(*OnSchemaGrantData) - // - // switch data.Kind { - // case OnSchemaSchemaGrantKind: - // opts.On = &sdk.ShowGrantsOn{ - // Object: &sdk.Object{ - // ObjectType: sdk.ObjectTypeSchema, - // Name: data.SchemaName, - // }, - // } - // case OnAllSchemasInDatabaseSchemaGrantKind: - // log.Printf("[INFO] Show with on_schema.all_schemas_in_database option is skipped. No changes in privileges in Snowflake will be detected.") - // return nil, "" - // case OnFutureSchemasInDatabaseSchemaGrantKind: - // opts.Future = sdk.Bool(true) - // opts.In = &sdk.ShowGrantsIn{ - // Database: data.DatabaseName, - // } - // } - //case OnSchemaObjectAccountRoleGrantKind: - // data := id.Data.(*OnSchemaObjectGrantData) - // - // switch data.Kind { - // case OnObjectSchemaObjectGrantKind: - // grantedOn = data.Object.ObjectType - // opts.On = &sdk.ShowGrantsOn{ - // Object: data.Object, - // } - // case OnAllSchemaObjectGrantKind: - // log.Printf("[INFO] Show with on_schema_object.on_all option is skipped. No changes in privileges in Snowflake will be detected.") - // return nil, "" - // case OnFutureSchemaObjectGrantKind: - // grantedOn = data.OnAllOrFuture.ObjectNamePlural.Singular() - // opts.Future = sdk.Bool(true) - // - // switch data.OnAllOrFuture.Kind { - // case InDatabaseBulkOperationGrantKind: - // opts.In = &sdk.ShowGrantsIn{ - // Database: data.OnAllOrFuture.Database, - // } - // case InSchemaBulkOperationGrantKind: - // opts.In = &sdk.ShowGrantsIn{ - // Schema: data.OnAllOrFuture.Schema, - // } - // } - // } - //} - - return opts, grantedOn + return client.Grants.Show(ctx, opts) } -func flattenGrants(grants []snowflake.GrantDetail) []map[string]interface{} { +func flattenGrants(grants []sdk.Grant) []map[string]interface{} { grantDetails := make([]map[string]interface{}, len(grants)) for i, grant := range grants { grantDetails[i] = map[string]interface{}{ "created_on": grant.CreatedOn.String, - "privilege": grant.Privilege.String, + "privilege": grant.Privilege, "granted_on": grant.GrantedOn.String, - "name": grant.Name.String, + "name": grant.Name.FullyQualifiedName(), "granted_to": grant.GrantedTo.String, - "grantee_name": grant.GranteeName.String, - "grant_option": grant.GrantOption.String == "true", - "granted_by": grant.GrantedBy.String, + "grantee_name": grant.GranteeName.FullyQualifiedName(), + "grant_option": grant.GrantOption, + "granted_by": grant.GrantedBy.FullyQualifiedName(), } } return grantDetails From 66d6616cb3864e4e5a5f0bfc048dc33bcf88ade7 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:20:35 +0100 Subject: [PATCH 08/27] Remove querying grants from snowflake package --- pkg/snowflake/grant.go | 65 ------------------------------------------ 1 file changed, 65 deletions(-) diff --git a/pkg/snowflake/grant.go b/pkg/snowflake/grant.go index 2ee39bc330..92f4596d5d 100644 --- a/pkg/snowflake/grant.go +++ b/pkg/snowflake/grant.go @@ -1,13 +1,8 @@ package snowflake import ( - "database/sql" - "errors" "fmt" - "log" "strings" - - "github.com/jmoiron/sqlx" ) type grantType string @@ -408,63 +403,3 @@ func (ge *CurrentGrantExecutable) RevokeOwnership(r string) []string { func (ge *CurrentGrantExecutable) Show() string { return fmt.Sprintf(`SHOW GRANTS OF %v "%v"`, ge.granteeType, ge.granteeName) } - -type GrantDetail struct { - CreatedOn sql.NullString `db:"created_on"` - Privilege sql.NullString `db:"privilege"` - GrantedOn sql.NullString `db:"granted_on"` - Name sql.NullString `db:"name"` - GrantedTo sql.NullString `db:"granted_to"` - GranteeName sql.NullString `db:"grantee_name"` - GrantOption sql.NullString `db:"grant_option"` - GrantedBy sql.NullString `db:"granted_by"` -} - -func queryGrants(db *sql.DB, stmt string) ([]GrantDetail, error) { - rows, err := Query(db, stmt) - if err != nil { - return nil, err - } - defer rows.Close() - - grantDetails := []GrantDetail{} - err = sqlx.StructScan(rows, &grantDetails) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - log.Println("[DEBUG] no grants found") - return nil, fmt.Errorf("unable to scan rows for %s, err = %w", stmt, err) - } - return grantDetails, err - } - return grantDetails, nil -} - -func ShowGrantsOn(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS ON %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowGrantsOnAccount(db *sql.DB) ([]GrantDetail, error) { - stmt := `SHOW GRANTS ON ACCOUNT` - return queryGrants(db, stmt) -} - -func ShowGrantsTo(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS TO %v "%v"`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowGrantsOf(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS OF %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowFutureGrantsIn(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW FUTURE GRANTS IN %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowFutureGrantsTo(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW FUTURE GRANTS TO %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} From c3f7a1beae05826dcdd15102516fea2bdcd13218 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:34:29 +0100 Subject: [PATCH 09/27] Change method signatures --- pkg/datasources/grants.go | 59 ++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index ef5ea1278b..81504c898c 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -305,28 +305,28 @@ func Grants() *schema.Resource { func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*provider.Context).Client - var grants []sdk.Grant + var opts *sdk.ShowGrantOptions var err error - if v, ok := d.GetOk("grants_on"); ok { - grants, err = handleGrantsOn(ctx, client, v) + if grantsOn, ok := d.GetOk("grants_on"); ok { + opts, err = buildOptsForGrantsOn(grantsOn.([]interface{})[0].(map[string]interface{})) } - - if v, ok := d.GetOk("grants_to"); ok { - grants, err = handleGrantsTo(ctx, client, v) + if grantsTo, ok := d.GetOk("grants_to"); ok { + opts, err = buildOptsForGrantsTo(grantsTo.([]interface{})[0].(map[string]interface{})) } - - if v, ok := d.GetOk("grants_of"); ok { - grants, err = handleGrantsOf(ctx, client, v) + if grantsOf, ok := d.GetOk("grants_of"); ok { + opts, err = buildOptsForGrantsOf(grantsOf.([]interface{})[0].(map[string]interface{})) } - - if v, ok := d.GetOk("future_grants_in"); ok { - grants, err = handleFutureGrantsIn(ctx, client, v) + if futureGrantsIn, ok := d.GetOk("future_grants_in"); ok { + opts, err = buildOptsForFutureGrantsIn(ctx, client, futureGrantsIn.([]interface{})[0].(map[string]interface{})) } - - if v, ok := d.GetOk("future_grants_to"); ok { - grants, err = handleFutureGrantsTo(ctx, client, v) + if futureGrantsTo, ok := d.GetOk("future_grants_to"); ok { + opts, err = buildOptsForFutureGrantsTo(futureGrantsTo.([]interface{})[0].(map[string]interface{})) + } + if err != nil { + return diag.FromErr(err) } + grants, err := client.Grants.Show(ctx, opts) if err != nil { return diag.FromErr(err) } @@ -335,15 +335,14 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d if err != nil { return diag.FromErr(err) } + d.SetId("grants") return nil } -func handleGrantsOn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { - var err error +func buildOptsForGrantsOn(grantsOn map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - grantsOn := v.([]interface{})[0].(map[string]interface{}) objectType := grantsOn["object_type"].(string) objectName := grantsOn["object_name"].(string) account := grantsOn["account"].(bool) @@ -354,7 +353,7 @@ func handleGrantsOn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant } } else { if objectType == "" || objectName == "" { - return nil, err + return nil, fmt.Errorf("object_type (%s) or object_name (%s) missing", objectType, objectName) } objectId, err := helpers.DecodeSnowflakeParameterID(objectName) if err != nil { @@ -367,12 +366,11 @@ func handleGrantsOn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant }, } } - return client.Grants.Show(ctx, opts) + return opts, nil } -func handleGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { +func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - grantsTo := v.([]interface{})[0].(map[string]interface{}) // TODO: add database role? if application := grantsTo["application"].(string); application != "" { @@ -398,12 +396,11 @@ func handleGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant } // TODO: unsupported IN APPLICATION PACKAGE } - return client.Grants.Show(ctx, opts) + return opts, nil } -func handleGrantsOf(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { +func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - grantsOf := v.([]interface{})[0].(map[string]interface{}) // TODO: add database role? if role := grantsOf["role"].(string); role != "" { @@ -419,13 +416,12 @@ func handleGrantsOf(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant Share: sdk.NewAccountObjectIdentifier(share), } } - return client.Grants.Show(ctx, opts) + return opts, nil } -func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { +func buildOptsForFutureGrantsIn(ctx context.Context, client *sdk.Client, futureGrantsIn map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) opts.Future = sdk.Bool(true) - futureGrantsIn := v.([]interface{})[0].(map[string]interface{}) if db := futureGrantsIn["database"].(string); db != "" { opts.In = &sdk.ShowGrantsIn{ @@ -447,13 +443,12 @@ func handleFutureGrantsIn(ctx context.Context, client *sdk.Client, v any) ([]sdk Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)), } } - return client.Grants.Show(ctx, opts) + return opts, nil } -func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk.Grant, error) { +func buildOptsForFutureGrantsTo(futureGrantsTo map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) opts.Future = sdk.Bool(true) - futureGrantsTo := v.([]interface{})[0].(map[string]interface{}) if role := futureGrantsTo["role"].(string); role != "" { opts.To = &sdk.ShowGrantsTo{ @@ -473,7 +468,7 @@ func handleFutureGrantsTo(ctx context.Context, client *sdk.Client, v any) ([]sdk DatabaseRole: validDatabaseRoleId, } } - return client.Grants.Show(ctx, opts) + return opts, nil } func flattenGrants(grants []sdk.Grant) []map[string]interface{} { From ffcde16922008ce9b52da6003253ac9eb77dad38 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:36:32 +0100 Subject: [PATCH 10/27] Pass existing datasource test --- pkg/datasources/grants.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 81504c898c..2c446aaace 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -475,11 +475,11 @@ func flattenGrants(grants []sdk.Grant) []map[string]interface{} { grantDetails := make([]map[string]interface{}, len(grants)) for i, grant := range grants { grantDetails[i] = map[string]interface{}{ - "created_on": grant.CreatedOn.String, + "created_on": grant.CreatedOn.String(), "privilege": grant.Privilege, - "granted_on": grant.GrantedOn.String, + "granted_on": grant.GrantedOn.String(), "name": grant.Name.FullyQualifiedName(), - "granted_to": grant.GrantedTo.String, + "granted_to": grant.GrantedTo.String(), "grantee_name": grant.GranteeName.FullyQualifiedName(), "grant_option": grant.GrantOption, "granted_by": grant.GrantedBy.FullyQualifiedName(), From 93907ae70a7736bfd2fd61392a1bcb1fbb5a0ca2 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:46:09 +0100 Subject: [PATCH 11/27] Prepare list of tests --- pkg/datasources/grants_acceptance_test.go | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index b17479faab..d0eda5ddd3 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -9,6 +9,33 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) +// TODO: tests (examples from the correct ones): +// - on - account +// - on - db object +// - on - schema object +// - on - invalid config - no attribute +// - on - invalid config - missing object type or name +// - to - application +// - to - application role +// - to - role +// - to - user +// - to - share +// - to - share with application package +// - to - invalid config - no attribute +// - to - invalid config - share name missing +// - of - role +// - of - application role +// - of - share +// - of - invalid config - no attribute +// - future in - database +// - future in - schema (both db and sc present) +// - future in - schema (only sc present) +// - future in - invalid config - no attribute +// - future in - invalid config - schema with no schema name +// - future to - role +// - future to - database role +// - future to - invalid config - no attribute +// - future to - invalid config - database role id invalid func TestAcc_Grants(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, From 168d888c1422e4c25669409783a0a0028be20dbe Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 28 Mar 2024 17:53:43 +0100 Subject: [PATCH 12/27] Implement first test --- pkg/datasources/grants_acceptance_test.go | 20 +++++-------------- .../On_Account/snowflake_grants_on_account.tf | 5 +++++ 2 files changed, 10 insertions(+), 15 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index d0eda5ddd3..815525d7a1 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -10,7 +10,7 @@ import ( ) // TODO: tests (examples from the correct ones): -// - on - account +// + on - account // - on - db object // - on - schema object // - on - invalid config - no attribute @@ -36,31 +36,21 @@ import ( // - future to - database role // - future to - invalid config - no attribute // - future to - invalid config - database role id invalid -func TestAcc_Grants(t *testing.T) { +func TestAcc_Grants_On_Account(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: grantsAccount(), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Account"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_grants.g", "grants.#"), + resource.TestCheckResourceAttrSet("data.snowflake_grants.test", "grants.#"), ), }, }, }) } - -func grantsAccount() string { - s := ` -data "snowflake_grants" "g" { - grants_on { - account = true - } -} -` - return s -} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf b/pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf new file mode 100644 index 0000000000..4c66d5a46e --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_on { + account = true + } +} From f56f8c85ccd31f2585cc02fceeefa29811b6141b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 11:47:38 +0100 Subject: [PATCH 13/27] Add tests for grants on --- pkg/datasources/grants.go | 9 +- pkg/datasources/grants_acceptance_test.go | 138 +++++++++++++++++- .../snowflake_grants_on_account_object.tf | 6 + .../On_AccountObject/variables.tf | 3 + .../snowflake_grants_on_database_object.tf | 6 + .../On_DatabaseObject/variables.tf | 7 + ...e_grants_on_invalid_missing_object_type.tf | 5 + ...nowflake_grants_on_invalid_no_attribute.tf | 4 + .../snowflake_grants_on_schema_object.tf | 17 +++ .../On_SchemaObject/variables.tf | 11 ++ 10 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 2c446aaace..ab1bd4bddd 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -28,11 +28,10 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Name of object to list privileges on.", }, "object_type": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"grants_on.0.object_name"}, - Description: "Type of object to list privileges on.", - ConflictsWith: []string{"grants_on.0.account"}, + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"grants_on.0.object_name"}, + Description: "Type of object to list privileges on.", }, "account": { Type: schema.TypeBool, diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 815525d7a1..37cdbce8be 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -1,20 +1,40 @@ package datasources_test import ( + "regexp" + "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) +func checkAtLeastOneGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_by"), + ) +} + // TODO: tests (examples from the correct ones): // + on - account -// - on - db object -// - on - schema object -// - on - invalid config - no attribute -// - on - invalid config - missing object type or name +// + on - account object +// + on - db object +// + on - schema object +// + on - invalid config - no attribute +// + on - invalid config - missing object type or name // - to - application // - to - application role // - to - role @@ -47,9 +67,113 @@ func TestAcc_Grants_On_Account(t *testing.T) { Steps: []resource.TestStep{ { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Account"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_grants.test", "grants.#"), - ), + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_AccountObject(t *testing.T) { + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_AccountObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_DatabaseObject(t *testing.T) { + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + "schema": config.StringVariable(acc.TestSchemaName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_DatabaseObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_SchemaObject(t *testing.T) { + tableName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + "schema": config.StringVariable(acc.TestSchemaName), + "table": config.StringVariable(tableName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_SchemaObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Invalid_NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_On_Invalid_MissingObjectType(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Invalid_MissingObjectType"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Missing required argument"), }, }, }) diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf new file mode 100644 index 0000000000..cd93692366 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf @@ -0,0 +1,6 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = var.database + object_type = "DATABASE" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf new file mode 100644 index 0000000000..ffb7081367 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf @@ -0,0 +1,6 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = "\"${var.database}\".\"${var.schema}\"" + object_type = "SCHEMA" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf new file mode 100644 index 0000000000..626dbab534 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf b/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf new file mode 100644 index 0000000000..52db3ea3fa --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = "" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf new file mode 100644 index 0000000000..4ec09a6115 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_on { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf new file mode 100644 index 0000000000..cd40a0d18c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf @@ -0,0 +1,17 @@ +resource "snowflake_table" "test" { + database = var.database + schema = var.schema + name = var.table + + column { + name = "id" + type = "NUMBER(38,0)" + } +} + +data "snowflake_grants" "test" { + grants_on { + object_name = "\"${snowflake_table.test.database}\".\"${snowflake_table.test.schema}\".\"${snowflake_table.test.name}\"" + object_type = "TABLE" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf new file mode 100644 index 0000000000..92e06a8848 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf @@ -0,0 +1,11 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} + +variable "table" { + type = string +} From 9f3124ba5bf134a05f4195346b7de43faf33818f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 11:51:12 +0100 Subject: [PATCH 14/27] Reorganize test config directories for snowflake_grants --- pkg/datasources/grants_acceptance_test.go | 12 ++++++------ .../Account}/snowflake_grants_on_account.tf | 0 .../snowflake_grants_on_account_object.tf | 0 .../AccountObject}/variables.tf | 0 .../snowflake_grants_on_database_object.tf | 0 .../DatabaseObject}/variables.tf | 0 ...nowflake_grants_on_invalid_missing_object_type.tf | 0 .../snowflake_grants_on_invalid_no_attribute.tf | 0 .../snowflake_grants_on_schema_object.tf | 0 .../SchemaObject}/variables.tf | 0 10 files changed, 6 insertions(+), 6 deletions(-) rename pkg/datasources/testdata/TestAcc_Grants/{On_Account => On/Account}/snowflake_grants_on_account.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_AccountObject => On/AccountObject}/snowflake_grants_on_account_object.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_AccountObject => On/AccountObject}/variables.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_DatabaseObject => On/DatabaseObject}/snowflake_grants_on_database_object.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_DatabaseObject => On/DatabaseObject}/variables.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_Invalid_MissingObjectType => On/Invalid/MissingObjectType}/snowflake_grants_on_invalid_missing_object_type.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_Invalid_NoAttribute => On/Invalid/NoAttribute}/snowflake_grants_on_invalid_no_attribute.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_SchemaObject => On/SchemaObject}/snowflake_grants_on_schema_object.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/{On_SchemaObject => On/SchemaObject}/variables.tf (100%) diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 37cdbce8be..c1911be1a6 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -66,7 +66,7 @@ func TestAcc_Grants_On_Account(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Account"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Account"), Check: checkAtLeastOneGrantPresent(), }, }, @@ -87,7 +87,7 @@ func TestAcc_Grants_On_AccountObject(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_AccountObject"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/AccountObject"), ConfigVariables: configVariables, Check: checkAtLeastOneGrantPresent(), }, @@ -110,7 +110,7 @@ func TestAcc_Grants_On_DatabaseObject(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_DatabaseObject"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/DatabaseObject"), ConfigVariables: configVariables, Check: checkAtLeastOneGrantPresent(), }, @@ -135,7 +135,7 @@ func TestAcc_Grants_On_SchemaObject(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_SchemaObject"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/SchemaObject"), ConfigVariables: configVariables, Check: checkAtLeastOneGrantPresent(), }, @@ -153,7 +153,7 @@ func TestAcc_Grants_On_Invalid_NoAttribute(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Invalid_NoAttribute"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Invalid/NoAttribute"), PlanOnly: true, ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), }, @@ -171,7 +171,7 @@ func TestAcc_Grants_On_Invalid_MissingObjectType(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On_Invalid_MissingObjectType"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Invalid/MissingObjectType"), PlanOnly: true, ExpectError: regexp.MustCompile("Error: Missing required argument"), }, diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Account/snowflake_grants_on_account.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_Account/snowflake_grants_on_account.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/Account/snowflake_grants_on_account.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/snowflake_grants_on_account_object.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/snowflake_grants_on_account_object.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/snowflake_grants_on_account_object.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/variables.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_AccountObject/variables.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/variables.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/snowflake_grants_on_database_object.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/snowflake_grants_on_database_object.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/snowflake_grants_on_database_object.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/variables.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_DatabaseObject/variables.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/variables.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_Invalid_MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/NoAttribute/snowflake_grants_on_invalid_no_attribute.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_Invalid_NoAttribute/snowflake_grants_on_invalid_no_attribute.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/Invalid/NoAttribute/snowflake_grants_on_invalid_no_attribute.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/snowflake_grants_on_schema_object.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/snowflake_grants_on_schema_object.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/snowflake_grants_on_schema_object.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/variables.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/On_SchemaObject/variables.tf rename to pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/variables.tf From 2ba594fe5be5d4cfd9665f445d2c8abbf6727a25 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 12:24:44 +0100 Subject: [PATCH 15/27] Add test for grants to (only supported ones for now) --- pkg/datasources/grants.go | 2 +- pkg/datasources/grants_acceptance_test.go | 163 +++++++++++++++++- ...nowflake_grants_to_invalid_no_attribute.tf | 4 + ...ke_grants_to_invalid_share_name_missing.tf | 5 + .../To/Role/snowflake_grants_to_role.tf | 7 + .../To/Share/snowflake_grants_to_share.tf | 25 +++ .../TestAcc_Grants/To/Share/variables.tf | 7 + ...rants_to_share_with_application_package.tf | 25 +++ .../ShareWithApplicationPackage/variables.tf | 7 + .../To/User/snowflake_grants_to_user.tf | 5 + .../TestAcc_Grants/To/User/variables.tf | 3 + 11 files changed, 245 insertions(+), 8 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index ab1bd4bddd..4c2d4d6cfb 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -388,7 +388,7 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption User: sdk.NewAccountObjectIdentifier(user), } } - if share := grantsTo["share"]; share != nil { + if share := grantsTo["share"]; share != nil && len(share.([]interface{})) > 0 { shareMap := share.([]interface{})[0].(map[string]interface{}) opts.To = &sdk.ShowGrantsTo{ Share: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index c1911be1a6..41d5b3656b 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -1,12 +1,14 @@ package datasources_test import ( + "context" "regexp" "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -24,10 +26,30 @@ func checkAtLeastOneGrantPresent() resource.TestCheckFunc { resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_by"), ) } +func checkAtLeastOneGrantPresentWithoutValidations() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + ) +} + +func getCurrentUser(t *testing.T) string { + t.Helper() + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + user, err := client.ContextFunctions.CurrentUser(context.Background()) + if err != nil { + t.Fatal(err) + } + return user +} + // TODO: tests (examples from the correct ones): // + on - account // + on - account object @@ -37,12 +59,12 @@ func checkAtLeastOneGrantPresent() resource.TestCheckFunc { // + on - invalid config - missing object type or name // - to - application // - to - application role -// - to - role -// - to - user -// - to - share -// - to - share with application package -// - to - invalid config - no attribute -// - to - invalid config - share name missing +// + to - role +// + to - user +// + to - share +// +/- to - share with application package +// + to - invalid config - no attribute +// + to - invalid config - share name missing // - of - role // - of - application role // - of - share @@ -178,3 +200,130 @@ func TestAcc_Grants_On_Invalid_MissingObjectType(t *testing.T) { }, }) } + +func TestAcc_Grants_To_Role(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Role"), + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_To_User(t *testing.T) { + user := getCurrentUser(t) + configVariables := config.Variables{ + "user": config.StringVariable(user), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/User"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresentWithoutValidations(), + }, + }, + }) +} + +func TestAcc_Grants_To_Share(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "share": config.StringVariable(shareName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Share"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_To_ShareWithApplicationPackage(t *testing.T) { + t.Skip("No SDK support yet") + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "share": config.StringVariable(shareName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/ShareWithApplicationPackage"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_To_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_To_Invalid_ShareNameMissing(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/ShareNameMissing"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf new file mode 100644 index 0000000000..5762ea22f0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_to { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf new file mode 100644 index 0000000000..b535ec09de --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + share {} + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf new file mode 100644 index 0000000000..99cce594e1 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf @@ -0,0 +1,7 @@ +data "snowflake_current_role" "test" {} + +data "snowflake_grants" "test" { + grants_to { + role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf new file mode 100644 index 0000000000..5fa9e34424 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf @@ -0,0 +1,25 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_share" "test" { + depends_on = [snowflake_database.test] + + name = var.share +} + +resource "snowflake_grant_privileges_to_share" "test" { + to_share = snowflake_share.test.name + privileges = ["USAGE"] + on_database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_share.test] + + grants_to { + share { + share_name = snowflake_share.test.name + } + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf new file mode 100644 index 0000000000..5de127b632 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "share" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf new file mode 100644 index 0000000000..5fa9e34424 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf @@ -0,0 +1,25 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_share" "test" { + depends_on = [snowflake_database.test] + + name = var.share +} + +resource "snowflake_grant_privileges_to_share" "test" { + to_share = snowflake_share.test.name + privileges = ["USAGE"] + on_database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_share.test] + + grants_to { + share { + share_name = snowflake_share.test.name + } + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf new file mode 100644 index 0000000000..5de127b632 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "share" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf b/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf new file mode 100644 index 0000000000..fa2cffc3ac --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + user = var.user + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf new file mode 100644 index 0000000000..be0022a44a --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf @@ -0,0 +1,3 @@ +variable "user" { + type = string +} From 694a193b8aec51c16fa0fa6ed5fe21f793a2c062 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 12:59:22 +0100 Subject: [PATCH 16/27] Add test for grants of (only supported ones for now) --- pkg/datasources/grants_acceptance_test.go | 111 +++++++++++++++++- ...nowflake_grants_of_invalid_no_attribute.tf | 4 + .../Of/Role/snowflake_grants_of_role.tf | 7 ++ .../Of/Share/snowflake_grants_of_share.tf | 24 ++++ .../TestAcc_Grants/Of/Share/variables.tf | 11 ++ 5 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 41d5b3656b..23f7b8e574 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -8,11 +8,13 @@ import ( acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/stretchr/testify/require" ) func checkAtLeastOneGrantPresent() resource.TestCheckFunc { @@ -37,6 +39,14 @@ func checkAtLeastOneGrantPresentWithoutValidations() resource.TestCheckFunc { ) } +func checkNoGrantsPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckNoResourceAttr(datasourceName, "grants.0.created_on"), + ) +} + func getCurrentUser(t *testing.T) string { t.Helper() client, err := sdk.NewDefaultClient() @@ -50,6 +60,36 @@ func getCurrentUser(t *testing.T) string { return user } +func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { + t.Helper() + + t.Helper() + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + cfg, err := sdk.ProfileConfig(testprofiles.Secondary) + if err != nil { + t.Fatal(err) + } + secondaryClient, err := sdk.NewClient(cfg) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + replicationAccounts, err := client.ReplicationFunctions.ShowReplicationAccounts(ctx) + if err != nil { + t.Fatal(err) + } + for _, replicationAccount := range replicationAccounts { + if replicationAccount.AccountLocator == secondaryClient.GetAccountLocator() { + return sdk.Pointer(sdk.NewAccountIdentifier(replicationAccount.OrganizationName, replicationAccount.AccountName)) + } + } + return nil +} + // TODO: tests (examples from the correct ones): // + on - account // + on - account object @@ -65,10 +105,10 @@ func getCurrentUser(t *testing.T) string { // +/- to - share with application package // + to - invalid config - no attribute // + to - invalid config - share name missing -// - of - role +// + of - role // - of - application role -// - of - share -// - of - invalid config - no attribute +// + of - share +// + of - invalid config - no attribute // - future in - database // - future in - schema (both db and sc present) // - future in - schema (only sc present) @@ -327,3 +367,68 @@ func TestAcc_Grants_To_Invalid_ShareNameMissing(t *testing.T) { }, }) } + +func TestAcc_Grants_Of_Role(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Role"), + Check: checkAtLeastOneGrantPresentWithoutValidations(), + }, + }, + }) +} + +func TestAcc_Grants_Of_Share(t *testing.T) { + t.Skip("TestAcc_Share are skipped") + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + accountId := getSecondaryAccountIdentifier(t) + require.NotNil(t, accountId) + + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "share": config.StringVariable(shareName), + "account": config.StringVariable(accountId.FullyQualifiedName()), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Share"), + ConfigVariables: configVariables, + Check: checkNoGrantsPresent(), + }, + }, + }) +} + +func TestAcc_Grants_Of_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf new file mode 100644 index 0000000000..2b0e08d3e8 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_of { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf new file mode 100644 index 0000000000..09fa52cc21 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf @@ -0,0 +1,7 @@ +data "snowflake_current_role" "test" {} + +data "snowflake_grants" "test" { + grants_of { + role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf new file mode 100644 index 0000000000..001bb832d5 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf @@ -0,0 +1,24 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_share" "test" { + depends_on = [snowflake_database.test] + + name = var.share + accounts = [var.account] +} + +resource "snowflake_grant_privileges_to_share" "test" { + to_share = snowflake_share.test.name + privileges = ["USAGE"] + on_database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_share.test] + + grants_of { + share = snowflake_share.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf new file mode 100644 index 0000000000..f620845508 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf @@ -0,0 +1,11 @@ +variable "database" { + type = string +} + +variable "share" { + type = string +} + +variable "account" { + type = string +} From 664ec9ec28b8e47e049061290ff8b315aa2b3ab4 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 14:28:25 +0100 Subject: [PATCH 17/27] Add test for future grants in --- pkg/datasources/grants.go | 39 ++----- pkg/datasources/grants_acceptance_test.go | 105 +++++++++++++++++- .../snowflake_grants_future_in_database.tf | 22 ++++ .../FutureIn/Database/variables.tf | 3 + ...e_grants_future_in_invalid_no_attribute.tf | 4 + ...invalid_schema_name_not_fully_qualified.tf | 5 + .../snowflake_grants_future_in_schema.tf | 30 +++++ .../FutureIn/Schema/variables.tf | 7 ++ 8 files changed, 179 insertions(+), 36 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 4c2d4d6cfb..c502784169 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -6,6 +6,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -188,28 +189,14 @@ var grantsSchema = map[string]*schema.Schema{ }, }, "schema": { - Type: schema.TypeList, - MaxItems: 1, + Type: schema.TypeString, Optional: true, - Description: "Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role.", + Description: "Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. Schema must be a fully qualified name (\"<db_name>\".\"<schema_name>\").", ExactlyOneOf: []string{ "future_grants_in.0.database", "future_grants_in.0.schema", }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "schema_name": { - Type: schema.TypeString, - Required: true, - Description: "The name of the schema to list all privileges of new (ie. future) objects granted to.", - }, - "database_name": { - Type: schema.TypeString, - Optional: true, - Description: "The database in which the scehma resides. Optional when querying a schema in the current database.", - }, - }, - }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, }, }, @@ -316,7 +303,7 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d opts, err = buildOptsForGrantsOf(grantsOf.([]interface{})[0].(map[string]interface{})) } if futureGrantsIn, ok := d.GetOk("future_grants_in"); ok { - opts, err = buildOptsForFutureGrantsIn(ctx, client, futureGrantsIn.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForFutureGrantsIn(futureGrantsIn.([]interface{})[0].(map[string]interface{})) } if futureGrantsTo, ok := d.GetOk("future_grants_to"); ok { opts, err = buildOptsForFutureGrantsTo(futureGrantsTo.([]interface{})[0].(map[string]interface{})) @@ -418,7 +405,7 @@ func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOption return opts, nil } -func buildOptsForFutureGrantsIn(ctx context.Context, client *sdk.Client, futureGrantsIn map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForFutureGrantsIn(futureGrantsIn map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) opts.Future = sdk.Bool(true) @@ -427,19 +414,9 @@ func buildOptsForFutureGrantsIn(ctx context.Context, client *sdk.Client, futureG Database: sdk.Pointer(sdk.NewAccountObjectIdentifier(db)), } } - if sc := futureGrantsIn["schema"].([]interface{}); len(sc) > 0 { - schemaMap := sc[0].(map[string]interface{}) - schemaName := schemaMap["schema_name"].(string) - databaseName := schemaMap["database_name"].(string) - if databaseName == "" { - current, err := client.ContextFunctions.CurrentDatabase(ctx) - if err != nil { - return nil, err - } - databaseName = current - } + if sc := futureGrantsIn["schema"].(string); sc != "" { opts.In = &sdk.ShowGrantsIn{ - Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)), + Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(sc)), } } return opts, nil diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 23f7b8e574..9ae4d3ca9a 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -31,6 +31,18 @@ func checkAtLeastOneGrantPresent() resource.TestCheckFunc { ) } +func checkAtLeastOneFutureGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + ) +} + func checkAtLeastOneGrantPresentWithoutValidations() resource.TestCheckFunc { datasourceName := "data.snowflake_grants.test" return resource.ComposeTestCheckFunc( @@ -109,11 +121,10 @@ func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { // - of - application role // + of - share // + of - invalid config - no attribute -// - future in - database -// - future in - schema (both db and sc present) -// - future in - schema (only sc present) -// - future in - invalid config - no attribute -// - future in - invalid config - schema with no schema name +// + future in - database +// + future in - schema (both db and sc present) +// + future in - invalid config - no attribute +// + future in - invalid config - schema name not fully qualified // - future to - role // - future to - database role // - future to - invalid config - no attribute @@ -432,3 +443,87 @@ func TestAcc_Grants_Of_Invalid_NoAttribute(t *testing.T) { }, }) } + +func TestAcc_Grants_FutureIn_Database(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Database"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Schema(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "schema": config.StringVariable(schemaName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Schema"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Invalid_SchemaNameNotFullyQualified(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf new file mode 100644 index 0000000000..5a7b657f0e --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf @@ -0,0 +1,22 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_in { + database = snowflake_database.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf new file mode 100644 index 0000000000..d85dbce68d --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + future_grants_in { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf new file mode 100644 index 0000000000..9204f6cd66 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + future_grants_in { + schema = "schema" + } +} \ No newline at end of file diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf new file mode 100644 index 0000000000..5da1376697 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf @@ -0,0 +1,30 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_schema" "test" { + name = var.schema + database = snowflake_database.test.name +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["INSERT"] + + on_schema_object { + future { + object_type_plural = "TABLES" + in_schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\"" + } + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_in { + schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf new file mode 100644 index 0000000000..626dbab534 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} From 55b6ee5d3d8eeaa0768cae7e0503a8a515bf44aa Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 14:46:17 +0100 Subject: [PATCH 18/27] Add test for future grants to --- pkg/datasources/grants.go | 11 +-- pkg/datasources/grants_acceptance_test.go | 92 ++++++++++++++++++- ...nowflake_grants_future_to_database_role.tf | 25 +++++ .../FutureTo/DatabaseRole/variables.tf | 7 ++ ...ure_to_invalid_database_role_id_invalid.tf | 5 + ...e_grants_future_to_invalid_no_attribute.tf | 3 + .../Role/snowflake_grants_future_to_role.tf | 22 +++++ .../TestAcc_Grants/FutureTo/Role/variables.tf | 3 + 8 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index c502784169..b6103adc95 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -226,6 +226,7 @@ var grantsSchema = map[string]*schema.Schema{ "future_grants_to.0.role", "future_grants_to.0.database_role", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, }, }, @@ -432,16 +433,8 @@ func buildOptsForFutureGrantsTo(futureGrantsTo map[string]interface{}) (*sdk.Sho } } if databaseRole := futureGrantsTo["database_role"].(string); databaseRole != "" { - databaseRoleId, err := helpers.DecodeSnowflakeParameterID(databaseRole) - if err != nil { - return nil, err - } - validDatabaseRoleId, ok := databaseRoleId.(sdk.DatabaseObjectIdentifier) - if !ok { - return nil, fmt.Errorf("incorrect database role identifier (%s)", databaseRole) - } opts.To = &sdk.ShowGrantsTo{ - DatabaseRole: validDatabaseRoleId, + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), } } return opts, nil diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 9ae4d3ca9a..b130e92969 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -125,10 +125,10 @@ func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { // + future in - schema (both db and sc present) // + future in - invalid config - no attribute // + future in - invalid config - schema name not fully qualified -// - future to - role -// - future to - database role -// - future to - invalid config - no attribute -// - future to - invalid config - database role id invalid +// + future to - role +// + future to - database role +// + future to - invalid config - no attribute +// + future to - invalid config - database role id invalid func TestAcc_Grants_On_Account(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -527,3 +527,87 @@ func TestAcc_Grants_FutureIn_Invalid_SchemaNameNotFullyQualified(t *testing.T) { }, }) } + +func TestAcc_Grants_FutureTo_Role(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Role"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf new file mode 100644 index 0000000000..e194007f22 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf @@ -0,0 +1,25 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +resource "snowflake_grant_privileges_to_database_role" "test" { + database_role_name = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_database_role.test] + + future_grants_to { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..49f3275ce2 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + future_grants_to { + database_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf new file mode 100644 index 0000000000..780fc9fd08 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf @@ -0,0 +1,3 @@ +data "snowflake_grants" "test" { + future_grants_to {} +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf new file mode 100644 index 0000000000..148cc2359b --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf @@ -0,0 +1,22 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_to { + role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} From 78859d826c95476a03942b81467b4c0f9342edf3 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 15:19:15 +0100 Subject: [PATCH 19/27] Add database role to show grants of and to --- pkg/datasources/grants.go | 46 ++++++++- pkg/datasources/grants_acceptance_test.go | 97 ++++++++++++++++++- .../snowflake_grants_of_database_role.tf | 23 +++++ .../Of/DatabaseRole/variables.tf | 7 ++ ...nts_of_invalid_database_role_id_invalid.tf | 5 + .../snowflake_grants_to_database_role.tf | 14 +++ .../To/DatabaseRole/variables.tf | 7 ++ ...nts_to_invalid_database_role_id_invalid.tf | 5 + 8 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index b6103adc95..65711b0ce0 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -60,6 +60,7 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.application", "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, @@ -72,6 +73,7 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.application", "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, @@ -84,10 +86,25 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.application", "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, }, + "database_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all privileges and roles granted to the database role.", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.role", + "grants_to.0.database_role", + "grants_to.0.user", + "grants_to.0.share", + }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), + }, "user": { Type: schema.TypeString, Optional: true, @@ -96,6 +113,7 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.application", "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, @@ -109,6 +127,7 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.application", "grants_to.0.application_role", "grants_to.0.role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, @@ -144,9 +163,22 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all users and roles to which the role has been granted.", ExactlyOneOf: []string{ "grants_of.0.role", + "grants_of.0.database_role", + "grants_of.0.application_role", + "grants_of.0.share", + }, + }, + "database_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all users and roles to which the database role has been granted.", + ExactlyOneOf: []string{ + "grants_of.0.role", + "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, "application_role": { Type: schema.TypeString, @@ -154,6 +186,7 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all the users and roles to which the application role has been granted. Note: if fully qualified application role identifier is not specified, i.e. only the application role name is given, Snowflake uses the current application. If the application is not a database, this command does not return results. Consult with the proper section in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants).", ExactlyOneOf: []string{ "grants_of.0.role", + "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", }, @@ -164,6 +197,7 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all the accounts for the share and indicates the accounts that are using the share.", ExactlyOneOf: []string{ "grants_of.0.role", + "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", }, @@ -359,7 +393,6 @@ func buildOptsForGrantsOn(grantsOn map[string]interface{}) (*sdk.ShowGrantOption func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - // TODO: add database role? if application := grantsTo["application"].(string); application != "" { // TODO: unsupported SHOW GRANTS TO APPLICATION } @@ -371,6 +404,11 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption Role: sdk.NewAccountObjectIdentifier(role), } } + if databaseRole := grantsTo["database_role"].(string); databaseRole != "" { + opts.To = &sdk.ShowGrantsTo{ + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), + } + } if user := grantsTo["user"].(string); user != "" { opts.To = &sdk.ShowGrantsTo{ User: sdk.NewAccountObjectIdentifier(user), @@ -389,12 +427,16 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - // TODO: add database role? if role := grantsOf["role"].(string); role != "" { opts.Of = &sdk.ShowGrantsOf{ Role: sdk.NewAccountObjectIdentifier(role), } } + if databaseRole := grantsOf["database_role"].(string); databaseRole != "" { + opts.Of = &sdk.ShowGrantsOf{ + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), + } + } if applicationRole := grantsOf["application_role"].(string); applicationRole != "" { // TODO: unsupported SHOW GRANTS OF APPLICATION ROLE } diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index b130e92969..513ddb7012 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -43,11 +43,12 @@ func checkAtLeastOneFutureGrantPresent() resource.TestCheckFunc { ) } -func checkAtLeastOneGrantPresentWithoutValidations() resource.TestCheckFunc { +func checkAtLeastOneGrantPresentLimited() resource.TestCheckFunc { datasourceName := "data.snowflake_grants.test" return resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), ) } @@ -112,15 +113,19 @@ func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { // - to - application // - to - application role // + to - role +// + to - database role // + to - user // + to - share // +/- to - share with application package // + to - invalid config - no attribute // + to - invalid config - share name missing +// + to - invalid config - database role id invalid // + of - role +// + of - database role // - of - application role // + of - share // + of - invalid config - no attribute +// + of - invalid config - database role id invalid // + future in - database // + future in - schema (both db and sc present) // + future in - invalid config - no attribute @@ -269,6 +274,31 @@ func TestAcc_Grants_To_Role(t *testing.T) { }) } +func TestAcc_Grants_To_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + func TestAcc_Grants_To_User(t *testing.T) { user := getCurrentUser(t) configVariables := config.Variables{ @@ -286,7 +316,7 @@ func TestAcc_Grants_To_User(t *testing.T) { { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/User"), ConfigVariables: configVariables, - Check: checkAtLeastOneGrantPresentWithoutValidations(), + Check: checkAtLeastOneGrantPresentLimited(), }, }, }) @@ -379,6 +409,24 @@ func TestAcc_Grants_To_Invalid_ShareNameMissing(t *testing.T) { }) } +func TestAcc_Grants_To_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + func TestAcc_Grants_Of_Role(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -390,7 +438,32 @@ func TestAcc_Grants_Of_Role(t *testing.T) { Steps: []resource.TestStep{ { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Role"), - Check: checkAtLeastOneGrantPresentWithoutValidations(), + Check: checkAtLeastOneGrantPresentLimited(), + }, + }, + }) +} + +func TestAcc_Grants_Of_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresentLimited(), }, }, }) @@ -444,6 +517,24 @@ func TestAcc_Grants_Of_Invalid_NoAttribute(t *testing.T) { }) } +func TestAcc_Grants_Of_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + func TestAcc_Grants_FutureIn_Database(t *testing.T) { databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) configVariables := config.Variables{ diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf new file mode 100644 index 0000000000..2495cee7f9 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf @@ -0,0 +1,23 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +resource "snowflake_grant_database_role" "test" { + database_role_name = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + parent_role_name = data.snowflake_current_role.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_database_role.test] + + grants_of { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..46b6c8caa7 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_of { + database_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf new file mode 100644 index 0000000000..87183c9a9b --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf @@ -0,0 +1,14 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + grants_to { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..c9365923ca --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + database_role = "role" + } +} From b10ca3510f103d63de734bc09f51f90f553789ce Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 15:46:57 +0100 Subject: [PATCH 20/27] Add show grants to and of application role to the SDK --- pkg/datasources/grants.go | 20 ++++++---- pkg/datasources/grants_acceptance_test.go | 38 +++++++++++++++++++ ..._of_invalid_application_role_id_invalid.tf | 5 +++ ..._to_invalid_application_role_id_invalid.tf | 5 +++ pkg/sdk/grants.go | 16 ++++---- .../application_roles_gen_integration_test.go | 37 ++++++++++++++++++ 6 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf create mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 65711b0ce0..963332ef10 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -68,7 +68,7 @@ var grantsSchema = map[string]*schema.Schema{ "application_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all the privileges and roles granted to the application role. Note: if fully qualified application role identifier is not specified, i.e. only the application role name is given, Snowflake uses the current application. If the application is not a database, this command does not return results. Consult with the proper section in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants).", + Description: "Lists all the privileges and roles granted to the application role. Must be a fully qualified name (\"<app_name>\".\"<app_role_name>\").", ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", @@ -77,6 +77,7 @@ var grantsSchema = map[string]*schema.Schema{ "grants_to.0.user", "grants_to.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, "role": { Type: schema.TypeString, @@ -94,7 +95,7 @@ var grantsSchema = map[string]*schema.Schema{ "database_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all privileges and roles granted to the database role.", + Description: "Lists all privileges and roles granted to the database role. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", @@ -171,7 +172,7 @@ var grantsSchema = map[string]*schema.Schema{ "database_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all users and roles to which the database role has been granted.", + Description: "Lists all users and roles to which the database role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ "grants_of.0.role", "grants_of.0.database_role", @@ -183,13 +184,14 @@ var grantsSchema = map[string]*schema.Schema{ "application_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all the users and roles to which the application role has been granted. Note: if fully qualified application role identifier is not specified, i.e. only the application role name is given, Snowflake uses the current application. If the application is not a database, this command does not return results. Consult with the proper section in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants).", + Description: "Lists all the users and roles to which the application role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ "grants_of.0.role", "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, "share": { Type: schema.TypeString, @@ -255,7 +257,7 @@ var grantsSchema = map[string]*schema.Schema{ "database_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all privileges on new (i.e. future) objects granted to the database role.", + Description: "Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ "future_grants_to.0.role", "future_grants_to.0.database_role", @@ -397,7 +399,9 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption // TODO: unsupported SHOW GRANTS TO APPLICATION } if applicationRole := grantsTo["application_role"].(string); applicationRole != "" { - // TODO: unsupported SHOW GRANTS TO APPLICATION ROLE + opts.To = &sdk.ShowGrantsTo{ + ApplicationRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(applicationRole), + } } if role := grantsTo["role"].(string); role != "" { opts.To = &sdk.ShowGrantsTo{ @@ -438,7 +442,9 @@ func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOption } } if applicationRole := grantsOf["application_role"].(string); applicationRole != "" { - // TODO: unsupported SHOW GRANTS OF APPLICATION ROLE + opts.Of = &sdk.ShowGrantsOf{ + ApplicationRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(applicationRole), + } } if share := grantsOf["share"].(string); share != "" { opts.Of = &sdk.ShowGrantsOf{ diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 513ddb7012..730687e56d 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -120,12 +120,14 @@ func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { // + to - invalid config - no attribute // + to - invalid config - share name missing // + to - invalid config - database role id invalid +// + to - invalid config - application role id invalid // + of - role // + of - database role // - of - application role // + of - share // + of - invalid config - no attribute // + of - invalid config - database role id invalid +// + of - invalid config - application role id invalid // + future in - database // + future in - schema (both db and sc present) // + future in - invalid config - no attribute @@ -427,6 +429,24 @@ func TestAcc_Grants_To_Invalid_DatabaseRoleIdInvalid(t *testing.T) { }) } +func TestAcc_Grants_To_Invalid_ApplicationRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + func TestAcc_Grants_Of_Role(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -535,6 +555,24 @@ func TestAcc_Grants_Of_Invalid_DatabaseRoleIdInvalid(t *testing.T) { }) } +func TestAcc_Grants_Of_Invalid_ApplicationRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + func TestAcc_Grants_FutureIn_Database(t *testing.T) { databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) configVariables := config.Variables{ diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf new file mode 100644 index 0000000000..0c25ac7b2a --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_of { + application_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf new file mode 100644 index 0000000000..d0815f4b1c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + application_role = "role" + } +} diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go index 32134a84fe..d736ee51fa 100644 --- a/pkg/sdk/grants.go +++ b/pkg/sdk/grants.go @@ -176,16 +176,18 @@ type ShowGrantsOn struct { } type ShowGrantsTo struct { - Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` - User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` - Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` - DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` + ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` + Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` + User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` + Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` } type ShowGrantsOf struct { - Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` - DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` - Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` + Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` + DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` + Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` } type grantRow struct { diff --git a/pkg/sdk/testint/application_roles_gen_integration_test.go b/pkg/sdk/testint/application_roles_gen_integration_test.go index dd712a5e85..979a8fd87e 100644 --- a/pkg/sdk/testint/application_roles_gen_integration_test.go +++ b/pkg/sdk/testint/application_roles_gen_integration_test.go @@ -79,4 +79,41 @@ func TestInt_ApplicationRoles(t *testing.T) { assertApplicationRoles(t, appRoles, "app_role_1", "some comment") assertApplicationRoles(t, appRoles, "app_role_2", "some comment2") }) + + t.Run("show grants to", func(t *testing.T) { + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + opts := new(sdk.ShowGrantOptions) + opts.To = &sdk.ShowGrantsTo{ + ApplicationRole: id, + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectPrivilegeUsage.String(), grants[0].Privilege) + require.Equal(t, sdk.ObjectTypeDatabase, grants[0].GrantedOn) + require.Equal(t, sdk.ObjectTypeApplicationRole, grants[0].GrantedTo) + }) + + t.Run("show grants of", func(t *testing.T) { + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + opts := new(sdk.ShowGrantOptions) + opts.Of = &sdk.ShowGrantsOf{ + ApplicationRole: id, + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectTypeRole, grants[0].GrantedTo) + require.Equal(t, sdk.NewAccountObjectIdentifier(appName), grants[0].GrantedBy) + }) } From 9953cee2c3e5639cd20b13964f8e3163993d517a Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 16:12:59 +0100 Subject: [PATCH 21/27] Add show grants to application to the SDK --- pkg/datasources/grants.go | 4 +- pkg/sdk/grants.go | 1 + .../application_roles_gen_integration_test.go | 46 +++++++++++++++++-- pkg/sdk/testint/testdata/manifest2.yml | 5 ++ 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 pkg/sdk/testint/testdata/manifest2.yml diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 963332ef10..bc64c4e2f9 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -396,7 +396,9 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption opts := new(sdk.ShowGrantOptions) if application := grantsTo["application"].(string); application != "" { - // TODO: unsupported SHOW GRANTS TO APPLICATION + opts.To = &sdk.ShowGrantsTo{ + Application: sdk.NewAccountObjectIdentifier(application), + } } if applicationRole := grantsTo["application_role"].(string); applicationRole != "" { opts.To = &sdk.ShowGrantsTo{ diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go index d736ee51fa..c3fad615f4 100644 --- a/pkg/sdk/grants.go +++ b/pkg/sdk/grants.go @@ -176,6 +176,7 @@ type ShowGrantsOn struct { } type ShowGrantsTo struct { + Application AccountObjectIdentifier `ddl:"identifier" sql:"APPLICATION"` ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` diff --git a/pkg/sdk/testint/application_roles_gen_integration_test.go b/pkg/sdk/testint/application_roles_gen_integration_test.go index 979a8fd87e..393a3178c0 100644 --- a/pkg/sdk/testint/application_roles_gen_integration_test.go +++ b/pkg/sdk/testint/application_roles_gen_integration_test.go @@ -2,10 +2,12 @@ package testint import ( "context" + "fmt" "testing" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,7 +23,7 @@ import ( func TestInt_ApplicationRoles(t *testing.T) { client := testClient(t) - stageName := "stage_name" + stageName := random.AlphaN(8) stage, cleanupStage := createStage(t, client, sdk.NewSchemaObjectIdentifier(TestDatabaseName, TestSchemaName, stageName)) t.Cleanup(cleanupStage) @@ -80,7 +82,7 @@ func TestInt_ApplicationRoles(t *testing.T) { assertApplicationRoles(t, appRoles, "app_role_2", "some comment2") }) - t.Run("show grants to", func(t *testing.T) { + t.Run("show grants to application role", func(t *testing.T) { name := "app_role_1" id := sdk.NewDatabaseObjectIdentifier(appName, name) ctx := context.Background() @@ -99,7 +101,7 @@ func TestInt_ApplicationRoles(t *testing.T) { require.Equal(t, sdk.ObjectTypeApplicationRole, grants[0].GrantedTo) }) - t.Run("show grants of", func(t *testing.T) { + t.Run("show grants of application role", func(t *testing.T) { name := "app_role_1" id := sdk.NewDatabaseObjectIdentifier(appName, name) ctx := context.Background() @@ -116,4 +118,42 @@ func TestInt_ApplicationRoles(t *testing.T) { require.Equal(t, sdk.ObjectTypeRole, grants[0].GrantedTo) require.Equal(t, sdk.NewAccountObjectIdentifier(appName), grants[0].GrantedBy) }) + + t.Run("show grants to application", func(t *testing.T) { + // Need second app to be able to grant application role to it. Cannot grant to parent application (098806 (0A000): Cannot grant an APPLICATION ROLE to the parent APPLICATION). + stageName2 := random.AlphaN(8) + stage2, cleanupStage2 := createStage(t, client, sdk.NewSchemaObjectIdentifier(TestDatabaseName, TestSchemaName, stageName2)) + t.Cleanup(cleanupStage2) + + putOnStage(t, client, stage2, "manifest2.yml") + putOnStage(t, client, stage2, "setup.sql") + + appName2 := "snowflake_app_2" + cleanupApp2 := createApplication(t, client, appName2, appPackageName, versionName) + t.Cleanup(cleanupApp2) + + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + _, err := client.ExecForTests(ctx, fmt.Sprintf(`GRANT APPLICATION ROLE %s TO APPLICATION %s`, id.FullyQualifiedName(), sdk.NewAccountObjectIdentifier(appName2).FullyQualifiedName())) + require.NoError(t, err) + defer func() { + _, err := client.ExecForTests(ctx, fmt.Sprintf(`REVOKE APPLICATION ROLE %s FROM APPLICATION %s`, id.FullyQualifiedName(), sdk.NewAccountObjectIdentifier(appName2).FullyQualifiedName())) + require.NoError(t, err) + }() + + opts := new(sdk.ShowGrantOptions) + opts.To = &sdk.ShowGrantsTo{ + Application: sdk.NewAccountObjectIdentifier(appName2), + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectPrivilegeUsage.String(), grants[0].Privilege) + require.Equal(t, sdk.ObjectTypeApplicationRole, grants[0].GrantedOn) + require.Equal(t, sdk.ObjectTypeApplication, grants[0].GrantedTo) + }) } diff --git a/pkg/sdk/testint/testdata/manifest2.yml b/pkg/sdk/testint/testdata/manifest2.yml new file mode 100644 index 0000000000..b951fb531b --- /dev/null +++ b/pkg/sdk/testint/testdata/manifest2.yml @@ -0,0 +1,5 @@ +manifest_version: 1 # required +version: + name: application_roles_test_app_2 + label: "v1.0" + comment: "This application is used by Snowflake Terraform Provider for application role integration tests" From f5b5ef37a789a296041d384a1c62fc74d7bde19e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 16:22:43 +0100 Subject: [PATCH 22/27] Add placeholder tests for application and application role --- pkg/datasources/grants_acceptance_test.go | 46 ++++++++--------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 730687e56d..4073b094f6 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -104,38 +104,7 @@ func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { } // TODO: tests (examples from the correct ones): -// + on - account -// + on - account object -// + on - db object -// + on - schema object -// + on - invalid config - no attribute -// + on - invalid config - missing object type or name -// - to - application -// - to - application role -// + to - role -// + to - database role -// + to - user -// + to - share // +/- to - share with application package -// + to - invalid config - no attribute -// + to - invalid config - share name missing -// + to - invalid config - database role id invalid -// + to - invalid config - application role id invalid -// + of - role -// + of - database role -// - of - application role -// + of - share -// + of - invalid config - no attribute -// + of - invalid config - database role id invalid -// + of - invalid config - application role id invalid -// + future in - database -// + future in - schema (both db and sc present) -// + future in - invalid config - no attribute -// + future in - invalid config - schema name not fully qualified -// + future to - role -// + future to - database role -// + future to - invalid config - no attribute -// + future to - invalid config - database role id invalid func TestAcc_Grants_On_Account(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -259,6 +228,16 @@ func TestAcc_Grants_On_Invalid_MissingObjectType(t *testing.T) { }) } +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_To_Application(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_To_ApplicationRole(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + func TestAcc_Grants_To_Role(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -489,6 +468,11 @@ func TestAcc_Grants_Of_DatabaseRole(t *testing.T) { }) } +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_Of_ApplicationRole(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + func TestAcc_Grants_Of_Share(t *testing.T) { t.Skip("TestAcc_Share are skipped") databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) From b0f92ad6087c6641232514d5b0ec5b8e0e6eb724 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 16:32:31 +0100 Subject: [PATCH 23/27] Add TODO for share tests --- pkg/datasources/grants_acceptance_test.go | 171 +++++++++++----------- pkg/resources/share_acceptance_test.go | 1 + 2 files changed, 85 insertions(+), 87 deletions(-) diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 4073b094f6..6a0c1f5065 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -17,92 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -func checkAtLeastOneGrantPresent() resource.TestCheckFunc { - datasourceName := "data.snowflake_grants.test" - return resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_on"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), - ) -} - -func checkAtLeastOneFutureGrantPresent() resource.TestCheckFunc { - datasourceName := "data.snowflake_grants.test" - return resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), - ) -} - -func checkAtLeastOneGrantPresentLimited() resource.TestCheckFunc { - datasourceName := "data.snowflake_grants.test" - return resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), - resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), - ) -} - -func checkNoGrantsPresent() resource.TestCheckFunc { - datasourceName := "data.snowflake_grants.test" - return resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), - resource.TestCheckNoResourceAttr(datasourceName, "grants.0.created_on"), - ) -} - -func getCurrentUser(t *testing.T) string { - t.Helper() - client, err := sdk.NewDefaultClient() - if err != nil { - t.Fatal(err) - } - user, err := client.ContextFunctions.CurrentUser(context.Background()) - if err != nil { - t.Fatal(err) - } - return user -} - -func getSecondaryAccountIdentifier(t *testing.T) *sdk.AccountIdentifier { - t.Helper() - - t.Helper() - client, err := sdk.NewDefaultClient() - if err != nil { - t.Fatal(err) - } - cfg, err := sdk.ProfileConfig(testprofiles.Secondary) - if err != nil { - t.Fatal(err) - } - secondaryClient, err := sdk.NewClient(cfg) - if err != nil { - t.Fatal(err) - } - ctx := context.Background() - - replicationAccounts, err := client.ReplicationFunctions.ShowReplicationAccounts(ctx) - if err != nil { - t.Fatal(err) - } - for _, replicationAccount := range replicationAccounts { - if replicationAccount.AccountLocator == secondaryClient.GetAccountLocator() { - return sdk.Pointer(sdk.NewAccountIdentifier(replicationAccount.OrganizationName, replicationAccount.AccountName)) - } - } - return nil -} - // TODO: tests (examples from the correct ones): // +/- to - share with application package func TestAcc_Grants_On_Account(t *testing.T) { @@ -473,10 +387,41 @@ func TestAcc_Grants_Of_ApplicationRole(t *testing.T) { t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") } +// TODO [SNOW-1284394]: Unskip the test func TestAcc_Grants_Of_Share(t *testing.T) { t.Skip("TestAcc_Share are skipped") databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + getSecondaryAccountIdentifier := func(t *testing.T) *sdk.AccountIdentifier { + t.Helper() + + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + cfg, err := sdk.ProfileConfig(testprofiles.Secondary) + if err != nil { + t.Fatal(err) + } + secondaryClient, err := sdk.NewClient(cfg) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + replicationAccounts, err := client.ReplicationFunctions.ShowReplicationAccounts(ctx) + if err != nil { + t.Fatal(err) + } + for _, replicationAccount := range replicationAccounts { + if replicationAccount.AccountLocator == secondaryClient.GetAccountLocator() { + return sdk.Pointer(sdk.NewAccountIdentifier(replicationAccount.OrganizationName, replicationAccount.AccountName)) + } + } + return nil + } + accountId := getSecondaryAccountIdentifier(t) require.NotNil(t, accountId) @@ -485,6 +430,7 @@ func TestAcc_Grants_Of_Share(t *testing.T) { "share": config.StringVariable(shareName), "account": config.StringVariable(accountId.FullyQualifiedName()), } + datasourceName := "data.snowflake_grants.test" resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -497,7 +443,10 @@ func TestAcc_Grants_Of_Share(t *testing.T) { { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Share"), ConfigVariables: configVariables, - Check: checkNoGrantsPresent(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckNoResourceAttr(datasourceName, "grants.0.created_on"), + ), }, }, }) @@ -724,3 +673,51 @@ func TestAcc_Grants_FutureTo_Invalid_DatabaseRoleIdInvalid(t *testing.T) { }, }) } + +func checkAtLeastOneGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + ) +} + +func checkAtLeastOneFutureGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + ) +} + +func checkAtLeastOneGrantPresentLimited() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), + ) +} + +func getCurrentUser(t *testing.T) string { + t.Helper() + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + user, err := client.ContextFunctions.CurrentUser(context.Background()) + if err != nil { + t.Fatal(err) + } + return user +} diff --git a/pkg/resources/share_acceptance_test.go b/pkg/resources/share_acceptance_test.go index eac0f0be6d..486ad449d7 100644 --- a/pkg/resources/share_acceptance_test.go +++ b/pkg/resources/share_acceptance_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) +// TODO [SNOW-1284394]: Unskip the test func TestAcc_Share(t *testing.T) { t.Skip("second and third account must be set for Share acceptance tests") var account2 string From 4a718b18d46dfb3132a275af2cc1aef1c7b73e60 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 17:13:43 +0100 Subject: [PATCH 24/27] Comment out in application package for shares --- pkg/datasources/grants.go | 20 +++++++++----- pkg/datasources/grants_acceptance_test.go | 27 ++----------------- ...rants_to_share_with_application_package.tf | 25 ----------------- .../ShareWithApplicationPackage/variables.tf | 7 ----- ...ant_privileges_to_share_acceptance_test.go | 4 ++- pkg/sdk/grants.go | 7 ++++- pkg/sdk/grants_test.go | 18 ++++++++++++- pkg/sdk/testint/grants_integration_test.go | 25 +++++++++++++++++ 8 files changed, 66 insertions(+), 67 deletions(-) delete mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf delete mode 100644 pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index bc64c4e2f9..a0a927f323 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -139,11 +139,12 @@ var grantsSchema = map[string]*schema.Schema{ Required: true, Description: "Lists all of the privileges and roles granted to the specified share.", }, - "in_application_package": { - Type: schema.TypeString, - Optional: true, - Description: "Lists all of the privileges and roles granted to a share in the specified application package.", - }, + // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. + //"in_application_package": { + // Type: schema.TypeString, + // Optional: true, + // Description: "Lists all of the privileges and roles granted to a share in the specified application package.", + //}, }, }, }, @@ -423,9 +424,14 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption if share := grantsTo["share"]; share != nil && len(share.([]interface{})) > 0 { shareMap := share.([]interface{})[0].(map[string]interface{}) opts.To = &sdk.ShowGrantsTo{ - Share: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), + Share: &sdk.ShowGrantsToShare{ + Name: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), + }, } - // TODO: unsupported IN APPLICATION PACKAGE + // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. + //if inApplicationPackage := shareMap["in_application_package"]; inApplicationPackage != "" { + // opts.To.Share.InApplicationPackage = sdk.Pointer(sdk.NewAccountObjectIdentifier(inApplicationPackage.(string))) + //} } return opts, nil } diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 6a0c1f5065..03409784c0 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -17,8 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -// TODO: tests (examples from the correct ones): -// +/- to - share with application package func TestAcc_Grants_On_Account(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -242,30 +240,9 @@ func TestAcc_Grants_To_Share(t *testing.T) { }) } +// TODO [SNOW-1284382]: Implement after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. func TestAcc_Grants_To_ShareWithApplicationPackage(t *testing.T) { - t.Skip("No SDK support yet") - databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - configVariables := config.Variables{ - "database": config.StringVariable(databaseName), - "share": config.StringVariable(shareName), - } - - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - CheckDestroy: nil, - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/ShareWithApplicationPackage"), - ConfigVariables: configVariables, - Check: checkAtLeastOneGrantPresent(), - }, - }, - }) + t.Skip("Skipped until SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working.") } func TestAcc_Grants_To_Invalid_NoAttribute(t *testing.T) { diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf deleted file mode 100644 index 5fa9e34424..0000000000 --- a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/snowflake_grants_to_share_with_application_package.tf +++ /dev/null @@ -1,25 +0,0 @@ -resource "snowflake_database" "test" { - name = var.database -} - -resource "snowflake_share" "test" { - depends_on = [snowflake_database.test] - - name = var.share -} - -resource "snowflake_grant_privileges_to_share" "test" { - to_share = snowflake_share.test.name - privileges = ["USAGE"] - on_database = snowflake_database.test.name -} - -data "snowflake_grants" "test" { - depends_on = [snowflake_grant_privileges_to_share.test] - - grants_to { - share { - share_name = snowflake_share.test.name - } - } -} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf deleted file mode 100644 index 5de127b632..0000000000 --- a/pkg/datasources/testdata/TestAcc_Grants/To/ShareWithApplicationPackage/variables.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "database" { - type = string -} - -variable "share" { - type = string -} diff --git a/pkg/resources/grant_privileges_to_share_acceptance_test.go b/pkg/resources/grant_privileges_to_share_acceptance_test.go index c4a1a3b87c..15d92a5767 100644 --- a/pkg/resources/grant_privileges_to_share_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_share_acceptance_test.go @@ -533,7 +533,9 @@ func testAccCheckSharePrivilegesRevoked() func(*terraform.State) error { id := sdk.NewExternalObjectIdentifierFromFullyQualifiedName(rs.Primary.Attributes["to_share"]) grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ To: &sdk.ShowGrantsTo{ - Share: sdk.NewAccountObjectIdentifier(id.Name()), + Share: &sdk.ShowGrantsToShare{ + Name: sdk.NewAccountObjectIdentifier(id.Name()), + }, }, }) if err != nil { diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go index c3fad615f4..906d6c7d8a 100644 --- a/pkg/sdk/grants.go +++ b/pkg/sdk/grants.go @@ -180,10 +180,15 @@ type ShowGrantsTo struct { ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` - Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + Share *ShowGrantsToShare `ddl:"-"` DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` } +type ShowGrantsToShare struct { + Name AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + InApplicationPackage *AccountObjectIdentifier `ddl:"identifier" sql:"IN APPLICATION PACKAGE"` +} + type ShowGrantsOf struct { ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` diff --git a/pkg/sdk/grants_test.go b/pkg/sdk/grants_test.go index 69ccf6b44c..ad20aca09e 100644 --- a/pkg/sdk/grants_test.go +++ b/pkg/sdk/grants_test.go @@ -1041,12 +1041,28 @@ func TestGrantShow(t *testing.T) { shareID := RandomAccountObjectIdentifier() opts := &ShowGrantOptions{ To: &ShowGrantsTo{ - Share: shareID, + Share: &ShowGrantsToShare{ + Name: shareID, + }, }, } assertOptsValidAndSQLEquals(t, opts, "SHOW GRANTS TO SHARE %s", shareID.FullyQualifiedName()) }) + t.Run("to share in application package", func(t *testing.T) { + shareID := RandomAccountObjectIdentifier() + packageId := RandomAccountObjectIdentifier() + opts := &ShowGrantOptions{ + To: &ShowGrantsTo{ + Share: &ShowGrantsToShare{ + Name: shareID, + InApplicationPackage: &packageId, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "SHOW GRANTS TO SHARE %s IN APPLICATION PACKAGE %s", shareID.FullyQualifiedName(), packageId.FullyQualifiedName()) + }) + t.Run("of role", func(t *testing.T) { roleID := RandomAccountObjectIdentifier() opts := &ShowGrantOptions{ diff --git a/pkg/sdk/testint/grants_integration_test.go b/pkg/sdk/testint/grants_integration_test.go index 1b1a908778..6a7c63956c 100644 --- a/pkg/sdk/testint/grants_integration_test.go +++ b/pkg/sdk/testint/grants_integration_test.go @@ -756,6 +756,31 @@ func TestInt_GrantPrivilegeToShare(t *testing.T) { require.NoError(t, err) assertGrant(t, grants, table.ID(), sdk.ObjectPrivilegeSelect) + grants, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Share: &sdk.ShowGrantsToShare{ + Name: shareTest.ID(), + }, + }, + }) + require.NoError(t, err) + + appPackageName := random.AlphaN(8) + cleanupAppPackage := createApplicationPackage(t, client, appPackageName) + t.Cleanup(cleanupAppPackage) + // TODO [SNOW-1284382]: alter the test when the syntax starts working + //2024/03/29 17:04:20 [DEBUG] sql-conn-query: [query SHOW GRANTS TO SHARE "0a8DMkl3NOx7" IN APPLICATION PACKAGE "hziiAtqY" err 001003 (42000): SQL compilation error: + //syntax error line 1 at position 39 unexpected 'APPLICATION'. duration 445.248042ms args {}] (IYA62698) + grants, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Share: &sdk.ShowGrantsToShare{ + Name: shareTest.ID(), + InApplicationPackage: sdk.Pointer(sdk.NewAccountObjectIdentifier(appPackageName)), + }, + }, + }) + require.Error(t, err) + err = client.Grants.RevokePrivilegeFromShare(ctx, []sdk.ObjectPrivilege{sdk.ObjectPrivilegeSelect}, &sdk.ShareGrantOn{ Table: &sdk.OnTable{ AllInSchema: testSchema(t).ID(), From edb2d93a7e801fb577a29758a1eacbfa05f84ef0 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 17:49:03 +0100 Subject: [PATCH 25/27] Add examples and migration guide --- MIGRATION_GUIDE.md | 68 ++++++ docs/data-sources/grants.md | 194 +++++++++++++----- .../snowflake_grants/data-source.tf | 144 ++++++++++--- pkg/datasources/grants.go | 8 +- pkg/sdk/testint/grants_integration_test.go | 8 +- 5 files changed, 344 insertions(+), 78 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 3313ccfacb..41bc1e5221 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,6 +9,74 @@ across different versions. #### *(behavior change)* Execute as validation added From now on, the `snowflake_procedure`'s `execute_as` parameter allows only two values: OWNER and CALLER (case-insensitive). Setting other values earlier resulted in falling back to the Snowflake default (currently OWNER) and creating a permadiff. +### snowflake_grants datasource changes +`snowflake_grants` datasource was refreshed as part of the ongoing [Grants Redesign](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#redesigning-grants). + +#### *(behavior change)* grants_to.share type change +`grants_to.share` was a text field. Because Snowflake introduced new syntax `SHOW GRANTS TO SHARE IN APPLICATION PACKAGE ` (check more in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants)) the type was changed to object. To migrate simply change: +```terraform +data "snowflake_grants" "example_to_share" { + grants_to { + share = "some_share" + } +} +``` +to +```terraform +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} +``` +Note: `in_application_package` is not yet supported. + +#### *(behavior change)* future_grants_in.schema type change +`future_grants_in.schema` was an object field allowing to set required `schema_name` and optional `database_name`. Our strategy is to be explicit, so the schema field was changed to string and fully qualified name is expected. To migrate change: +```terraform +data "snowflake_grants" "example_future_in_schema" { + future_grants_in { + schema { + database_name = "some_database" + schema_name = "some_schema" + } + } +} +``` +to +```terraform +data "snowflake_grants" "example_future_in_schema" { + future_grants_in { + schema = "\"some_database\".\"some_schema\"" + } +} +``` +#### *(new feature)* grants_to new options +`grants_to` was enriched with three new options: +- `application` +- `application_role` +- `database_role` + +No migration work is needed here. + +#### *(new feature)* grants_of new options +`grants_to` was enriched with two new options: +- `database_role` +- `application_role` + +No migration work is needed here. + +#### *(new feature)* future_grants_to new options +`future_grants_to` was enriched with one new option: +- `database_role` + +No migration work is needed here. + +#### *(documentation)* improvements +Descriptions of attributes were altered. More examples were added (both for old nd new features). + ## v0.86.0 ➞ v0.87.0 ### snowflake_database resource changes #### *(behavior change)* External object identifier changes diff --git a/docs/data-sources/grants.md b/docs/data-sources/grants.md index 6a330d80f8..5dab679c61 100644 --- a/docs/data-sources/grants.md +++ b/docs/data-sources/grants.md @@ -12,56 +12,154 @@ description: |- ## Example Usage ```terraform -# list all grants on account -data "snowflake_grants" "grants" { +################################## +### SHOW GRANTS ON ... +################################## + +# account +data "snowflake_grants" "example_on_account" { grants_on { account = true } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants2" { +# account object (e.g. database) +data "snowflake_grants" "example_on_account_object" { grants_on { - object_name = "\"tst\"" + object_name = "some_database" object_type = "DATABASE" } } -# list all grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants3" { +# database object (e.g. schema) +data "snowflake_grants" "example_on_database_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\"" + object_type = "SCHEMA" + } +} + +# schema object (e.g. table) +data "snowflake_grants" "example_on_schema_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\".\"some_table\"" + object_type = "TABLE" + } +} + +################################## +### SHOW GRANTS TO ... +################################## + +# application +data "snowflake_grants" "example_to_application" { + grants_to { + application = "some_application" + } +} + +# application role +data "snowflake_grants" "example_to_application_role" { grants_to { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" + } +} + +# role +data "snowflake_grants" "example_to_role" { + grants_to { + role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_to_database_role" { + grants_to { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# share +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} + +# user +data "snowflake_grants" "example_to_user" { + grants_to { + user = "some_user" + } +} + +################################## +### SHOW GRANTS OF ... +################################## + +# application role +data "snowflake_grants" "example_of_application_role" { + grants_of { + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants of role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants4" { +# database role +data "snowflake_grants" "example_of_database_role" { grants_of { - role = "ACCOUNTADMIN" + database_role = "\"some_database\".\"some_database_role\"" } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants5" { +# role +data "snowflake_grants" "example_of_role" { + grants_of { + role = "some_role" + } +} + +# share +data "snowflake_grants" "example_of_share" { + grants_of { + share = "some_share" + } +} + +################################## +### SHOW FUTURE GRANTS IN ... +################################## + +# database +data "snowflake_grants" "example_future_in_database" { future_grants_in { - database = "\"tst\"" + database = "some_database" } } -# list all future grants in schema with name "mydatabase" and database with name "myschema" -data "snowflake_grants" "grants6" { +# schema +data "snowflake_grants" "example_future_in_schema" { future_grants_in { - schema { - database_name = "\"mydatabase\"" - schema_name = "\"myschema\"" - } + schema = "\"some_database\".\"some_schema\"" } } -# list all future grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants7" { +################################## +### SHOW FUTURE GRANTS TO ... +################################## + +# role +data "snowflake_grants" "example_future_to_role" { future_grants_to { - role = "ACCOUNTADMIN" + role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_future_to_database_role" { + future_grants_to { + database_role = "\"some_database\".\"some_database_role\"" } } ``` @@ -71,11 +169,11 @@ data "snowflake_grants" "grants7" { ### Optional -- `future_grants_in` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects (see [below for nested schema](#nestedblock--future_grants_in)) -- `future_grants_to` (Block List, Max: 1) Lists all privileges granted to the object on new (i.e. future) objects (see [below for nested schema](#nestedblock--future_grants_to)) -- `grants_of` (Block List, Max: 1) Lists all objects to which the given object has been granted (see [below for nested schema](#nestedblock--grants_of)) -- `grants_on` (Block List, Max: 1) Lists all privileges that have been granted on an object or account (see [below for nested schema](#nestedblock--grants_on)) -- `grants_to` (Block List, Max: 1) Lists all privileges granted to the object (see [below for nested schema](#nestedblock--grants_to)) +- `future_grants_in` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects. (see [below for nested schema](#nestedblock--future_grants_in)) +- `future_grants_to` (Block List, Max: 1) Lists all privileges granted to the object on new (i.e. future) objects. (see [below for nested schema](#nestedblock--future_grants_to)) +- `grants_of` (Block List, Max: 1) Lists all objects to which the given object has been granted. (see [below for nested schema](#nestedblock--grants_of)) +- `grants_on` (Block List, Max: 1) Lists all privileges that have been granted on an object or on an account. (see [below for nested schema](#nestedblock--grants_on)) +- `grants_to` (Block List, Max: 1) Lists all privileges granted to the object. (see [below for nested schema](#nestedblock--grants_to)) ### Read-Only @@ -88,26 +186,15 @@ data "snowflake_grants" "grants7" { Optional: - `database` (String) Lists all privileges on new (i.e. future) objects of a specified type in the database granted to a role. -- `schema` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. (see [below for nested schema](#nestedblock--future_grants_in--schema)) - - -### Nested Schema for `future_grants_in.schema` - -Required: - -- `schema_name` (String) The name of the schema to list all privileges of new (ie. future) objects granted to - -Optional: - -- `database_name` (String) The database in which the scehma resides. Optional when querying a schema in the current database. - +- `schema` (String) Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. Schema must be a fully qualified name ("<db_name>"."<schema_name>"). ### Nested Schema for `future_grants_to` -Required: +Optional: +- `database_role` (String) Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). - `role` (String) Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role. @@ -116,7 +203,9 @@ Required: Optional: -- `role` (String) Lists all users and roles to which the role has been granted +- `application_role` (String) Lists all the users and roles to which the application role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). +- `database_role` (String) Lists all users and roles to which the database role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). +- `role` (String) Lists all users and roles to which the role has been granted. - `share` (String) Lists all the accounts for the share and indicates the accounts that are using the share. @@ -126,7 +215,7 @@ Optional: Optional: - `account` (Boolean) Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles. -- `object_name` (String) Name of object to list privileges on +- `object_name` (String) Name of object to list privileges on. - `object_type` (String) Type of object to list privileges on. @@ -135,9 +224,20 @@ Optional: Optional: -- `role` (String) Lists all privileges and roles granted to the role -- `share` (String) Lists all the privileges granted to the share -- `user` (String) Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed +- `application` (String) Lists all the privileges and roles granted to the application. +- `application_role` (String) Lists all the privileges and roles granted to the application role. Must be a fully qualified name ("<app_name>"."<app_role_name>"). +- `database_role` (String) Lists all privileges and roles granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). +- `role` (String) Lists all privileges and roles granted to the role. +- `share` (Block List, Max: 1) Lists all the privileges granted to the share. (see [below for nested schema](#nestedblock--grants_to--share)) +- `user` (String) Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed. + + +### Nested Schema for `grants_to.share` + +Required: + +- `share_name` (String) Lists all of the privileges and roles granted to the specified share. + diff --git a/examples/data-sources/snowflake_grants/data-source.tf b/examples/data-sources/snowflake_grants/data-source.tf index 1682092584..6fe9c8c438 100644 --- a/examples/data-sources/snowflake_grants/data-source.tf +++ b/examples/data-sources/snowflake_grants/data-source.tf @@ -1,52 +1,150 @@ -# list all grants on account -data "snowflake_grants" "grants" { +################################## +### SHOW GRANTS ON ... +################################## + +# account +data "snowflake_grants" "example_on_account" { grants_on { account = true } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants2" { +# account object (e.g. database) +data "snowflake_grants" "example_on_account_object" { grants_on { - object_name = "\"tst\"" + object_name = "some_database" object_type = "DATABASE" } } -# list all grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants3" { +# database object (e.g. schema) +data "snowflake_grants" "example_on_database_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\"" + object_type = "SCHEMA" + } +} + +# schema object (e.g. table) +data "snowflake_grants" "example_on_schema_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\".\"some_table\"" + object_type = "TABLE" + } +} + +################################## +### SHOW GRANTS TO ... +################################## + +# application +data "snowflake_grants" "example_to_application" { + grants_to { + application = "some_application" + } +} + +# application role +data "snowflake_grants" "example_to_application_role" { grants_to { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants of role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants4" { +# role +data "snowflake_grants" "example_to_role" { + grants_to { + role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_to_database_role" { + grants_to { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# share +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} + +# user +data "snowflake_grants" "example_to_user" { + grants_to { + user = "some_user" + } +} + +################################## +### SHOW GRANTS OF ... +################################## + +# application role +data "snowflake_grants" "example_of_application_role" { grants_of { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants5" { +# database role +data "snowflake_grants" "example_of_database_role" { + grants_of { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# role +data "snowflake_grants" "example_of_role" { + grants_of { + role = "some_role" + } +} + +# share +data "snowflake_grants" "example_of_share" { + grants_of { + share = "some_share" + } +} + +################################## +### SHOW FUTURE GRANTS IN ... +################################## + +# database +data "snowflake_grants" "example_future_in_database" { future_grants_in { - database = "\"tst\"" + database = "some_database" } } -# list all future grants in schema with name "mydatabase" and database with name "myschema" -data "snowflake_grants" "grants6" { +# schema +data "snowflake_grants" "example_future_in_schema" { future_grants_in { - schema { - database_name = "\"mydatabase\"" - schema_name = "\"myschema\"" - } + schema = "\"some_database\".\"some_schema\"" + } +} + +################################## +### SHOW FUTURE GRANTS TO ... +################################## + +# role +data "snowflake_grants" "example_future_to_role" { + future_grants_to { + role = "some_role" } } -# list all future grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants7" { +# database role +data "snowflake_grants" "example_future_to_database_role" { future_grants_to { - role = "ACCOUNTADMIN" + database_role = "\"some_database\".\"some_database_role\"" } } diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index a0a927f323..7de61e3556 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -140,11 +140,11 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all of the privileges and roles granted to the specified share.", }, // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. - //"in_application_package": { + // "in_application_package": { // Type: schema.TypeString, // Optional: true, // Description: "Lists all of the privileges and roles granted to a share in the specified application package.", - //}, + // }, }, }, }, @@ -429,9 +429,9 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption }, } // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. - //if inApplicationPackage := shareMap["in_application_package"]; inApplicationPackage != "" { + // if inApplicationPackage := shareMap["in_application_package"]; inApplicationPackage != "" { // opts.To.Share.InApplicationPackage = sdk.Pointer(sdk.NewAccountObjectIdentifier(inApplicationPackage.(string))) - //} + // } } return opts, nil } diff --git a/pkg/sdk/testint/grants_integration_test.go b/pkg/sdk/testint/grants_integration_test.go index 6a7c63956c..43cd92c10f 100644 --- a/pkg/sdk/testint/grants_integration_test.go +++ b/pkg/sdk/testint/grants_integration_test.go @@ -756,7 +756,7 @@ func TestInt_GrantPrivilegeToShare(t *testing.T) { require.NoError(t, err) assertGrant(t, grants, table.ID(), sdk.ObjectPrivilegeSelect) - grants, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + _, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ To: &sdk.ShowGrantsTo{ Share: &sdk.ShowGrantsToShare{ Name: shareTest.ID(), @@ -769,9 +769,9 @@ func TestInt_GrantPrivilegeToShare(t *testing.T) { cleanupAppPackage := createApplicationPackage(t, client, appPackageName) t.Cleanup(cleanupAppPackage) // TODO [SNOW-1284382]: alter the test when the syntax starts working - //2024/03/29 17:04:20 [DEBUG] sql-conn-query: [query SHOW GRANTS TO SHARE "0a8DMkl3NOx7" IN APPLICATION PACKAGE "hziiAtqY" err 001003 (42000): SQL compilation error: - //syntax error line 1 at position 39 unexpected 'APPLICATION'. duration 445.248042ms args {}] (IYA62698) - grants, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + // 2024/03/29 17:04:20 [DEBUG] sql-conn-query: [query SHOW GRANTS TO SHARE "0a8DMkl3NOx7" IN APPLICATION PACKAGE "hziiAtqY" err 001003 (42000): SQL compilation error: + // syntax error line 1 at position 39 unexpected 'APPLICATION'. duration 445.248042ms args {}] (IYA62698) + _, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ To: &sdk.ShowGrantsTo{ Share: &sdk.ShowGrantsToShare{ Name: shareTest.ID(), From c4df1d3dd6c77745728e0c46b519acdb3f20179e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 29 Mar 2024 19:15:50 +0100 Subject: [PATCH 26/27] Fill out object name in one of the tests --- .../snowflake_grants_on_invalid_missing_object_type.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf index 52db3ea3fa..d94be962db 100644 --- a/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf +++ b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf @@ -1,5 +1,5 @@ data "snowflake_grants" "test" { grants_on { - object_name = "" + object_name = "some_object" } } From 00bbf531c95d35a90a836cda8a3752d74a6d2370 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Wed, 3 Apr 2024 13:16:31 +0200 Subject: [PATCH 27/27] Fix after review --- MIGRATION_GUIDE.md | 10 ++- docs/data-sources/grants.md | 18 ++-- .../snowflake_grants/data-source.tf | 12 +-- pkg/datasources/grants.go | 89 +++++++++---------- pkg/datasources/grants_acceptance_test.go | 12 +-- ...nowflake_grants_future_to_account_role.tf} | 2 +- .../{Role => AccountRole}/variables.tf | 0 .../snowflake_grants_of_account_role.tf} | 2 +- .../snowflake_grants_to_account_role.tf} | 2 +- 9 files changed, 77 insertions(+), 70 deletions(-) rename pkg/datasources/testdata/TestAcc_Grants/FutureTo/{Role/snowflake_grants_future_to_role.tf => AccountRole/snowflake_grants_future_to_account_role.tf} (89%) rename pkg/datasources/testdata/TestAcc_Grants/FutureTo/{Role => AccountRole}/variables.tf (100%) rename pkg/datasources/testdata/TestAcc_Grants/Of/{Role/snowflake_grants_of_role.tf => AccountRole/snowflake_grants_of_account_role.tf} (62%) rename pkg/datasources/testdata/TestAcc_Grants/To/{Role/snowflake_grants_to_role.tf => AccountRole/snowflake_grants_to_account_role.tf} (62%) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 41bc1e5221..127524395d 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -12,6 +12,14 @@ From now on, the `snowflake_procedure`'s `execute_as` parameter allows only two ### snowflake_grants datasource changes `snowflake_grants` datasource was refreshed as part of the ongoing [Grants Redesign](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#redesigning-grants). +#### *(behavior change)* role fields renames +To be aligned with the convention in other grant resources, `role` was renamed to `account_role` for the following fields: +- `grants_to.role` +- `grants_of.role` +- `future_grants_to.role`. + +To migrate simply change `role` to `account_role` in the aforementioned fields. + #### *(behavior change)* grants_to.share type change `grants_to.share` was a text field. Because Snowflake introduced new syntax `SHOW GRANTS TO SHARE IN APPLICATION PACKAGE ` (check more in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants)) the type was changed to object. To migrate simply change: ```terraform @@ -75,7 +83,7 @@ No migration work is needed here. No migration work is needed here. #### *(documentation)* improvements -Descriptions of attributes were altered. More examples were added (both for old nd new features). +Descriptions of attributes were altered. More examples were added (both for old and new features). ## v0.86.0 ➞ v0.87.0 ### snowflake_database resource changes diff --git a/docs/data-sources/grants.md b/docs/data-sources/grants.md index 5dab679c61..c7ccbfafce 100644 --- a/docs/data-sources/grants.md +++ b/docs/data-sources/grants.md @@ -65,10 +65,10 @@ data "snowflake_grants" "example_to_application_role" { } } -# role +# account role data "snowflake_grants" "example_to_role" { grants_to { - role = "some_role" + account_role = "some_role" } } @@ -113,10 +113,10 @@ data "snowflake_grants" "example_of_database_role" { } } -# role +# account role data "snowflake_grants" "example_of_role" { grants_of { - role = "some_role" + account_role = "some_role" } } @@ -149,10 +149,10 @@ data "snowflake_grants" "example_future_in_schema" { ### SHOW FUTURE GRANTS TO ... ################################## -# role +# account role data "snowflake_grants" "example_future_to_role" { future_grants_to { - role = "some_role" + account_role = "some_role" } } @@ -194,8 +194,8 @@ Optional: Optional: +- `account_role` (String) Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the account role. - `database_role` (String) Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). -- `role` (String) Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role. @@ -203,9 +203,9 @@ Optional: Optional: +- `account_role` (String) Lists all users and roles to which the account role has been granted. - `application_role` (String) Lists all the users and roles to which the application role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). - `database_role` (String) Lists all users and roles to which the database role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). -- `role` (String) Lists all users and roles to which the role has been granted. - `share` (String) Lists all the accounts for the share and indicates the accounts that are using the share. @@ -224,10 +224,10 @@ Optional: Optional: +- `account_role` (String) Lists all privileges and roles granted to the role. - `application` (String) Lists all the privileges and roles granted to the application. - `application_role` (String) Lists all the privileges and roles granted to the application role. Must be a fully qualified name ("<app_name>"."<app_role_name>"). - `database_role` (String) Lists all privileges and roles granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). -- `role` (String) Lists all privileges and roles granted to the role. - `share` (Block List, Max: 1) Lists all the privileges granted to the share. (see [below for nested schema](#nestedblock--grants_to--share)) - `user` (String) Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed. diff --git a/examples/data-sources/snowflake_grants/data-source.tf b/examples/data-sources/snowflake_grants/data-source.tf index 6fe9c8c438..758f729eed 100644 --- a/examples/data-sources/snowflake_grants/data-source.tf +++ b/examples/data-sources/snowflake_grants/data-source.tf @@ -51,10 +51,10 @@ data "snowflake_grants" "example_to_application_role" { } } -# role +# account role data "snowflake_grants" "example_to_role" { grants_to { - role = "some_role" + account_role = "some_role" } } @@ -99,10 +99,10 @@ data "snowflake_grants" "example_of_database_role" { } } -# role +# account role data "snowflake_grants" "example_of_role" { grants_of { - role = "some_role" + account_role = "some_role" } } @@ -135,10 +135,10 @@ data "snowflake_grants" "example_future_in_schema" { ### SHOW FUTURE GRANTS TO ... ################################## -# role +# account role data "snowflake_grants" "example_future_to_role" { future_grants_to { - role = "some_role" + account_role = "some_role" } } diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 7de61e3556..ad2e465e6c 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -35,11 +35,10 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Type of object to list privileges on.", }, "account": { - Type: schema.TypeBool, - Optional: true, - Description: "Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles.", - ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, - ConflictsWith: []string{"grants_on.0.object_type"}, + Type: schema.TypeBool, + Optional: true, + Description: "Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles.", + ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, }, }, }, @@ -59,7 +58,7 @@ var grantsSchema = map[string]*schema.Schema{ ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", @@ -72,21 +71,21 @@ var grantsSchema = map[string]*schema.Schema{ ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, - "role": { + "account_role": { Type: schema.TypeString, Optional: true, Description: "Lists all privileges and roles granted to the role.", ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", @@ -99,7 +98,7 @@ var grantsSchema = map[string]*schema.Schema{ ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", @@ -113,7 +112,7 @@ var grantsSchema = map[string]*schema.Schema{ ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", @@ -127,7 +126,7 @@ var grantsSchema = map[string]*schema.Schema{ ExactlyOneOf: []string{ "grants_to.0.application", "grants_to.0.application_role", - "grants_to.0.role", + "grants_to.0.account_role", "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", @@ -159,12 +158,12 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all objects to which the given object has been granted.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "account_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all users and roles to which the role has been granted.", + Description: "Lists all users and roles to which the account role has been granted.", ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", @@ -175,7 +174,7 @@ var grantsSchema = map[string]*schema.Schema{ Optional: true, Description: "Lists all users and roles to which the database role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", @@ -187,7 +186,7 @@ var grantsSchema = map[string]*schema.Schema{ Optional: true, Description: "Lists all the users and roles to which the application role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", @@ -199,7 +198,7 @@ var grantsSchema = map[string]*schema.Schema{ Optional: true, Description: "Lists all the accounts for the share and indicates the accounts that are using the share.", ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", "grants_of.0.database_role", "grants_of.0.application_role", "grants_of.0.share", @@ -246,12 +245,12 @@ var grantsSchema = map[string]*schema.Schema{ Description: "Lists all privileges granted to the object on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "account_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role.", + Description: "Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the account role.", ExactlyOneOf: []string{ - "future_grants_to.0.role", + "future_grants_to.0.account_role", "future_grants_to.0.database_role", }, }, @@ -260,7 +259,7 @@ var grantsSchema = map[string]*schema.Schema{ Optional: true, Description: "Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ - "future_grants_to.0.role", + "future_grants_to.0.account_role", "future_grants_to.0.database_role", }, ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), @@ -326,25 +325,25 @@ func Grants() *schema.Resource { } } -func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ReadGrants(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client var opts *sdk.ShowGrantOptions var err error if grantsOn, ok := d.GetOk("grants_on"); ok { - opts, err = buildOptsForGrantsOn(grantsOn.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForGrantsOn(grantsOn.([]any)[0].(map[string]any)) } if grantsTo, ok := d.GetOk("grants_to"); ok { - opts, err = buildOptsForGrantsTo(grantsTo.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForGrantsTo(grantsTo.([]any)[0].(map[string]any)) } if grantsOf, ok := d.GetOk("grants_of"); ok { - opts, err = buildOptsForGrantsOf(grantsOf.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForGrantsOf(grantsOf.([]any)[0].(map[string]any)) } if futureGrantsIn, ok := d.GetOk("future_grants_in"); ok { - opts, err = buildOptsForFutureGrantsIn(futureGrantsIn.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForFutureGrantsIn(futureGrantsIn.([]any)[0].(map[string]any)) } if futureGrantsTo, ok := d.GetOk("future_grants_to"); ok { - opts, err = buildOptsForFutureGrantsTo(futureGrantsTo.([]interface{})[0].(map[string]interface{})) + opts, err = buildOptsForFutureGrantsTo(futureGrantsTo.([]any)[0].(map[string]any)) } if err != nil { return diag.FromErr(err) @@ -355,7 +354,7 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d return diag.FromErr(err) } - err = d.Set("grants", flattenGrants(grants)) + err = d.Set("grants", convertGrants(grants)) if err != nil { return diag.FromErr(err) } @@ -364,7 +363,7 @@ func ReadGrants(ctx context.Context, d *schema.ResourceData, meta interface{}) d return nil } -func buildOptsForGrantsOn(grantsOn map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForGrantsOn(grantsOn map[string]any) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) objectType := grantsOn["object_type"].(string) @@ -393,7 +392,7 @@ func buildOptsForGrantsOn(grantsOn map[string]interface{}) (*sdk.ShowGrantOption return opts, nil } -func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForGrantsTo(grantsTo map[string]any) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) if application := grantsTo["application"].(string); application != "" { @@ -406,9 +405,9 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption ApplicationRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(applicationRole), } } - if role := grantsTo["role"].(string); role != "" { + if accountRole := grantsTo["account_role"].(string); accountRole != "" { opts.To = &sdk.ShowGrantsTo{ - Role: sdk.NewAccountObjectIdentifier(role), + Role: sdk.NewAccountObjectIdentifier(accountRole), } } if databaseRole := grantsTo["database_role"].(string); databaseRole != "" { @@ -421,8 +420,8 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption User: sdk.NewAccountObjectIdentifier(user), } } - if share := grantsTo["share"]; share != nil && len(share.([]interface{})) > 0 { - shareMap := share.([]interface{})[0].(map[string]interface{}) + if share := grantsTo["share"]; share != nil && len(share.([]any)) > 0 { + shareMap := share.([]any)[0].(map[string]any) opts.To = &sdk.ShowGrantsTo{ Share: &sdk.ShowGrantsToShare{ Name: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), @@ -436,12 +435,12 @@ func buildOptsForGrantsTo(grantsTo map[string]interface{}) (*sdk.ShowGrantOption return opts, nil } -func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForGrantsOf(grantsOf map[string]any) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) - if role := grantsOf["role"].(string); role != "" { + if accountRole := grantsOf["account_role"].(string); accountRole != "" { opts.Of = &sdk.ShowGrantsOf{ - Role: sdk.NewAccountObjectIdentifier(role), + Role: sdk.NewAccountObjectIdentifier(accountRole), } } if databaseRole := grantsOf["database_role"].(string); databaseRole != "" { @@ -462,7 +461,7 @@ func buildOptsForGrantsOf(grantsOf map[string]interface{}) (*sdk.ShowGrantOption return opts, nil } -func buildOptsForFutureGrantsIn(futureGrantsIn map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForFutureGrantsIn(futureGrantsIn map[string]any) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) opts.Future = sdk.Bool(true) @@ -479,13 +478,13 @@ func buildOptsForFutureGrantsIn(futureGrantsIn map[string]interface{}) (*sdk.Sho return opts, nil } -func buildOptsForFutureGrantsTo(futureGrantsTo map[string]interface{}) (*sdk.ShowGrantOptions, error) { +func buildOptsForFutureGrantsTo(futureGrantsTo map[string]any) (*sdk.ShowGrantOptions, error) { opts := new(sdk.ShowGrantOptions) opts.Future = sdk.Bool(true) - if role := futureGrantsTo["role"].(string); role != "" { + if accountRole := futureGrantsTo["account_role"].(string); accountRole != "" { opts.To = &sdk.ShowGrantsTo{ - Role: sdk.NewAccountObjectIdentifier(role), + Role: sdk.NewAccountObjectIdentifier(accountRole), } } if databaseRole := futureGrantsTo["database_role"].(string); databaseRole != "" { @@ -496,10 +495,10 @@ func buildOptsForFutureGrantsTo(futureGrantsTo map[string]interface{}) (*sdk.Sho return opts, nil } -func flattenGrants(grants []sdk.Grant) []map[string]interface{} { - grantDetails := make([]map[string]interface{}, len(grants)) +func convertGrants(grants []sdk.Grant) []map[string]any { + grantDetails := make([]map[string]any, len(grants)) for i, grant := range grants { - grantDetails[i] = map[string]interface{}{ + grantDetails[i] = map[string]any{ "created_on": grant.CreatedOn.String(), "privilege": grant.Privilege, "granted_on": grant.GrantedOn.String(), diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index 03409784c0..5389832530 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -150,7 +150,7 @@ func TestAcc_Grants_To_ApplicationRole(t *testing.T) { t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") } -func TestAcc_Grants_To_Role(t *testing.T) { +func TestAcc_Grants_To_AccountRole(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -160,7 +160,7 @@ func TestAcc_Grants_To_Role(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Role"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/AccountRole"), Check: checkAtLeastOneGrantPresent(), }, }, @@ -317,7 +317,7 @@ func TestAcc_Grants_To_Invalid_ApplicationRoleIdInvalid(t *testing.T) { }) } -func TestAcc_Grants_Of_Role(t *testing.T) { +func TestAcc_Grants_Of_AccountRole(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -327,7 +327,7 @@ func TestAcc_Grants_Of_Role(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Role"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/AccountRole"), Check: checkAtLeastOneGrantPresentLimited(), }, }, @@ -567,7 +567,7 @@ func TestAcc_Grants_FutureIn_Invalid_SchemaNameNotFullyQualified(t *testing.T) { }) } -func TestAcc_Grants_FutureTo_Role(t *testing.T) { +func TestAcc_Grants_FutureTo_AccountRole(t *testing.T) { databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) configVariables := config.Variables{ "database": config.StringVariable(databaseName), @@ -582,7 +582,7 @@ func TestAcc_Grants_FutureTo_Role(t *testing.T) { CheckDestroy: nil, Steps: []resource.TestStep{ { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Role"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/AccountRole"), ConfigVariables: configVariables, Check: checkAtLeastOneFutureGrantPresent(), }, diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf similarity index 89% rename from pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf rename to pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf index 148cc2359b..51959e6ff9 100644 --- a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/snowflake_grants_future_to_role.tf +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf @@ -17,6 +17,6 @@ data "snowflake_grants" "test" { depends_on = [snowflake_grant_privileges_to_account_role.test] future_grants_to { - role = data.snowflake_current_role.test.name + account_role = data.snowflake_current_role.test.name } } diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/variables.tf similarity index 100% rename from pkg/datasources/testdata/TestAcc_Grants/FutureTo/Role/variables.tf rename to pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/variables.tf diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf similarity index 62% rename from pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf rename to pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf index 09fa52cc21..7a0815f229 100644 --- a/pkg/datasources/testdata/TestAcc_Grants/Of/Role/snowflake_grants_of_role.tf +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf @@ -2,6 +2,6 @@ data "snowflake_current_role" "test" {} data "snowflake_grants" "test" { grants_of { - role = data.snowflake_current_role.test.name + account_role = data.snowflake_current_role.test.name } } diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf b/pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf similarity index 62% rename from pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf rename to pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf index 99cce594e1..a5bb91a0b7 100644 --- a/pkg/datasources/testdata/TestAcc_Grants/To/Role/snowflake_grants_to_role.tf +++ b/pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf @@ -2,6 +2,6 @@ data "snowflake_current_role" "test" {} data "snowflake_grants" "test" { grants_to { - role = data.snowflake_current_role.test.name + account_role = data.snowflake_current_role.test.name } }