diff --git a/pkg/resources/role_grants.go b/pkg/resources/role_grants.go index 21e4a82963..0a944350d1 100644 --- a/pkg/resources/role_grants.go +++ b/pkg/resources/role_grants.go @@ -2,6 +2,7 @@ package resources import ( "database/sql" + "errors" "fmt" "log" "strings" @@ -26,6 +27,7 @@ func RoleGrants() *schema.Resource { Type: schema.TypeString, Required: true, Description: "The name of the role we are granting.", + ForceNew: true, ValidateFunc: func(val interface{}, key string) ([]string, []error) { additionalCharsToIgnoreValidation := []string{".", " ", ":", "(", ")"} return snowflake.ValidateIdentifier(val, additionalCharsToIgnoreValidation) @@ -115,6 +117,15 @@ func ReadRoleGrants(d *schema.ResourceData, meta interface{}) error { roles := make([]string, 0) users := make([]string, 0) + row := snowflake.QueryRow(db, fmt.Sprintf("SHOW ROLES LIKE '%s'", grantID.ObjectName)) + _, err = snowflake.ScanRole(row) + if errors.Is(err, sql.ErrNoRows) { + // If not found, mark resource to be removed from statefile during apply or refresh + log.Printf("[DEBUG] role (%s) not found", grantID.ObjectName) + d.SetId("") + return nil + } + grants, err := readGrants(db, grantID.ObjectName) if err != nil { return err @@ -209,6 +220,7 @@ func DeleteRoleGrants(d *schema.ResourceData, meta interface{}) error { func revokeRoleFromRole(db *sql.DB, role1, role2 string) error { rg := snowflake.RoleGrant(role1).Role(role2) err := snowflake.Exec(db, rg.Revoke()) + log.Printf("revokeRoleFromRole %v", err) if driverErr, ok := err.(*gosnowflake.SnowflakeError); ok { //nolint:errorlint // todo: should be fixed if driverErr.Number == 2003 { // handling error if a role has been deleted prior to revoking a role diff --git a/pkg/resources/role_grants_acceptance_test.go b/pkg/resources/role_grants_acceptance_test.go index a6682a2288..5ea1ff3961 100644 --- a/pkg/resources/role_grants_acceptance_test.go +++ b/pkg/resources/role_grants_acceptance_test.go @@ -151,6 +151,14 @@ func TestAcc_GrantRole(t *testing.T) { }, ), }, + // RENAMING + { + Config: rgConfig2(role1+"foo", role2, role3, user1, user2), + ResourceName: "snowflake_role_grants.w", + Check: resource.ComposeTestCheckFunc( + testCheckRolesAndUsers(t, "snowflake_role_grants.w", []string{role2}, []string{user1, user2})), + }, + baselineStep, // IMPORT { ResourceName: "snowflake_role_grants.w", diff --git a/pkg/resources/role_grants_test.go b/pkg/resources/role_grants_test.go index 7f3c840a63..1bc5a22820 100644 --- a/pkg/resources/role_grants_test.go +++ b/pkg/resources/role_grants_test.go @@ -4,6 +4,8 @@ import ( "database/sql" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + sqlmock "github.com/DATA-DOG/go-sqlmock" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" @@ -63,14 +65,36 @@ func TestRoleGrantsRead(t *testing.T) { }) WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { + r.NotEmpty(d.State()) expectReadRoleGrants(mock) err := resources.ReadRoleGrants(d, db) + r.NotEmpty(d.State()) r.NoError(err) r.Len(d.Get("users").(*schema.Set).List(), 0) r.Len(d.Get("roles").(*schema.Set).List(), 2) }) } +func TestRoleGrantsReadNotExists(t *testing.T) { + r := require.New(t) + + d := roleGrants(t, "good_name||||role1,role2|false", map[string]interface{}{ + "role_name": "good_name", + "roles": []interface{}{"role1", "role2"}, + "users": []interface{}{"user1", "user2"}, + }) + + WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { + // Test when schema resource is not found, checking if state will be empty + r.NotEmpty(d.State()) + q := snowflake.NewRoleBuilder("good_name").Show() + mock.ExpectQuery(q).WillReturnError(sql.ErrNoRows) + err := resources.ReadRoleGrants(d, db) + r.Empty(d.State()) + r.NoError(err) + }) +} + func TestRoleGrantsDelete(t *testing.T) { r := require.New(t)