-
Notifications
You must be signed in to change notification settings - Fork 399
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Create a snowflake_user_grant resource. (#1193)
* feat: snowflake_user_grant resource * fix user_grant_acceptance_test * fix acceptance test test and docs Co-authored-by: hleb.lizunkou <Hleb.Lizunkou@itechart-group.com>
- Loading branch information
1 parent
ea01e66
commit 37500ac
Showing
8 changed files
with
366 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "snowflake_user_grant Resource - terraform-provider-snowflake" | ||
subcategory: "" | ||
description: |- | ||
--- | ||
|
||
# snowflake_user_grant (Resource) | ||
|
||
|
||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource snowflake_user_grant grant { | ||
user_name = "user" | ||
privilege = "MONITOR" | ||
roles = [ | ||
"role1", | ||
] | ||
with_grant_option = false | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `privilege` (String) The privilege to grant on the user. | ||
- `user_name` (String) The name of the user on which to grant privileges. | ||
|
||
### Optional | ||
|
||
- `enable_multiple_grants` (Boolean) When this is set to true, multiple grants of the same type can be created. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform. | ||
- `roles` (Set of String) Grants privilege to these roles. | ||
- `with_grant_option` (Boolean) When this is set to true, allows the recipient role to grant the privileges to other roles. | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
## Import | ||
|
||
Import is supported using the following syntax: | ||
|
||
```shell | ||
# format is user name | | | privilege | true/false for with_grant_option | ||
terraform import snowflake_user_grant.example 'userName|||MONITOR|true' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# format is user name | | | privilege | true/false for with_grant_option | ||
terraform import snowflake_user_grant.example 'userName|||MONITOR|true' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
resource snowflake_user_grant grant { | ||
user_name = "user" | ||
privilege = "MONITOR" | ||
|
||
roles = [ | ||
"role1", | ||
] | ||
|
||
with_grant_option = false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package resources | ||
|
||
import ( | ||
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
) | ||
|
||
var validUserPrivileges = NewPrivilegeSet( | ||
privilegeMonitor, | ||
privilegeOwnership, | ||
) | ||
var userGrantSchema = map[string]*schema.Schema{ | ||
"user_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "The name of the user on which to grant privileges.", | ||
ForceNew: true, | ||
}, | ||
"privilege": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "The privilege to grant on the user.", | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice(validUserPrivileges.ToList(), true), | ||
}, | ||
"roles": { | ||
Type: schema.TypeSet, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Optional: true, | ||
Description: "Grants privilege to these roles.", | ||
}, | ||
"with_grant_option": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Description: "When this is set to true, allows the recipient role to grant the privileges to other roles.", | ||
Default: false, | ||
ForceNew: true, | ||
}, | ||
"enable_multiple_grants": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Description: "When this is set to true, multiple grants of the same type can be created. This will cause Terraform to not revoke grants applied to roles and objects outside Terraform.", | ||
Default: false, | ||
}, | ||
} | ||
|
||
// UserGrant returns a pointer to the resource representing a user grant | ||
func UserGrant() *TerraformGrantResource { | ||
return &TerraformGrantResource{ | ||
Resource: &schema.Resource{ | ||
Create: CreateUserGrant, | ||
Read: ReadUserGrant, | ||
Delete: DeleteUserGrant, | ||
Update: UpdateUserGrant, | ||
|
||
Schema: userGrantSchema, | ||
// FIXME - tests for this don't currently work | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
}, | ||
ValidPrivs: validUserPrivileges, | ||
} | ||
} | ||
|
||
// CreateUserGrant implements schema.CreateFunc | ||
func CreateUserGrant(d *schema.ResourceData, meta interface{}) error { | ||
w := d.Get("user_name").(string) | ||
priv := d.Get("privilege").(string) | ||
grantOption := d.Get("with_grant_option").(bool) | ||
builder := snowflake.UserGrant(w) | ||
roles := expandStringList(d.Get("roles").(*schema.Set).List()) | ||
|
||
err := createGenericGrant(d, meta, builder) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
grant := &grantID{ | ||
ResourceName: w, | ||
Privilege: priv, | ||
GrantOption: grantOption, | ||
Roles: roles, | ||
} | ||
dataIDInput, err := grant.String() | ||
if err != nil { | ||
return err | ||
} | ||
d.SetId(dataIDInput) | ||
|
||
return ReadUserGrant(d, meta) | ||
} | ||
|
||
// ReadUserGrant implements schema.ReadFunc | ||
func ReadUserGrant(d *schema.ResourceData, meta interface{}) error { | ||
grantID, err := grantIDFromString(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
w := grantID.ResourceName | ||
priv := grantID.Privilege | ||
|
||
err = d.Set("user_name", w) | ||
if err != nil { | ||
return err | ||
} | ||
err = d.Set("privilege", priv) | ||
if err != nil { | ||
return err | ||
} | ||
err = d.Set("with_grant_option", grantID.GrantOption) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
builder := snowflake.UserGrant(w) | ||
|
||
return readGenericGrant(d, meta, userGrantSchema, builder, false, validUserPrivileges) | ||
} | ||
|
||
// DeleteUserGrant implements schema.DeleteFunc | ||
func DeleteUserGrant(d *schema.ResourceData, meta interface{}) error { | ||
grantID, err := grantIDFromString(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
w := grantID.ResourceName | ||
|
||
builder := snowflake.UserGrant(w) | ||
|
||
return deleteGenericGrant(d, meta, builder) | ||
} | ||
|
||
// UpdateUserGrant implements schema.UpdateFunc | ||
func UpdateUserGrant(d *schema.ResourceData, meta interface{}) error { | ||
// for now the only thing we can update is roles. if nothing changed, | ||
// nothing to update and we're done. | ||
if !d.HasChanges("roles") { | ||
return nil | ||
} | ||
|
||
rolesToAdd, rolesToRevoke := changeDiff(d, "roles") | ||
|
||
grantID, err := grantIDFromString(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// create the builder | ||
builder := snowflake.UserGrant(grantID.ResourceName) | ||
|
||
// first revoke | ||
if err := deleteGenericGrantRolesAndShares( | ||
meta, | ||
builder, | ||
grantID.Privilege, | ||
rolesToRevoke, | ||
nil, | ||
); err != nil { | ||
return err | ||
} | ||
|
||
// then add | ||
if err := createGenericGrantRolesAndShares( | ||
meta, | ||
builder, | ||
grantID.Privilege, | ||
grantID.GrantOption, | ||
rolesToAdd, | ||
nil, | ||
); err != nil { | ||
return err | ||
} | ||
|
||
// Done, refresh state | ||
return ReadUserGrant(d, meta) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package resources_test | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAcc_UserGrant(t *testing.T) { | ||
if _, ok := os.LookupEnv("SKIP_USER_GRANT_TESTS"); ok { | ||
t.Skip("Skipping TestAccUserGrant") | ||
} | ||
wName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) | ||
roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
Providers: providers(), | ||
CheckDestroy: nil, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: userGrantConfig(wName, roleName), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("snowflake_user_grant.test", "user_name", wName), | ||
resource.TestCheckResourceAttr("snowflake_user_grant.test", "privilege", "MONITOR"), | ||
), | ||
}, | ||
// IMPORT | ||
{ | ||
ResourceName: "snowflake_user_grant.test", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
ImportStateVerifyIgnore: []string{ | ||
"enable_multiple_grants", // feature flag attribute not defined in Snowflake, can't be imported | ||
}, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func userGrantConfig(n, role string) string { | ||
return fmt.Sprintf(` | ||
resource "snowflake_user" "test" { | ||
name = "%v" | ||
} | ||
resource "snowflake_role" "test" { | ||
name = "%v" | ||
} | ||
resource "snowflake_user_grant" "test" { | ||
user_name = snowflake_user.test.name | ||
roles = [snowflake_role.test.name] | ||
privilege = "MONITOR" | ||
} | ||
`, n, role) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package resources_test | ||
|
||
import ( | ||
"database/sql" | ||
"testing" | ||
"time" | ||
|
||
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" | ||
. "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/testhelpers" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestUserGrant(t *testing.T) { | ||
r := require.New(t) | ||
err := resources.UserGrant().Resource.InternalValidate(provider.Provider().Schema, true) | ||
r.NoError(err) | ||
} | ||
|
||
func TestUserGrantCreate(t *testing.T) { | ||
r := require.New(t) | ||
|
||
in := map[string]interface{}{ | ||
"user_name": "test-user", | ||
"privilege": "MONITOR", | ||
"roles": []interface{}{"test-role-1", "test-role-2"}, | ||
} | ||
d := schema.TestResourceDataRaw(t, resources.UserGrant().Resource.Schema, in) | ||
r.NotNil(d) | ||
|
||
WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { | ||
mock.ExpectExec(`^GRANT MONITOR ON USER "test-user" TO ROLE "test-role-1"$`).WillReturnResult(sqlmock.NewResult(1, 1)) | ||
mock.ExpectExec(`^GRANT MONITOR ON USER "test-user" TO ROLE "test-role-2"$`).WillReturnResult(sqlmock.NewResult(1, 1)) | ||
expectReadUserGrant(mock) | ||
err := resources.CreateUserGrant(d, db) | ||
r.NoError(err) | ||
}) | ||
} | ||
|
||
func expectReadUserGrant(mock sqlmock.Sqlmock) { | ||
rows := sqlmock.NewRows([]string{ | ||
"created_on", "privilege", "granted_on", "name", "granted_to", "grantee_name", "grant_option", "granted_by", | ||
}).AddRow( | ||
time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), "MONITOR", "USER", "test-user", "ROLE", "test-role-1", false, "bob", | ||
).AddRow( | ||
time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), "MONITOR", "USER", "test-user", "ROLE", "test-role-2", false, "bob", | ||
) | ||
mock.ExpectQuery(`^SHOW GRANTS ON USER "test-user"$`).WillReturnRows(rows) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters