From 766080283ac53e95ee93b619b75fb7f217c15532 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 1 Feb 2024 13:23:00 +0100 Subject: [PATCH 01/11] Remove user from snowflake package --- pkg/datasources/users.go | 62 ++++++----- pkg/resources/network_policy_attachment.go | 9 +- pkg/resources/role_grants.go | 9 +- pkg/resources/user_public_keys.go | 10 +- pkg/snowflake/user.go | 120 --------------------- pkg/snowflake/user_test.go | 45 -------- 6 files changed, 51 insertions(+), 204 deletions(-) delete mode 100644 pkg/snowflake/user.go delete mode 100644 pkg/snowflake/user_test.go diff --git a/pkg/datasources/users.go b/pkg/datasources/users.go index 5dce0d2d37..bcfaf6ca25 100644 --- a/pkg/datasources/users.go +++ b/pkg/datasources/users.go @@ -1,15 +1,14 @@ package datasources import ( + "context" "database/sql" - "errors" "fmt" "log" "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -107,49 +106,48 @@ func Users() *schema.Resource { func ReadUsers(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) + userPattern := d.Get("pattern").(string) - account, err := snowflake.ReadCurrentAccount(db) - if err != nil { + account, err1 := client.ContextFunctions.CurrentAccount(ctx) + region, err2 := client.ContextFunctions.CurrentRegion(ctx) + if err1 != nil || err2 != nil { log.Print("[DEBUG] unable to retrieve current account") d.SetId("") return nil } - d.SetId(fmt.Sprintf("%s.%s", account.Account, account.Region)) + d.SetId(fmt.Sprintf("%s.%s", account, region)) + extractedUsers, err := client.Users.Show(ctx, &sdk.ShowUserOptions{ + Like: &sdk.Like{Pattern: sdk.String(userPattern)}, + }) - currentUsers, err := snowflake.ListUsers(userPattern, db) - if errors.Is(err, sql.ErrNoRows) { - // If not found, mark resource to be removed from state file during apply or refresh + if err != nil { log.Printf("[DEBUG] no users found in account (%s)", d.Id()) d.SetId("") return nil - } else if err != nil { - log.Printf("[DEBUG] unable to parse users in account (%s)", d.Id()) - d.SetId("") - return nil } - users := []map[string]interface{}{} - - for _, user := range currentUsers { - userMap := map[string]interface{}{} - userMap["name"] = user.Name.String - userMap["login_name"] = user.LoginName.String - userMap["comment"] = user.Comment.String - userMap["disabled"] = user.Disabled - userMap["default_warehouse"] = user.DefaultWarehouse.String - userMap["default_namespace"] = user.DefaultNamespace.String - userMap["default_role"] = user.DefaultRole.String - userMap["default_secondary_roles"] = strings.Split( - helpers.ListContentToString(user.DefaultSecondaryRoles.String), ",") - userMap["has_rsa_public_key"] = user.HasRsaPublicKey - userMap["email"] = user.Email.String - userMap["display_name"] = user.DisplayName.String - userMap["first_name"] = user.FirstName.String - userMap["last_name"] = user.LastName.String + users := make([]map[string]any, len(extractedUsers)) - users = append(users, userMap) + for i, user := range extractedUsers { + users[i] = map[string]any{ + "name": user.Name, + "login_name": user.LoginName, + "comment": user.Comment, + "disabled": user.Disabled, + "default_warehouse": user.DefaultWarehouse, + "default_namespace": user.DefaultNamespace, + "default_role": user.DefaultRole, + "default_secondary_roles": strings.Split(helpers.ListContentToString(user.DefaultSecondaryRoles), ","), + "has_rsa_public_key": user.HasRsaPublicKey, + "email": user.Email, + "display_name": user.DisplayName, + "first_name": user.FirstName, + "last_name": user.LastName, + } } return d.Set("users", users) diff --git a/pkg/resources/network_policy_attachment.go b/pkg/resources/network_policy_attachment.go index ecce6eaf06..4e33e605c3 100644 --- a/pkg/resources/network_policy_attachment.go +++ b/pkg/resources/network_policy_attachment.go @@ -1,9 +1,11 @@ package resources import ( + "context" "database/sql" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "log" "strings" @@ -275,9 +277,12 @@ func unsetOnUser(user string, data *schema.ResourceData, meta interface{}) error // ensureUserAlterPrivileges ensures the executing Snowflake user can alter each user in the set of users. func ensureUserAlterPrivileges(users []string, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) + for _, user := range users { - userDescSQL := snowflake.NewUserBuilder(user).Describe() - if err := snowflake.Exec(db, userDescSQL); err != nil { + _, err := client.Users.Describe(ctx, sdk.NewAccountObjectIdentifier(user)) + if err != nil { return fmt.Errorf("error altering network policy of user %v", user) } } diff --git a/pkg/resources/role_grants.go b/pkg/resources/role_grants.go index aa1e33d706..c4a7861a60 100644 --- a/pkg/resources/role_grants.go +++ b/pkg/resources/role_grants.go @@ -256,6 +256,9 @@ func revokeRoleFromRole(db *sql.DB, role1, role2 string) error { } func revokeRoleFromUser(db *sql.DB, role1, user string) error { + ctx := context.Background() + client := sdk.NewClientFromDB(db) + rg := snowflake.RoleGrant(role1).User(user) err := snowflake.Exec(db, rg.Revoke()) if driverErr, ok := err.(*gosnowflake.SnowflakeError); ok { //nolint:errorlint // todo: should be fixed @@ -263,10 +266,12 @@ func revokeRoleFromUser(db *sql.DB, role1, user string) error { // 002003 (02000): SQL compilation error: // User 'XXX' does not exist or not authorized. if driverErr.Number == 2003 { - users, _ := snowflake.ListUsers(user, db) + users, _ := client.Users.Show(ctx, &sdk.ShowUserOptions{ + Like: &sdk.Like{Pattern: sdk.String(user)}, + }) logins := make([]string, len(users)) for i, u := range users { - logins[i] = u.LoginName.String + logins[i] = u.LoginName } if !snowflake.Contains(logins, user) { log.Printf("[WARN] User %s does not exist. No need to revoke role %s", user, role1) diff --git a/pkg/resources/user_public_keys.go b/pkg/resources/user_public_keys.go index e7c92ae3e7..30249e5934 100644 --- a/pkg/resources/user_public_keys.go +++ b/pkg/resources/user_public_keys.go @@ -1,9 +1,11 @@ package resources import ( + "context" "database/sql" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "log" "strings" @@ -60,10 +62,12 @@ func UserPublicKeys() *schema.Resource { } func checkUserExists(db *sql.DB, name string) (bool, error) { + ctx := context.Background() + client := sdk.NewClientFromDB(db) + // First check if user exists - stmt := snowflake.NewUserBuilder(name).Describe() - _, err := snowflake.Query(db, stmt) - if errors.Is(err, sql.ErrNoRows) { + _, err := client.Users.Describe(ctx, sdk.NewAccountObjectIdentifier(name)) + if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { log.Printf("[DEBUG] user (%s) not found", name) return false, nil } diff --git a/pkg/snowflake/user.go b/pkg/snowflake/user.go deleted file mode 100644 index a3d8c60e37..0000000000 --- a/pkg/snowflake/user.go +++ /dev/null @@ -1,120 +0,0 @@ -package snowflake - -import ( - "database/sql" - "errors" - "fmt" - "log" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/jmoiron/sqlx" -) - -func NewUserBuilder(name string) *Builder { - return &Builder{ - entityType: UserType, - name: name, - } -} - -type User struct { - Comment sql.NullString `db:"comment"` - DefaultNamespace sql.NullString `db:"default_namespace"` - DefaultRole sql.NullString `db:"default_role"` - DefaultSecondaryRoles sql.NullString `db:"default_secondary_roles"` - DefaultWarehouse sql.NullString `db:"default_warehouse"` - Disabled bool `db:"disabled"` - DisplayName sql.NullString `db:"display_name"` - Email sql.NullString `db:"email"` - FirstName sql.NullString `db:"first_name"` - HasRsaPublicKey bool `db:"has_rsa_public_key"` - LastName sql.NullString `db:"last_name"` - LoginName sql.NullString `db:"login_name"` - Name sql.NullString `db:"name"` -} - -func ScanUser(row *sqlx.Row) (*User, error) { - r := &User{} - err := row.StructScan(r) - return r, err -} - -func ScanUserDescription(rows *sqlx.Rows) (*User, error) { - r := &User{} - var err error - - for rows.Next() { - userProp := &DescribeUserProp{} - if err := rows.StructScan(userProp); err != nil { - return nil, err - } - - // The "DESCRIBE USER ..." command returns the string "null" for null values - if userProp.Value.String == "null" { - userProp.Value.Valid = false - userProp.Value.String = "" - } - - switch propery := userProp.Property; propery { - case "COMMENT": - r.Comment = userProp.Value - case "DEFAULT_NAMESPACE": - r.DefaultNamespace = userProp.Value - case "DEFAULT_ROLE": - r.DefaultRole = userProp.Value - case "DEFAULT_SECONDARY_ROLES": - if len(userProp.Value.String) > 0 { - defaultSecondaryRoles := helpers.ListContentToString(userProp.Value.String) - r.DefaultSecondaryRoles = sql.NullString{String: defaultSecondaryRoles, Valid: true} - } else { - r.DefaultSecondaryRoles = sql.NullString{Valid: false} - } - case "DEFAULT_WAREHOUSE": - r.DefaultWarehouse = userProp.Value - case "DISABLED": - r.Disabled = userProp.Value.String == "true" - case "DISPLAY_NAME": - r.DisplayName = userProp.Value - case "EMAIL": - r.Email = userProp.Value - case "FIRST_NAME": - r.FirstName = userProp.Value - case "RSA_PUBLIC_KEY_FP": - r.HasRsaPublicKey = userProp.Value.Valid - case "LAST_NAME": - r.LastName = userProp.Value - case "LOGIN_NAME": - r.LoginName = userProp.Value - case "NAME": - r.Name = userProp.Value - } - } - - err = rows.Err() - - return r, err -} - -type DescribeUserProp struct { - Property string `db:"property"` - Value sql.NullString `db:"value"` -} - -func ListUsers(pattern string, db *sql.DB) ([]User, error) { - stmt := fmt.Sprintf(`SHOW USERS like '%s'`, pattern) - rows, err := Query(db, stmt) - if err != nil { - return nil, err - } - defer rows.Close() - - dbs := []User{} - if err := sqlx.StructScan(rows, &dbs); err != nil { - if errors.Is(err, sql.ErrNoRows) { - log.Println("[DEBUG] no users found") - return nil, nil - } - return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) - } - return dbs, nil -} diff --git a/pkg/snowflake/user_test.go b/pkg/snowflake/user_test.go deleted file mode 100644 index 55b843d633..0000000000 --- a/pkg/snowflake/user_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package snowflake_test - -import ( - "testing" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/stretchr/testify/require" -) - -func TestUser(t *testing.T) { - r := require.New(t) - u := snowflake.NewUserBuilder("user1") - r.NotNil(u) - - q := u.Show() - r.Equal("SHOW USERS LIKE 'user1'", q) - - q = u.Describe() - r.Equal(`DESCRIBE USER "user1"`, q) - - q = u.Drop() - r.Equal(`DROP USER "user1"`, q) - - q = u.Rename("user2") - r.Equal(`ALTER USER "user1" RENAME TO "user2"`, q) - - ab := u.Alter() - r.NotNil(ab) - - ab.SetString(`foo`, `bar`) - q = ab.Statement() - - r.Equal(`ALTER USER "user1" SET FOO='bar'`, q) - - ab.SetBool(`bam`, false) - q = ab.Statement() - - r.Equal(`ALTER USER "user1" SET FOO='bar' BAM=false`, q) - - c := u.Create() - c.SetString("foo", "bar") - c.SetBool("bam", false) - q = c.Statement() - r.Equal(`CREATE USER "user1" FOO='bar' BAM=false`, q) -} From 3596e940c4ffaf6effaa56a491d3d92d96277f57 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 1 Feb 2024 13:37:21 +0100 Subject: [PATCH 02/11] Remove current user from snowflake package --- pkg/datasources/current_role.go | 12 +++-- .../current_role_acceptance_test.go | 10 ++++- pkg/snowflake/current_role.go | 26 ----------- pkg/snowflake/current_role_test.go | 44 ------------------- 4 files changed, 16 insertions(+), 76 deletions(-) delete mode 100644 pkg/snowflake/current_role.go delete mode 100644 pkg/snowflake/current_role_test.go diff --git a/pkg/datasources/current_role.go b/pkg/datasources/current_role.go index 8104a48f12..b5d79d829d 100644 --- a/pkg/datasources/current_role.go +++ b/pkg/datasources/current_role.go @@ -1,11 +1,12 @@ package datasources import ( + "context" "database/sql" "fmt" "log" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -26,15 +27,18 @@ func CurrentRole() *schema.Resource { func ReadCurrentRole(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - role, err := snowflake.ReadCurrentRole(db) + ctx := context.Background() + client := sdk.NewClientFromDB(db) + + role, err := client.ContextFunctions.CurrentRole(ctx) if err != nil { log.Printf("[DEBUG] current_role failed to decode") d.SetId("") return nil } - d.SetId(fmt.Sprintf(role.Role)) - err = d.Set("name", role.Role) + d.SetId(fmt.Sprintf(role)) + err = d.Set("name", role) if err != nil { return err } diff --git a/pkg/datasources/current_role_acceptance_test.go b/pkg/datasources/current_role_acceptance_test.go index 600ce4fe3b..3d0b41ce50 100644 --- a/pkg/datasources/current_role_acceptance_test.go +++ b/pkg/datasources/current_role_acceptance_test.go @@ -3,12 +3,18 @@ package datasources_test import ( "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_CurrentRole(t *testing.T) { - resource.ParallelTest(t, resource.TestCase{ - Providers: providers(), + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, Steps: []resource.TestStep{ { Config: currentRole(), diff --git a/pkg/snowflake/current_role.go b/pkg/snowflake/current_role.go deleted file mode 100644 index 93ca4f2e94..0000000000 --- a/pkg/snowflake/current_role.go +++ /dev/null @@ -1,26 +0,0 @@ -package snowflake - -import ( - "database/sql" - - "github.com/jmoiron/sqlx" -) - -func SelectCurrentRole() string { - return `SELECT CURRENT_ROLE() AS "currentRole";` -} - -type CurrentRole struct { - Role string `db:"currentRole"` -} - -func ScanCurrentRole(row *sqlx.Row) (*CurrentRole, error) { - role := &CurrentRole{} - err := row.StructScan(role) - return role, err -} - -func ReadCurrentRole(db *sql.DB) (*CurrentRole, error) { - row := QueryRow(db, SelectCurrentRole()) - return ScanCurrentRole(row) -} diff --git a/pkg/snowflake/current_role_test.go b/pkg/snowflake/current_role_test.go deleted file mode 100644 index a5cf615ee7..0000000000 --- a/pkg/snowflake/current_role_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package snowflake_test - -import ( - "testing" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/require" -) - -func TestCurrentRoleSelect(t *testing.T) { - r := require.New(t) - r.Equal(`SELECT CURRENT_ROLE() AS "currentRole";`, snowflake.SelectCurrentRole()) -} - -func TestCurrentRoleRead(t *testing.T) { - type testCaseEntry struct { - currentRole string - } - - testCases := map[string]testCaseEntry{ - "name": { - "sys_admin", - }, - } - - for name, testCase := range testCases { - t.Run(name, func(t *testing.T) { - r := require.New(t) - mockDB, mock, err := sqlmock.New() - r.NoError(err) - defer mockDB.Close() - sqlxDB := sqlx.NewDb(mockDB, "sqlmock") - - rows := sqlmock.NewRows([]string{"currentRole"}).AddRow(testCase.currentRole) - mock.ExpectQuery(`SELECT CURRENT_ROLE\(\) AS "currentRole";`).WillReturnRows(rows) - - acc, err := snowflake.ReadCurrentRole(sqlxDB.DB) - r.NoError(err) - r.Equal(testCase.currentRole, acc.Role) - }) - } -} From 01ec3a4b7fab1834706e61c9b474456138915c89 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 1 Feb 2024 14:02:32 +0100 Subject: [PATCH 03/11] Migrate current account datasource --- pkg/datasources/current_account.go | 16 ++++--- .../current_account_acceptance_test.go | 8 +++- pkg/sdk/context_functions.go | 44 +++++++++++++++++++ pkg/sdk/region_mapping.go | 41 +++++++++++++++++ .../context_functions_integration_test.go | 14 ++++++ 5 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 pkg/sdk/region_mapping.go diff --git a/pkg/datasources/current_account.go b/pkg/datasources/current_account.go index f375b793c5..878e728e64 100644 --- a/pkg/datasources/current_account.go +++ b/pkg/datasources/current_account.go @@ -1,11 +1,12 @@ package datasources import ( + "context" "database/sql" "fmt" "log" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -40,23 +41,26 @@ func CurrentAccount() *schema.Resource { // ReadCurrentAccount read the current snowflake account information. func ReadCurrentAccount(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - acc, err := snowflake.ReadCurrentAccount(db) + ctx := context.Background() + client := sdk.NewClientFromDB(db) + + current, err := client.ContextFunctions.Current(ctx) if err != nil { log.Println("[DEBUG] current_account failed to decode") d.SetId("") return nil } - d.SetId(fmt.Sprintf("%s.%s", acc.Account, acc.Region)) - accountErr := d.Set("account", acc.Account) + d.SetId(fmt.Sprintf("%s.%s", current.Account, current.Region)) + accountErr := d.Set("account", current.Account) if accountErr != nil { return accountErr } - regionErr := d.Set("region", acc.Region) + regionErr := d.Set("region", current.Region) if regionErr != nil { return regionErr } - url, err := acc.AccountURL() + url, err := current.AccountURL() if err != nil { log.Println("[DEBUG] generating snowflake url failed") return nil diff --git a/pkg/datasources/current_account_acceptance_test.go b/pkg/datasources/current_account_acceptance_test.go index e6cd2337f7..84801d486e 100644 --- a/pkg/datasources/current_account_acceptance_test.go +++ b/pkg/datasources/current_account_acceptance_test.go @@ -3,12 +3,18 @@ package datasources_test import ( "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_CurrentAccount(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ - Providers: providers(), + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, CheckDestroy: nil, Steps: []resource.TestStep{ { diff --git a/pkg/sdk/context_functions.go b/pkg/sdk/context_functions.go index c8ee69ef4c..d8783746b9 100644 --- a/pkg/sdk/context_functions.go +++ b/pkg/sdk/context_functions.go @@ -8,6 +8,7 @@ import ( "strings" ) +// TODO [SNOW-]: add generic select type ContextFunctions interface { // Session functions. CurrentAccount(ctx context.Context) (string, error) @@ -16,6 +17,7 @@ type ContextFunctions interface { CurrentRegion(ctx context.Context) (string, error) CurrentSession(ctx context.Context) (string, error) CurrentUser(ctx context.Context) (string, error) + Current(ctx context.Context) (*CurrentDetails, error) // Session Object functions. CurrentDatabase(ctx context.Context) (string, error) @@ -30,6 +32,33 @@ type contextFunctions struct { client *Client } +type currentDetailsDBRow struct { + CurrentAccount string `db:"CURRENT_ACCOUNT"` + CurrentRole string `db:"CURRENT_ROLE"` + CurrentRegion string `db:"CURRENT_REGION"` + CurrentSession string `db:"CURRENT_SESSION"` + CurrentUser string `db:"CURRENT_USER"` +} + +type CurrentDetails struct { + Account string `db:"CURRENT_ACCOUNT"` + Role string `db:"CURRENT_ROLE"` + Region string `db:"CURRENT_REGION"` + Session string `db:"CURRENT_SESSION"` + User string `db:"CURRENT_USER"` +} + +func (acc *CurrentDetails) AccountURL() (string, error) { + if regionID, ok := regionMapping[strings.ToLower(acc.Region)]; ok { + accountID := acc.Account + if len(regionID) > 0 { + accountID = fmt.Sprintf("%s.%s", accountID, regionID) + } + return fmt.Sprintf("https://%s.snowflakecomputing.com", accountID), nil + } + return "", fmt.Errorf("failed to map Snowflake account region %s to a region_id", acc.Region) +} + func (c *contextFunctions) CurrentAccount(ctx context.Context) (string, error) { s := &struct { CurrentAccount string `db:"CURRENT_ACCOUNT"` @@ -130,6 +159,21 @@ func (c *contextFunctions) CurrentUser(ctx context.Context) (string, error) { return s.CurrentUser, nil } +func (c *contextFunctions) Current(ctx context.Context) (*CurrentDetails, error) { + s := ¤tDetailsDBRow{} + err := c.client.queryOne(ctx, s, "SELECT CURRENT_ACCOUNT() as CURRENT_ACCOUNT, CURRENT_ROLE() as CURRENT_ROLE, CURRENT_REGION() AS CURRENT_REGION, CURRENT_SESSION() as CURRENT_SESSION, CURRENT_USER() as CURRENT_USER") + if err != nil { + return nil, err + } + return &CurrentDetails{ + Account: s.CurrentAccount, + Role: s.CurrentRole, + Region: s.CurrentRegion, + Session: s.CurrentSession, + User: s.CurrentUser, + }, nil +} + func (c *contextFunctions) CurrentDatabase(ctx context.Context) (string, error) { s := &struct { CurrentDatabase sql.NullString `db:"CURRENT_DATABASE"` diff --git a/pkg/sdk/region_mapping.go b/pkg/sdk/region_mapping.go new file mode 100644 index 0000000000..20d060f930 --- /dev/null +++ b/pkg/sdk/region_mapping.go @@ -0,0 +1,41 @@ +package sdk + +// taken from https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#snowflake-region-ids +var regionMapping = map[string]string{ + "aws_us_west_2": "", // left black as this is the default + "aws_us_east_2": "us-east-2.aws", + "aws_us_east_1": "us-east-1", + "aws_us_east_1_gov": "us-east-1-gov.aws", + "aws_ca_central_1": "ca-central-1.aws", + "aws_sa_east_1": "sa-east-1.aws", + "aws_eu_west_1": "eu-west-1", + "aws_eu_west_2": "eu-west-2.aws", + "aws_eu_west_3": "eu-west-3.aws", + "aws_eu_central_1": "eu-central-1", + "aws_eu_north_1": "eu-north-1.aws", + "aws_ap_northeast_1": "ap-northeast-1.aws", + "aws_ap_northeast_2": "ap-northeast-2.aws", + "aws_ap_northeast_3": "ap-northeast-3.aws", + "aws_ap_south_1": "ap-south-1.aws", + "aws_ap_southeast_1": "ap-southeast-1", + "aws_ap_southeast_2": "ap-southeast-2", + "gcp_us_central1": "us-central1.gcp", + "gcp_us_east4": "us-east4.gcp", + "gcp_europe_west2": "europe-west2.gcp", + "gcp_europe_west4": "europe-west4.gcp", + "azure_westus2": "west-us-2.azure", + "azure_centralus": "central-us.azure", + "azure_southcentralus": "south-central-us.azure", + "azure_eastus2": "east-us-2.azure", + "azure_usgovvirginia": "us-gov-virginia.azure", + "azure_canadacentral": "canada-central.azure", + "azure_uksouth": "uk-south.azure", + "azure_northeurope": "north-europe.azure", + "azure_westeurope": "west-europe.azure", + "azure_southeastasia": "southeast-asia.azure", + "azure_switzerlandnorth": "switzerland-north.azure", + "azure_uaenorth": "uae-north.azure", + "azure_centralindia": "central-india.azure", + "azure_japaneast": "japan-east.azure", + "azure_australiaeast": "australia-east.azure", +} diff --git a/pkg/sdk/testint/context_functions_integration_test.go b/pkg/sdk/testint/context_functions_integration_test.go index 12906bae8c..2a86acdd1d 100644 --- a/pkg/sdk/testint/context_functions_integration_test.go +++ b/pkg/sdk/testint/context_functions_integration_test.go @@ -49,6 +49,20 @@ func TestInt_CurrentUser(t *testing.T) { assert.NotEmpty(t, user) } +func TestInt_Current(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + account, err := client.ContextFunctions.Current(ctx) + require.NoError(t, err) + assert.NotNil(t, account) + assert.NotEmpty(t, account.Account) + assert.NotEmpty(t, account.Role) + assert.NotEmpty(t, account.Region) + assert.NotEmpty(t, account.Session) + assert.NotEmpty(t, account.User) +} + func TestInt_CurrentDatabase(t *testing.T) { client := testClient(t) ctx := testContext(t) From 508a01973fa8c3bd859147ab7fcaaaca7256a151 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 2 Feb 2024 10:01:48 +0100 Subject: [PATCH 04/11] Migrate resource monitor datasource --- pkg/datasources/resource_monitors.go | 35 +-- .../resource_monitors_acceptance_test.go | 12 +- .../row_access_policies_acceptance_test.go | 12 +- pkg/snowflake/resource_monitor.go | 278 ------------------ pkg/snowflake/resource_monitor_test.go | 60 ---- 5 files changed, 35 insertions(+), 362 deletions(-) delete mode 100644 pkg/snowflake/resource_monitor.go delete mode 100644 pkg/snowflake/resource_monitor_test.go diff --git a/pkg/datasources/resource_monitors.go b/pkg/datasources/resource_monitors.go index 502a96cc1e..8bf969fba5 100644 --- a/pkg/datasources/resource_monitors.go +++ b/pkg/datasources/resource_monitors.go @@ -1,12 +1,12 @@ package datasources import ( + "context" "database/sql" - "errors" "fmt" "log" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -49,8 +49,10 @@ func ResourceMonitors() *schema.Resource { func ReadResourceMonitors(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + client := sdk.NewClientFromDB(db) + ctx := context.Background() - account, err := snowflake.ReadCurrentAccount(db) + account, err := client.ContextFunctions.Current(ctx) if err != nil { log.Print("[DEBUG] unable to retrieve current account") d.SetId("") @@ -59,29 +61,22 @@ func ReadResourceMonitors(d *schema.ResourceData, meta interface{}) error { d.SetId(fmt.Sprintf("%s.%s", account.Account, account.Region)) - currentResourceMonitors, err := snowflake.ListResourceMonitors(db) - if errors.Is(err, sql.ErrNoRows) { - // If not found, mark resource to be removed from state file during apply or refresh - log.Printf("[DEBUG] no resource monitors found in account (%s)", d.Id()) - d.SetId("") - return nil - } else if err != nil { + extractedResourceMonitors, err := client.ResourceMonitors.Show(ctx, &sdk.ShowResourceMonitorOptions{}) + if err != nil { log.Printf("[DEBUG] unable to parse resource monitors in account (%s)", d.Id()) d.SetId("") return nil } - resourceMonitors := []map[string]interface{}{} - - for _, resourceMonitor := range currentResourceMonitors { - resourceMonitorMap := map[string]interface{}{} - - resourceMonitorMap["name"] = resourceMonitor.Name.String - resourceMonitorMap["frequency"] = resourceMonitor.Frequency.String - resourceMonitorMap["credit_quota"] = resourceMonitor.CreditQuota.String - resourceMonitorMap["comment"] = resourceMonitor.Comment.String + resourceMonitors := make([]map[string]any, len(extractedResourceMonitors)) - resourceMonitors = append(resourceMonitors, resourceMonitorMap) + for i, resourceMonitor := range extractedResourceMonitors { + resourceMonitors[i] = map[string]any{ + "name": resourceMonitor.Name, + "frequency": resourceMonitor.Frequency, + "credit_quota": fmt.Sprintf("%f", resourceMonitor.CreditQuota), + "comment": resourceMonitor.Comment, + } } return d.Set("resource_monitors", resourceMonitors) diff --git a/pkg/datasources/resource_monitors_acceptance_test.go b/pkg/datasources/resource_monitors_acceptance_test.go index 7788e6c41c..38b6cb260a 100644 --- a/pkg/datasources/resource_monitors_acceptance_test.go +++ b/pkg/datasources/resource_monitors_acceptance_test.go @@ -5,14 +5,22 @@ import ( "strings" "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_ResourceMonitors(t *testing.T) { resourceMonitorName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - resource.ParallelTest(t, resource.TestCase{ - Providers: providers(), + + 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{ { diff --git a/pkg/datasources/row_access_policies_acceptance_test.go b/pkg/datasources/row_access_policies_acceptance_test.go index fe1528bea8..cb58fe0b05 100644 --- a/pkg/datasources/row_access_policies_acceptance_test.go +++ b/pkg/datasources/row_access_policies_acceptance_test.go @@ -5,16 +5,24 @@ import ( "strings" "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_RowAccessPolicies(t *testing.T) { databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) rowAccessPolicyName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - resource.ParallelTest(t, resource.TestCase{ - Providers: providers(), + + 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{ { diff --git a/pkg/snowflake/resource_monitor.go b/pkg/snowflake/resource_monitor.go deleted file mode 100644 index 1fa079a777..0000000000 --- a/pkg/snowflake/resource_monitor.go +++ /dev/null @@ -1,278 +0,0 @@ -package snowflake - -import ( - "database/sql" - "errors" - "fmt" - "log" - "strings" - - "github.com/jmoiron/sqlx" -) - -// ResourceMonitorBuilder extends the generic builder to provide support for triggers. -type ResourceMonitorBuilder struct { - Builder -} - -// ResourceMonitor returns a pointer to a ResourceMonitorBuilder that abstracts the DDL operations for a resource monitor. -// -// Supported DDL operations are: -// - CREATE RESOURCE MONITOR -// - ALTER RESOURCE MONITOR -// - DROP RESOURCE MONITOR -// - SHOW RESOURCE MONITOR -// -// [Snowflake Reference](https://docs.snowflake.net/manuals/user-guide/resource-monitors.html#ddl-for-resource-monitors) -func NewResourceMonitorBuilder(name string) *ResourceMonitorBuilder { - return &ResourceMonitorBuilder{ - Builder{ - entityType: ResourceMonitorType, - name: name, - }, - } -} - -// @TODO support for a ResourceMonitorAlterBuilder so that we can alter triggers - -// ResourceMonitorCreateBuilder extends the generic create builder to provide support for triggers. -type ResourceMonitorCreateBuilder struct { - CreateBuilder - - // triggers consist of the type (DO SUSPEND | SUSPEND_IMMEDIATE | NOTIFY) and - // the threshold (a percentage value) - triggers []trigger -} - -type ResourceMonitorAlterBuilder struct { - AlterPropertiesBuilder - - // triggers consist of the type (DO SUSPEND | SUSPEND_IMMEDIATE | NOTIFY) and - // the threshold (a percentage value) - triggers []trigger -} - -type action string - -type trigger struct { - action action - threshold int -} - -const ( - // SuspendTrigger suspends all assigned warehouses while allowing currently running queries to complete. - SuspendTrigger action = "SUSPEND" - // SuspendImmediatelyTrigger suspends all assigned warehouses immediately and cancel any currently running queries or statements using the warehouses. - SuspendImmediatelyTrigger action = "SUSPEND_IMMEDIATE" - // NotifyTrigger sends an alert (to all users who have enabled notifications for themselves), but do not take any other action. - NotifyTrigger action = "NOTIFY" -) - -// Create returns a pointer to a ResourceMonitorCreateBuilder. -func (rb *ResourceMonitorBuilder) Create() *ResourceMonitorCreateBuilder { - return &ResourceMonitorCreateBuilder{ - CreateBuilder{ - name: rb.name, - entityType: rb.entityType, - stringProperties: make(map[string]string), - boolProperties: make(map[string]bool), - intProperties: make(map[string]int), - floatProperties: make(map[string]float64), - stringListProperties: make(map[string][]string), - }, - make([]trigger, 0), - } -} - -// NotifyAt adds a notify trigger at the specified percentage threshold. -func (rcb *ResourceMonitorCreateBuilder) NotifyAt(pct int) *ResourceMonitorCreateBuilder { - rcb.triggers = append(rcb.triggers, trigger{NotifyTrigger, pct}) - return rcb -} - -// SuspendAt adds a suspend trigger at the specified percentage threshold. -func (rcb *ResourceMonitorCreateBuilder) SuspendAt(pct int) *ResourceMonitorCreateBuilder { - rcb.triggers = append(rcb.triggers, trigger{SuspendTrigger, pct}) - return rcb -} - -// SuspendImmediatelyAt adds a suspend immediately trigger at the specified percentage threshold. -func (rcb *ResourceMonitorCreateBuilder) SuspendImmediatelyAt(pct int) *ResourceMonitorCreateBuilder { - rcb.triggers = append(rcb.triggers, trigger{SuspendImmediatelyTrigger, pct}) - return rcb -} - -// Statement returns the SQL statement needed to actually create the resource. -func (rcb *ResourceMonitorCreateBuilder) Statement() string { - var sb strings.Builder - sb.WriteString(fmt.Sprintf(`CREATE %v "%v"`, rcb.entityType, rcb.name)) - - for k, v := range rcb.stringProperties { - sb.WriteString(fmt.Sprintf(` %v='%v'`, strings.ToUpper(k), EscapeString(v))) - } - - for k, v := range rcb.intProperties { - sb.WriteString(fmt.Sprintf(` %v=%d`, strings.ToUpper(k), v)) - } - - for k, v := range rcb.floatProperties { - sb.WriteString(fmt.Sprintf(` %v=%.2f`, strings.ToUpper(k), v)) - } - - for k, v := range rcb.stringListProperties { - sb.WriteString(fmt.Sprintf(" %s=%s", strings.ToUpper(k), formatStringList(v))) - } - - if len(rcb.triggers) > 0 { - sb.WriteString(" TRIGGERS") - } - - for _, trig := range rcb.triggers { - sb.WriteString(fmt.Sprintf(` ON %d PERCENT DO %v`, trig.threshold, trig.action)) - } - - return sb.String() -} - -// SetOnAccount returns the SQL query that will set the resource monitor globally on your Snowflake account. -func (rcb *ResourceMonitorCreateBuilder) SetOnAccount() string { - return fmt.Sprintf(`ALTER ACCOUNT SET RESOURCE_MONITOR = "%v"`, rcb.name) -} - -// SetOnWarehouse returns the SQL query that will set the resource monitor on the specified warehouse. -func (rcb *ResourceMonitorCreateBuilder) SetOnWarehouse(warehouse string) string { - return fmt.Sprintf(`ALTER WAREHOUSE "%v" SET RESOURCE_MONITOR = "%v"`, warehouse, rcb.name) -} - -// Alter returns a pointer to a ResourceMonitorAlterBuilder. -func (rb *ResourceMonitorBuilder) Alter() *ResourceMonitorAlterBuilder { - return &ResourceMonitorAlterBuilder{ - AlterPropertiesBuilder{ - name: rb.name, - entityType: rb.entityType, - stringProperties: make(map[string]string), - boolProperties: make(map[string]bool), - intProperties: make(map[string]int), - floatProperties: make(map[string]float64), - stringListProperties: make(map[string][]string), - }, - make([]trigger, 0), - } -} - -// NotifyAt adds a notify trigger at the specified percentage threshold. -func (rcb *ResourceMonitorAlterBuilder) NotifyAt(pct int) *ResourceMonitorAlterBuilder { - rcb.triggers = append(rcb.triggers, trigger{NotifyTrigger, pct}) - return rcb -} - -// SuspendAt adds a suspend trigger at the specified percentage threshold. -func (rcb *ResourceMonitorAlterBuilder) SuspendAt(pct int) *ResourceMonitorAlterBuilder { - rcb.triggers = append(rcb.triggers, trigger{SuspendTrigger, pct}) - return rcb -} - -// SuspendImmediatelyAt adds a suspend immediately trigger at the specified percentage threshold. -func (rcb *ResourceMonitorAlterBuilder) SuspendImmediatelyAt(pct int) *ResourceMonitorAlterBuilder { - rcb.triggers = append(rcb.triggers, trigger{SuspendImmediatelyTrigger, pct}) - return rcb -} - -// Statement returns the SQL statement needed to actually alter the resource. -func (rcb *ResourceMonitorAlterBuilder) Statement() string { - var sb strings.Builder - sb.WriteString(fmt.Sprintf(`ALTER %v "%v" SET`, rcb.entityType, rcb.name)) - - for k, v := range rcb.stringProperties { - sb.WriteString(fmt.Sprintf(` %v='%v'`, strings.ToUpper(k), EscapeString(v))) - } - - for k, v := range rcb.intProperties { - sb.WriteString(fmt.Sprintf(` %v=%d`, strings.ToUpper(k), v)) - } - - for k, v := range rcb.floatProperties { - sb.WriteString(fmt.Sprintf(` %v=%.2f`, strings.ToUpper(k), v)) - } - - for k, v := range rcb.stringListProperties { - sb.WriteString(fmt.Sprintf(" %s=%s", strings.ToUpper(k), formatStringList(v))) - } - - // If the only change is the trigges, then we do not need to add the SET keyword - if strings.HasSuffix(sb.String(), "SET") { - sb.Reset() - sb.WriteString(fmt.Sprintf(`ALTER %v "%v"`, rcb.entityType, rcb.name)) - } - if len(rcb.triggers) > 0 { - sb.WriteString(" TRIGGERS") - } - - for _, trig := range rcb.triggers { - sb.WriteString(fmt.Sprintf(` ON %d PERCENT DO %v`, trig.threshold, trig.action)) - } - - return sb.String() -} - -// SetOnAccount returns the SQL query that will set the resource monitor globally on your Snowflake account. -func (rcb *ResourceMonitorAlterBuilder) SetOnAccount() string { - return fmt.Sprintf(`ALTER ACCOUNT SET RESOURCE_MONITOR = "%v"`, rcb.name) -} - -func (rcb *ResourceMonitorAlterBuilder) UnsetOnAccount() string { - return `ALTER ACCOUNT SET RESOURCE_MONITOR = NULL` -} - -// SetOnWarehouse returns the SQL query that will set the resource monitor on the specified warehouse. -func (rcb *ResourceMonitorAlterBuilder) SetOnWarehouse(warehouse string) string { - return fmt.Sprintf(`ALTER WAREHOUSE "%v" SET RESOURCE_MONITOR = "%v"`, warehouse, rcb.name) -} - -// UnsetOnWarehouse returns the SQL query that will unset the resource monitor on the specified warehouse. -func (rcb *ResourceMonitorAlterBuilder) UnsetOnWarehouse(warehouse string) string { - return fmt.Sprintf(`ALTER WAREHOUSE "%v" SET RESOURCE_MONITOR = NULL`, warehouse) -} - -type ResourceMonitor struct { - Name sql.NullString `db:"name"` - CreditQuota sql.NullString `db:"credit_quota"` - UsedCredits sql.NullString `db:"used_credits"` - RemainingCredits sql.NullString `db:"remaining_credits"` - Level sql.NullString `db:"level"` - Frequency sql.NullString `db:"frequency"` - StartTime sql.NullString `db:"start_time"` - EndTime sql.NullString `db:"end_time"` - NotifyAt sql.NullString `db:"notify_at"` - SuspendAt sql.NullString `db:"suspend_at"` - SuspendImmediatelyAt sql.NullString `db:"suspend_immediately_at"` - CreatedOn sql.NullString `db:"created_on"` - Owner sql.NullString `db:"owner"` - Comment sql.NullString `db:"comment"` - NotifyUsers sql.NullString `db:"notify_users"` -} - -func ScanResourceMonitor(row *sqlx.Row) (*ResourceMonitor, error) { - rm := &ResourceMonitor{} - err := row.StructScan(rm) - return rm, err -} - -func ListResourceMonitors(db *sql.DB) ([]ResourceMonitor, error) { - stmt := "SHOW RESOURCE MONITORS" - rows, err := Query(db, stmt) - if err != nil { - return nil, err - } - defer rows.Close() - - dbs := []ResourceMonitor{} - if err := sqlx.StructScan(rows, &dbs); err != nil { - if errors.Is(err, sql.ErrNoRows) { - log.Println("[DEBUG] no resource monitors found") - return nil, nil - } - return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) - } - return dbs, nil -} diff --git a/pkg/snowflake/resource_monitor_test.go b/pkg/snowflake/resource_monitor_test.go deleted file mode 100644 index 1d2530ce25..0000000000 --- a/pkg/snowflake/resource_monitor_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package snowflake_test - -import ( - "testing" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/stretchr/testify/require" -) - -func TestResourceMonitor(t *testing.T) { - r := require.New(t) - rm := snowflake.NewResourceMonitorBuilder("resource_monitor") - r.NotNil(rm) - - q := rm.Show() - r.Equal(`SHOW RESOURCE MONITORS LIKE 'resource_monitor'`, q) - - q = rm.Create().Statement() - r.Equal(`CREATE RESOURCE MONITOR "resource_monitor"`, q) - - q = rm.Drop() - r.Equal(`DROP RESOURCE MONITOR "resource_monitor"`, q) - - ab := rm.Alter() - ab.SetInt("credit_quota", 66) - q = ab.Statement() - r.Equal(`ALTER RESOURCE MONITOR "resource_monitor" SET CREDIT_QUOTA=66`, q) - - ab = rm.Alter() - ab.SetStringList("notify_users", []string{"USERONE", "USERTWO"}) - q = ab.Statement() - r.Equal(`ALTER RESOURCE MONITOR "resource_monitor" SET NOTIFY_USERS=('USERONE', 'USERTWO')`, q) - - cb := snowflake.NewResourceMonitorBuilder("resource_monitor").Create() - cb.NotifyAt(80).NotifyAt(90).SuspendAt(95).SuspendImmediatelyAt(100) - cb.SetString("frequency", "YEARLY") - - cb.SetInt("credit_quota", 666) - cb.SetStringList("notify_users", []string{"USERONE", "USERTWO"}) - q = cb.Statement() - r.Equal(`CREATE RESOURCE MONITOR "resource_monitor" FREQUENCY='YEARLY' CREDIT_QUOTA=666 NOTIFY_USERS=('USERONE', 'USERTWO') TRIGGERS ON 80 PERCENT DO NOTIFY ON 90 PERCENT DO NOTIFY ON 95 PERCENT DO SUSPEND ON 100 PERCENT DO SUSPEND_IMMEDIATE`, q) -} - -func TestResourceMonitorSetOnAccount(t *testing.T) { - r := require.New(t) - s := snowflake.NewResourceMonitorBuilder("test_resource_monitor") - r.NotNil(s) - - q := s.Create().SetOnAccount() - r.Equal(`ALTER ACCOUNT SET RESOURCE_MONITOR = "test_resource_monitor"`, q) -} - -func TestResourceMonitorSetOnWarehouse(t *testing.T) { - r := require.New(t) - s := snowflake.NewResourceMonitorBuilder("test_resource_monitor") - r.NotNil(s) - - q := s.Create().SetOnWarehouse("test_warehouse") - r.Equal(`ALTER WAREHOUSE "test_warehouse" SET RESOURCE_MONITOR = "test_resource_monitor"`, q) -} From 4b5d37378814f8ef481418b55093049f9c0e4c4f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 2 Feb 2024 10:10:23 +0100 Subject: [PATCH 05/11] Migrate role datasource --- pkg/datasources/role.go | 20 ++++++++++---------- pkg/datasources/role_acceptance_test.go | 10 +++++++++- pkg/datasources/streams.go | 1 - pkg/snowflake/role.go | 11 ----------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/pkg/datasources/role.go b/pkg/datasources/role.go index 815624ee4a..ee215f6b33 100644 --- a/pkg/datasources/role.go +++ b/pkg/datasources/role.go @@ -1,11 +1,11 @@ package datasources import ( + "context" "database/sql" - "errors" "log" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -37,23 +37,23 @@ func Role() *schema.Resource { // ReadRole Reads the database metadata information. func ReadRole(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + client := sdk.NewClientFromDB(db) + ctx := context.Background() + roleName := d.Get("name").(string) - role, err := snowflake.NewRoleBuilder(db, roleName).Show() - if errors.Is(err, sql.ErrNoRows) { + role, err := client.Roles.ShowByID(ctx, sdk.NewShowByIdRoleRequest(sdk.NewAccountObjectIdentifier(roleName))) + if err != nil { log.Printf("[DEBUG] role (%s) not found", roleName) d.SetId("") return nil } - if err != nil { - return err - } - d.SetId(role.Name.String) - if err := d.Set("name", role.Name.String); err != nil { + d.SetId(role.Name) + if err := d.Set("name", role.Name); err != nil { return err } - if err := d.Set("comment", role.Comment.String); err != nil { + if err := d.Set("comment", role.Comment); err != nil { return err } return nil diff --git a/pkg/datasources/role_acceptance_test.go b/pkg/datasources/role_acceptance_test.go index 8c16c3537c..fff163b602 100644 --- a/pkg/datasources/role_acceptance_test.go +++ b/pkg/datasources/role_acceptance_test.go @@ -5,15 +5,23 @@ import ( "strings" "testing" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) func TestAcc_Role(t *testing.T) { roleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) comment := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + resource.ParallelTest(t, resource.TestCase{ - Providers: providers(), + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, CheckDestroy: nil, Steps: []resource.TestStep{ { diff --git a/pkg/datasources/streams.go b/pkg/datasources/streams.go index cd355f8b72..5209cab663 100644 --- a/pkg/datasources/streams.go +++ b/pkg/datasources/streams.go @@ -7,7 +7,6 @@ import ( "log" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) diff --git a/pkg/snowflake/role.go b/pkg/snowflake/role.go index f3c56b4bdf..9d465886a7 100644 --- a/pkg/snowflake/role.go +++ b/pkg/snowflake/role.go @@ -34,11 +34,6 @@ func (b *RoleBuilder) WithComment(comment string) *RoleBuilder { return b } -func (b *RoleBuilder) WithTags(tags []TagValue) *RoleBuilder { - b.tags = tags - return b -} - func (b *RoleBuilder) Create() error { q := strings.Builder{} q.WriteString(fmt.Sprintf(`CREATE ROLE "%v"`, b.name)) @@ -83,12 +78,6 @@ func (b *RoleBuilder) SetTag(tag TagValue) error { return err } -func (b *RoleBuilder) ChangeTag(tag TagValue) error { - q := fmt.Sprintf(`ALTER ROLE "%s" SET TAG "%v"."%v"."%v" = "%v"`, b.name, tag.Database, tag.Schema, tag.Name, tag.Value) - _, err := b.db.Exec(q) - return err -} - func (b *RoleBuilder) Drop() error { q := fmt.Sprintf(`DROP ROLE "%s"`, b.name) _, err := b.db.Exec(q) From 32c0376a358687773698946a48af31d0894586a1 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 2 Feb 2024 10:17:04 +0100 Subject: [PATCH 06/11] Remove current account logic from snowflake package --- .../system_get_snowflake_platform_info.go | 6 +- pkg/datasources/warehouses.go | 3 +- pkg/snowflake/current_account.go | 80 ------------------- pkg/snowflake/current_account_test.go | 73 ----------------- 4 files changed, 6 insertions(+), 156 deletions(-) delete mode 100644 pkg/snowflake/current_account.go delete mode 100644 pkg/snowflake/current_account_test.go diff --git a/pkg/datasources/system_get_snowflake_platform_info.go b/pkg/datasources/system_get_snowflake_platform_info.go index e2185f4acc..d59d15b6ea 100644 --- a/pkg/datasources/system_get_snowflake_platform_info.go +++ b/pkg/datasources/system_get_snowflake_platform_info.go @@ -1,11 +1,13 @@ package datasources import ( + "context" "database/sql" "errors" "fmt" "log" + "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/helper/schema" ) @@ -35,10 +37,12 @@ func SystemGetSnowflakePlatformInfo() *schema.Resource { // ReadSystemGetSnowflakePlatformInfo implements schema.ReadFunc. func ReadSystemGetSnowflakePlatformInfo(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + client := sdk.NewClientFromDB(db) + sel := snowflake.SystemGetSnowflakePlatformInfoQuery() row := snowflake.QueryRow(db, sel) - acc, err := snowflake.ReadCurrentAccount(db) + acc, err := client.ContextFunctions.Current(context.Background()) if err != nil { // If not found, mark resource to be removed from state file during apply or refresh d.SetId("") diff --git a/pkg/datasources/warehouses.go b/pkg/datasources/warehouses.go index 96cb136106..7583b96890 100644 --- a/pkg/datasources/warehouses.go +++ b/pkg/datasources/warehouses.go @@ -6,7 +6,6 @@ import ( "fmt" "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/helper/schema" ) @@ -62,7 +61,7 @@ func ReadWarehouses(d *schema.ResourceData, meta interface{}) error { client := sdk.NewClientFromDB(db) ctx := context.Background() - account, err := snowflake.ReadCurrentAccount(db) + account, err := client.ContextFunctions.Current(ctx) if err != nil { d.SetId("") return nil diff --git a/pkg/snowflake/current_account.go b/pkg/snowflake/current_account.go deleted file mode 100644 index c3540b5a71..0000000000 --- a/pkg/snowflake/current_account.go +++ /dev/null @@ -1,80 +0,0 @@ -package snowflake - -import ( - "database/sql" - "fmt" - "strings" - - "github.com/jmoiron/sqlx" -) - -// taken from https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#snowflake-region-ids -var regionMapping = map[string]string{ - "aws_us_west_2": "", // left black as this is the default - "aws_us_east_2": "us-east-2.aws", - "aws_us_east_1": "us-east-1", - "aws_us_east_1_gov": "us-east-1-gov.aws", - "aws_ca_central_1": "ca-central-1.aws", - "aws_sa_east_1": "sa-east-1.aws", - "aws_eu_west_1": "eu-west-1", - "aws_eu_west_2": "eu-west-2.aws", - "aws_eu_west_3": "eu-west-3.aws", - "aws_eu_central_1": "eu-central-1", - "aws_eu_north_1": "eu-north-1.aws", - "aws_ap_northeast_1": "ap-northeast-1.aws", - "aws_ap_northeast_2": "ap-northeast-2.aws", - "aws_ap_northeast_3": "ap-northeast-3.aws", - "aws_ap_south_1": "ap-south-1.aws", - "aws_ap_southeast_1": "ap-southeast-1", - "aws_ap_southeast_2": "ap-southeast-2", - "gcp_us_central1": "us-central1.gcp", - "gcp_us_east4": "us-east4.gcp", - "gcp_europe_west2": "europe-west2.gcp", - "gcp_europe_west4": "europe-west4.gcp", - "azure_westus2": "west-us-2.azure", - "azure_centralus": "central-us.azure", - "azure_southcentralus": "south-central-us.azure", - "azure_eastus2": "east-us-2.azure", - "azure_usgovvirginia": "us-gov-virginia.azure", - "azure_canadacentral": "canada-central.azure", - "azure_uksouth": "uk-south.azure", - "azure_northeurope": "north-europe.azure", - "azure_westeurope": "west-europe.azure", - "azure_southeastasia": "southeast-asia.azure", - "azure_switzerlandnorth": "switzerland-north.azure", - "azure_uaenorth": "uae-north.azure", - "azure_centralindia": "central-india.azure", - "azure_japaneast": "japan-east.azure", - "azure_australiaeast": "australia-east.azure", -} - -func SelectCurrentAccount() string { - return `SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";` -} - -type CurrentAccount struct { - Account string `db:"account"` - Region string `db:"region"` -} - -func ScanCurrentAccount(row *sqlx.Row) (*CurrentAccount, error) { - acc := &CurrentAccount{} - err := row.StructScan(acc) - return acc, err -} - -func ReadCurrentAccount(db *sql.DB) (*CurrentAccount, error) { - row := QueryRow(db, SelectCurrentAccount()) - return ScanCurrentAccount(row) -} - -func (acc *CurrentAccount) AccountURL() (string, error) { - if regionID, ok := regionMapping[strings.ToLower(acc.Region)]; ok { - accountID := acc.Account - if len(regionID) > 0 { - accountID = fmt.Sprintf("%s.%s", accountID, regionID) - } - return fmt.Sprintf("https://%s.snowflakecomputing.com", accountID), nil - } - return "", fmt.Errorf("failed to map Snowflake account region %s to a region_id", acc.Region) -} diff --git a/pkg/snowflake/current_account_test.go b/pkg/snowflake/current_account_test.go deleted file mode 100644 index 0273157f21..0000000000 --- a/pkg/snowflake/current_account_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package snowflake_test - -import ( - "testing" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/jmoiron/sqlx" - "github.com/stretchr/testify/require" -) - -func TestCurrentAccountSelect(t *testing.T) { - r := require.New(t) - r.Equal(`SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";`, snowflake.SelectCurrentAccount()) -} - -func TestCurrentAccountRead(t *testing.T) { - type testCaseEntry struct { - account string - region string - url string - } - - testCases := map[string]testCaseEntry{ - "aws oregon": { - "ab1234", - "AWS_US_WEST_2", - "https://ab1234.snowflakecomputing.com", - }, - "aws n virginia": { - "cd5678", - "AWS_US_EAST_1", - "https://cd5678.us-east-1.snowflakecomputing.com", - }, - "aws canada central": { - "ef9012", - "AWS_CA_CENTRAL_1", - "https://ef9012.ca-central-1.aws.snowflakecomputing.com", - }, - "gcp canada central": { - "gh3456", - "gcp_us_central1", - "https://gh3456.us-central1.gcp.snowflakecomputing.com", - }, - "azure washington": { - "ij7890", - "azure_westus2", - "https://ij7890.west-us-2.azure.snowflakecomputing.com", - }, - } - - for name, tc := range testCases { - tc := tc - t.Run(name, func(t *testing.T) { - r := require.New(t) - mockDB, mock, err := sqlmock.New() - r.NoError(err) - defer mockDB.Close() - sqlxDB := sqlx.NewDb(mockDB, "sqlmock") - - rows := sqlmock.NewRows([]string{"account", "region"}).AddRow(tc.account, tc.region) - mock.ExpectQuery(`SELECT CURRENT_ACCOUNT\(\) AS "account", CURRENT_REGION\(\) AS "region";`).WillReturnRows(rows) - - acc, err := snowflake.ReadCurrentAccount(sqlxDB.DB) - r.NoError(err) - r.Equal(tc.account, acc.Account) - r.Equal(tc.region, acc.Region) - url, err := acc.AccountURL() - r.NoError(err) - r.Equal(tc.url, url) - }) - } -} From 87b27c0108079015497e5fcc4b728357697bfe15 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 2 Feb 2024 10:25:21 +0100 Subject: [PATCH 07/11] Remove unused logic from snowflake package --- pkg/snowflake/generic.go | 13 +------ pkg/snowflake/identifier.go | 8 ---- pkg/snowflake/parser_internal_test.go | 3 -- pkg/snowflake/replication.go | 56 --------------------------- pkg/snowflake/tag.go | 24 +----------- 5 files changed, 2 insertions(+), 102 deletions(-) delete mode 100644 pkg/snowflake/parser_internal_test.go delete mode 100644 pkg/snowflake/replication.go diff --git a/pkg/snowflake/generic.go b/pkg/snowflake/generic.go index 5ccd7ddc2b..153c6ed6f0 100644 --- a/pkg/snowflake/generic.go +++ b/pkg/snowflake/generic.go @@ -10,18 +10,7 @@ import ( type EntityType string const ( - APIIntegrationType EntityType = "API INTEGRATION" - DatabaseType EntityType = "DATABASE" - ManagedAccountType EntityType = "MANAGED ACCOUNT" - ResourceMonitorType EntityType = "RESOURCE MONITOR" - RoleType EntityType = "ROLE" - ShareType EntityType = "SHARE" - ReplicationType EntityType = "REPLICATION" - StorageIntegrationType EntityType = "STORAGE INTEGRATION" - NotificationIntegrationType EntityType = "NOTIFICATION INTEGRATION" - SecurityIntegrationType EntityType = "SECURITY INTEGRATION" - UserType EntityType = "USER" - WarehouseType EntityType = "WAREHOUSE" + SecurityIntegrationType EntityType = "SECURITY INTEGRATION" ) type Builder struct { diff --git a/pkg/snowflake/identifier.go b/pkg/snowflake/identifier.go index 534a977079..928cea5301 100644 --- a/pkg/snowflake/identifier.go +++ b/pkg/snowflake/identifier.go @@ -32,14 +32,6 @@ func (i *SchemaIdentifier) QualifiedName() string { return fmt.Sprintf(`"%v"."%v"`, i.Database, i.Schema) } -func SchemaIdentifierFromQualifiedName(name string) *SchemaIdentifier { - parts := strings.Split(name, ".") - return &SchemaIdentifier{ - Database: strings.Trim(parts[0], `"`), - Schema: strings.Trim(parts[1], `"`), - } -} - type SchemaObjectIdentifier struct { Database string Schema string diff --git a/pkg/snowflake/parser_internal_test.go b/pkg/snowflake/parser_internal_test.go deleted file mode 100644 index c2a7a09d9b..0000000000 --- a/pkg/snowflake/parser_internal_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package snowflake - -// Internal tests for ViewSelectStatementExtractor diff --git a/pkg/snowflake/replication.go b/pkg/snowflake/replication.go deleted file mode 100644 index 28ba6615d8..0000000000 --- a/pkg/snowflake/replication.go +++ /dev/null @@ -1,56 +0,0 @@ -package snowflake - -import ( - "database/sql" - "fmt" - - "github.com/jmoiron/sqlx" -) - -// Replication returns a pointer to a Builder that abstracts the DDL operations for a replication. -// -// Supported DDL operations are: -// - SHOW REPLICATION DATABASES -// -// [Snowflake Reference](https://docs.snowflake.com/en/user-guide/database-replication-config.html) - -// ReplicationBuilder is a basic builder that enables replication on databases. -type ReplicationBuilder struct { - database string -} - -// DatabaseFromDatabase returns a pointer to a builder that can create a database from a source database. -func NewReplicationBuilder(database string) *ReplicationBuilder { - return &ReplicationBuilder{ - database: database, - } -} - -type Replication struct { - Region sql.NullString `db:"snowflake_region"` - CreatedOn sql.NullString `db:"created_on"` - AccountName sql.NullString `db:"account_name"` - DBName sql.NullString `db:"name"` - Comment sql.NullString `db:"comment"` - IsPrimary sql.NullBool `db:"is_primary"` - Primary sql.NullString `db:"primary"` - ReplAccounts sql.NullString `db:"replication_allowed_to_accounts"` - FailoverAccounts sql.NullString `db:"failover_allowed_to_accounts"` - Org sql.NullString `db:"organization_name"` - AccountLocator sql.NullString `db:"account_locator"` -} - -func ScanReplication(rows *sqlx.Rows, accName string) (*Replication, error) { - for rows.Next() { - r := &Replication{} - err := rows.StructScan(r) - if r.AccountName.String == accName { - return r, err - } - } - return nil, sql.ErrNoRows -} - -func (rb *ReplicationBuilder) Show() string { - return fmt.Sprintf(`SHOW REPLICATION DATABASES LIKE '%s'`, rb.database) -} diff --git a/pkg/snowflake/tag.go b/pkg/snowflake/tag.go index 80a513727a..41d0947f51 100644 --- a/pkg/snowflake/tag.go +++ b/pkg/snowflake/tag.go @@ -2,9 +2,7 @@ package snowflake import ( "database/sql" - "errors" "fmt" - "log" "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -147,7 +145,7 @@ func (tb *TagBuilder) AddMaskingPolicy() string { return fmt.Sprintf(`ALTER TAG %v SET MASKING POLICY %v`, tb.QualifiedName(), tb.maskingPolicyBuilder.QualifiedName()) } -// ReamoveMaskingPolicy returns the SQL query that will remove a masking policy from a tag. +// RemoveMaskingPolicy returns the SQL query that will remove a masking policy from a tag. func (tb *TagBuilder) RemoveMaskingPolicy() string { return fmt.Sprintf(`ALTER TAG %v UNSET MASKING POLICY %v`, tb.QualifiedName(), tb.maskingPolicyBuilder.QualifiedName()) } @@ -212,23 +210,3 @@ func ScanTagPolicy(row *sqlx.Row) (*TagPolicyAttachment, error) { err := row.StructScan(r) return r, err } - -// ListTags returns a list of tags in a database or schema. -func ListTags(databaseName, schemaName string, db *sql.DB) ([]Tag, error) { - stmt := fmt.Sprintf(`SHOW TAGS IN SCHEMA "%v"."%v"`, databaseName, schemaName) - rows, err := Query(db, stmt) - if err != nil { - return nil, err - } - defer rows.Close() - - tags := []Tag{} - if err := sqlx.StructScan(rows, &tags); err != nil { - if errors.Is(err, sql.ErrNoRows) { - log.Println("[DEBUG] no tags found") - return nil, nil - } - return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) - } - return tags, nil -} From 887f5e1466cf856b8c878490f6a8376c1d942e61 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 2 Feb 2024 15:08:05 +0100 Subject: [PATCH 08/11] Fix linter complaints --- pkg/datasources/current_role.go | 3 +-- pkg/datasources/users.go | 1 - pkg/resources/network_policy_attachment.go | 2 +- pkg/resources/user_public_keys.go | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/datasources/current_role.go b/pkg/datasources/current_role.go index b5d79d829d..42a4a4507f 100644 --- a/pkg/datasources/current_role.go +++ b/pkg/datasources/current_role.go @@ -3,7 +3,6 @@ package datasources import ( "context" "database/sql" - "fmt" "log" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -37,7 +36,7 @@ func ReadCurrentRole(d *schema.ResourceData, meta interface{}) error { return nil } - d.SetId(fmt.Sprintf(role)) + d.SetId(role) err = d.Set("name", role) if err != nil { return err diff --git a/pkg/datasources/users.go b/pkg/datasources/users.go index bcfaf6ca25..545728fffe 100644 --- a/pkg/datasources/users.go +++ b/pkg/datasources/users.go @@ -123,7 +123,6 @@ func ReadUsers(d *schema.ResourceData, meta interface{}) error { extractedUsers, err := client.Users.Show(ctx, &sdk.ShowUserOptions{ Like: &sdk.Like{Pattern: sdk.String(userPattern)}, }) - if err != nil { log.Printf("[DEBUG] no users found in account (%s)", d.Id()) d.SetId("") diff --git a/pkg/resources/network_policy_attachment.go b/pkg/resources/network_policy_attachment.go index 4e33e605c3..55a5596eca 100644 --- a/pkg/resources/network_policy_attachment.go +++ b/pkg/resources/network_policy_attachment.go @@ -5,10 +5,10 @@ import ( "database/sql" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "log" "strings" + "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/helper/schema" ) diff --git a/pkg/resources/user_public_keys.go b/pkg/resources/user_public_keys.go index 30249e5934..b1378d3a04 100644 --- a/pkg/resources/user_public_keys.go +++ b/pkg/resources/user_public_keys.go @@ -5,10 +5,10 @@ import ( "database/sql" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "log" "strings" + "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/helper/schema" ) From 004372c111fbec2037bb6649fa8d1f0f5259b0ff Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 5 Feb 2024 15:50:48 +0100 Subject: [PATCH 09/11] Fix network policy --- pkg/resources/network_policy_attachment.go | 48 ++++++----- .../network_policy_attachment_test.go | 81 ------------------- pkg/sdk/parameters.go | 1 + pkg/snowflake/network_policy.go | 72 ----------------- pkg/snowflake/network_policy_test.go | 62 -------------- 5 files changed, 28 insertions(+), 236 deletions(-) delete mode 100644 pkg/resources/network_policy_attachment_test.go delete mode 100644 pkg/snowflake/network_policy.go delete mode 100644 pkg/snowflake/network_policy_test.go diff --git a/pkg/resources/network_policy_attachment.go b/pkg/resources/network_policy_attachment.go index 55a5596eca..0a7aa141ff 100644 --- a/pkg/resources/network_policy_attachment.go +++ b/pkg/resources/network_policy_attachment.go @@ -3,13 +3,11 @@ package resources import ( "context" "database/sql" - "errors" "fmt" "log" "strings" "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/helper/schema" ) @@ -78,8 +76,9 @@ func CreateNetworkPolicyAttachment(d *schema.ResourceData, meta interface{}) err // ReadNetworkPolicyAttachment implements schema.ReadFunc. func ReadNetworkPolicyAttachment(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) policyName := strings.Replace(d.Id(), "_attachment", "", 1) - builder := snowflake.NetworkPolicy(policyName) var currentUsers []string if err := d.Set("network_policy_name", policyName); err != nil { @@ -89,14 +88,13 @@ func ReadNetworkPolicyAttachment(d *schema.ResourceData, meta interface{}) error if u, ok := d.GetOk("users"); ok { users := expandStringList(u.(*schema.Set).List()) for _, user := range users { - row := snowflake.QueryRow(db, builder.ShowOnUser(user)) - attachment, err := snowflake.ScanNetworkPolicyAttachment(row) - if errors.Is(err, sql.ErrNoRows) { + parameter, err := client.Parameters.ShowUserParameter(ctx, sdk.UserParameterNetworkPolicy, sdk.NewAccountObjectIdentifier(user)) + if err != nil { log.Printf("[DEBUG] network policy (%s) not found on user (%s)", d.Id(), user) continue } - if attachment.Level.String == "USER" && attachment.Key.String == "NETWORK_POLICY" && attachment.Value.String == policyName { + if parameter.Level == "USER" && parameter.Key == "NETWORK_POLICY" && parameter.Value == policyName { currentUsers = append(currentUsers, user) } } @@ -107,14 +105,14 @@ func ReadNetworkPolicyAttachment(d *schema.ResourceData, meta interface{}) error } isSetOnAccount := false - row := snowflake.QueryRow(db, builder.ShowOnAccount()) - attachment, err := snowflake.ScanNetworkPolicyAttachment(row) - if errors.Is(err, sql.ErrNoRows) { + + parameter, err := client.Parameters.ShowAccountParameter(ctx, sdk.AccountParameterNetworkPolicy) + if err != nil { log.Printf("[DEBUG] network policy (%s) not found on account", d.Id()) isSetOnAccount = false } - if err == nil && attachment.Level.String == "ACCOUNT" && attachment.Key.String == "NETWORK_POLICY" && attachment.Value.String == policyName { + if err == nil && parameter.Level == "ACCOUNT" && parameter.Key == "NETWORK_POLICY" && parameter.Value == policyName { isSetOnAccount = true } @@ -201,11 +199,12 @@ func DeleteNetworkPolicyAttachment(d *schema.ResourceData, meta interface{}) err // Note: the ip address of the session executing this SQL must be allowed by the network policy being set. func setOnAccount(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) policyName := d.Get("network_policy_name").(string) - acctSQL := snowflake.NetworkPolicy(policyName).SetOnAccount() - - if err := snowflake.Exec(db, acctSQL); err != nil { + err := client.Accounts.Alter(ctx, &sdk.AlterAccountOptions{Set: &sdk.AccountSet{Parameters: &sdk.AccountLevelParameters{ObjectParameters: &sdk.ObjectParameters{NetworkPolicy: sdk.String(policyName)}}}}) + if err != nil { return fmt.Errorf("error setting network policy %v on account err = %w", policyName, err) } @@ -215,11 +214,12 @@ func setOnAccount(d *schema.ResourceData, meta interface{}) error { // setOnAccount unsets the network policy globally for the Snowflake account. func unsetOnAccount(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) policyName := d.Get("network_policy_name").(string) - acctSQL := snowflake.NetworkPolicy(policyName).UnsetOnAccount() - - if err := snowflake.Exec(db, acctSQL); err != nil { + err := client.Accounts.Alter(ctx, &sdk.AlterAccountOptions{Unset: &sdk.AccountUnset{Parameters: &sdk.AccountLevelParametersUnset{ObjectParameters: &sdk.ObjectParametersUnset{NetworkPolicy: sdk.Bool(true)}}}}) + if err != nil { return fmt.Errorf("error unsetting network policy %v on account err = %w", policyName, err) } @@ -241,9 +241,12 @@ func setOnUsers(users []string, data *schema.ResourceData, meta interface{}) err // setOnUser sets the network policy for a given user. func setOnUser(user string, data *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) policyName := data.Get("network_policy_name").(string) - userSQL := snowflake.NetworkPolicy(policyName).SetOnUser(user) - if err := snowflake.Exec(db, userSQL); err != nil { + + err := client.Users.Alter(ctx, sdk.NewAccountObjectIdentifier(user), &sdk.AlterUserOptions{Set: &sdk.UserSet{ObjectParameters: &sdk.UserObjectParameters{NetworkPolicy: sdk.String(policyName)}}}) + if err != nil { return fmt.Errorf("error setting network policy %v on user %v err = %w", policyName, user, err) } @@ -265,9 +268,12 @@ func unsetOnUsers(users []string, data *schema.ResourceData, meta interface{}) e // unsetOnUser sets the network policy for a given user. func unsetOnUser(user string, data *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) policyName := data.Get("network_policy_name").(string) - userSQL := snowflake.NetworkPolicy(policyName).UnsetOnUser(user) - if err := snowflake.Exec(db, userSQL); err != nil { + + err := client.Users.Alter(ctx, sdk.NewAccountObjectIdentifier(user), &sdk.AlterUserOptions{Unset: &sdk.UserUnset{ObjectParameters: &sdk.UserObjectParametersUnset{NetworkPolicy: sdk.Bool(true)}}}) + if err != nil { return fmt.Errorf("error unsetting network policy %v on user %v", policyName, user) } diff --git a/pkg/resources/network_policy_attachment_test.go b/pkg/resources/network_policy_attachment_test.go deleted file mode 100644 index ff9af671ff..0000000000 --- a/pkg/resources/network_policy_attachment_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package resources_test - -import ( - "database/sql" - "testing" - - 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 TestNetworkPolicyAttachment(t *testing.T) { - r := require.New(t) - err := resources.NetworkPolicyAttachment().InternalValidate(provider.Provider().Schema, true) - r.NoError(err) -} - -func TestNetworkPolicyAttachmentCreate(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "network_policy_name": "test-network-policy", - "set_for_account": true, - "users": []interface{}{"test-user"}, - } - d := schema.TestResourceDataRaw(t, resources.NetworkPolicyAttachment().Schema, in) - r.NotNil(d) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec(`^ALTER ACCOUNT SET NETWORK_POLICY = "test-network-policy"$`).WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec(`^DESCRIBE USER "test-user"$`).WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec(`^ALTER USER "test-user" SET NETWORK_POLICY = "test-network-policy"$`).WillReturnResult(sqlmock.NewResult(1, 1)) - - err := resources.CreateNetworkPolicyAttachment(d, db) - r.NoError(err) - }) -} - -func TestNetworkPolicyAttachmentSetOnAccountDelete(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "network_policy_name": "test-network-policy", - "set_for_account": true, - "users": []interface{}{"test-user"}, - } - d := schema.TestResourceDataRaw(t, resources.NetworkPolicyAttachment().Schema, in) - r.NotNil(d) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec(`^ALTER ACCOUNT UNSET NETWORK_POLICY$`).WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec(`^DESCRIBE USER "test-user"$`).WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec(`^ALTER USER "test-user" UNSET NETWORK_POLICY$`).WillReturnResult(sqlmock.NewResult(1, 1)) - - err := resources.DeleteNetworkPolicyAttachment(d, db) - r.NoError(err) - }) -} - -func TestNetworkPolicyAttachmentDelete(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "network_policy_name": "test-network-policy", - "set_for_account": false, - "users": []interface{}{"test-user"}, - } - d := schema.TestResourceDataRaw(t, resources.NetworkPolicyAttachment().Schema, in) - r.NotNil(d) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec(`^DESCRIBE USER "test-user"$`).WillReturnResult(sqlmock.NewResult(1, 1)) - mock.ExpectExec(`^ALTER USER "test-user" UNSET NETWORK_POLICY$`).WillReturnResult(sqlmock.NewResult(1, 1)) - - err := resources.DeleteNetworkPolicyAttachment(d, db) - r.NoError(err) - }) -} diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index 76b5f6a8f2..8bd98344ee 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -474,6 +474,7 @@ const ( UserParameterJsonIndent UserParameter = "JSON_INDENT" UserParameterLockTimeout UserParameter = "LOCK_TIMEOUT" UserParameterMultiStatementCount UserParameter = "MULTI_STATEMENT_COUNT" + UserParameterNetworkPolicy UserParameter = "NETWORK_POLICY" UserParameterQueryTag UserParameter = "QUERY_TAG" UserParameterQuotedIdentifiersIgnoreCase UserParameter = "QUOTED_IDENTIFIERS_IGNORE_CASE" UserParameterRowsPerResultset UserParameter = "ROWS_PER_RESULTSET" diff --git a/pkg/snowflake/network_policy.go b/pkg/snowflake/network_policy.go deleted file mode 100644 index d7d75dd5a5..0000000000 --- a/pkg/snowflake/network_policy.go +++ /dev/null @@ -1,72 +0,0 @@ -package snowflake - -import ( - "database/sql" - "fmt" - - "github.com/jmoiron/sqlx" -) - -// NetworkPolicyBuilder abstracts the creation of SQL queries for a Snowflake Network Policy. -type NetworkPolicyBuilder struct { - name string - comment string - allowedIPList string - blockedIPList string -} - -// NetworkPolicy returns a pointer to a Builder that abstracts the DDL operations for a network policy. -// -// Supported DDL operations are: -// - CREATE NETWORK POLICY -// - DROP NETWORK POLICY -// - SHOW NETWORK POLICIES -// -// [Snowflake Reference](https://docs.snowflake.com/en/user-guide/network-policies.html) -func NetworkPolicy(name string) *NetworkPolicyBuilder { - return &NetworkPolicyBuilder{ - name: name, - } -} - -// SetOnAccount returns the SQL query that will set the network policy globally on your Snowflake account. -func (npb *NetworkPolicyBuilder) SetOnAccount() string { - return fmt.Sprintf(`ALTER ACCOUNT SET NETWORK_POLICY = "%v"`, npb.name) -} - -// UnsetOnAccount returns the SQL query that will unset the network policy globally on your Snowflake account. -func (npb *NetworkPolicyBuilder) UnsetOnAccount() string { - return `ALTER ACCOUNT UNSET NETWORK_POLICY` -} - -// SetOnUser returns the SQL query that will set the network policy on a given user. -func (npb *NetworkPolicyBuilder) SetOnUser(u string) string { - return fmt.Sprintf(`ALTER USER "%v" SET NETWORK_POLICY = "%v"`, u, npb.name) -} - -// UnsetOnUser returns the SQL query that will unset the network policy of a given user. -func (npb *NetworkPolicyBuilder) UnsetOnUser(u string) string { - return fmt.Sprintf(`ALTER USER "%v" UNSET NETWORK_POLICY`, u) -} - -// ShowOnUser returns the SQL query that will SHOW network policy set on a specific User. -func (npb *NetworkPolicyBuilder) ShowOnUser(u string) string { - return fmt.Sprintf(`SHOW PARAMETERS LIKE 'network_policy' IN USER "%v"`, u) -} - -// ShowOnAccount returns the SQL query that will SHOW network policy set on Account. -func (npb *NetworkPolicyBuilder) ShowOnAccount() string { - return `SHOW PARAMETERS LIKE 'network_policy' IN ACCOUNT` -} - -type NetworkPolicyAttachmentStruct struct { - Key sql.NullString `db:"key"` - Value sql.NullString `db:"value"` - Level sql.NullString `db:"level"` -} - -func ScanNetworkPolicyAttachment(row *sqlx.Row) (*NetworkPolicyAttachmentStruct, error) { - r := &NetworkPolicyAttachmentStruct{} - err := row.StructScan(r) - return r, err -} diff --git a/pkg/snowflake/network_policy_test.go b/pkg/snowflake/network_policy_test.go deleted file mode 100644 index e85505dd64..0000000000 --- a/pkg/snowflake/network_policy_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package snowflake_test - -import ( - "testing" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/stretchr/testify/require" -) - -func TestNetworkPolicySetOnAccount(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.SetOnAccount() - r.Equal(`ALTER ACCOUNT SET NETWORK_POLICY = "test_network_policy"`, q) -} - -func TestNetworkPolicyUnsetOnAccount(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.UnsetOnAccount() - r.Equal(`ALTER ACCOUNT UNSET NETWORK_POLICY`, q) -} - -func TestNetworkPolicySetOnUser(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.SetOnUser("testuser") - r.Equal(`ALTER USER "testuser" SET NETWORK_POLICY = "test_network_policy"`, q) -} - -func TestNetworkPolicyUnsetOnUser(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.UnsetOnUser("testuser") - r.Equal(`ALTER USER "testuser" UNSET NETWORK_POLICY`, q) -} - -func TestNetworkPolicyShowOnUser(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.ShowOnUser("testuser") - r.Equal(`SHOW PARAMETERS LIKE 'network_policy' IN USER "testuser"`, q) -} - -func TestNetworkPolicyShowOnAccount(t *testing.T) { - r := require.New(t) - s := snowflake.NetworkPolicy("test_network_policy") - r.NotNil(s) - - q := s.ShowOnAccount() - r.Equal(`SHOW PARAMETERS LIKE 'network_policy' IN ACCOUNT`, q) -} From ce142dcaa4ab0ae6155e86e8d30aa73bd728e28b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 5 Feb 2024 17:22:46 +0100 Subject: [PATCH 10/11] Add issue number to TODO comment --- pkg/sdk/context_functions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sdk/context_functions.go b/pkg/sdk/context_functions.go index d8783746b9..6234f0c0ea 100644 --- a/pkg/sdk/context_functions.go +++ b/pkg/sdk/context_functions.go @@ -8,7 +8,7 @@ import ( "strings" ) -// TODO [SNOW-]: add generic select +// TODO [SNOW-1042951]: add generic select type ContextFunctions interface { // Session functions. CurrentAccount(ctx context.Context) (string, error) From c4452546c31cefc05d0a34ad3273f48008b27200 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Tue, 6 Feb 2024 11:36:25 +0100 Subject: [PATCH 11/11] Fix after review --- pkg/datasources/current_account.go | 2 +- pkg/datasources/resource_monitors.go | 2 +- .../system_get_snowflake_platform_info.go | 2 +- pkg/datasources/warehouses.go | 2 +- pkg/sdk/context_functions.go | 14 +++++++------- .../testint/context_functions_integration_test.go | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/datasources/current_account.go b/pkg/datasources/current_account.go index 878e728e64..af30f0d528 100644 --- a/pkg/datasources/current_account.go +++ b/pkg/datasources/current_account.go @@ -44,7 +44,7 @@ func ReadCurrentAccount(d *schema.ResourceData, meta interface{}) error { ctx := context.Background() client := sdk.NewClientFromDB(db) - current, err := client.ContextFunctions.Current(ctx) + current, err := client.ContextFunctions.CurrentSessionDetails(ctx) if err != nil { log.Println("[DEBUG] current_account failed to decode") d.SetId("") diff --git a/pkg/datasources/resource_monitors.go b/pkg/datasources/resource_monitors.go index 8bf969fba5..1d7a2984d6 100644 --- a/pkg/datasources/resource_monitors.go +++ b/pkg/datasources/resource_monitors.go @@ -52,7 +52,7 @@ func ReadResourceMonitors(d *schema.ResourceData, meta interface{}) error { client := sdk.NewClientFromDB(db) ctx := context.Background() - account, err := client.ContextFunctions.Current(ctx) + account, err := client.ContextFunctions.CurrentSessionDetails(ctx) if err != nil { log.Print("[DEBUG] unable to retrieve current account") d.SetId("") diff --git a/pkg/datasources/system_get_snowflake_platform_info.go b/pkg/datasources/system_get_snowflake_platform_info.go index d59d15b6ea..7e4c465b25 100644 --- a/pkg/datasources/system_get_snowflake_platform_info.go +++ b/pkg/datasources/system_get_snowflake_platform_info.go @@ -42,7 +42,7 @@ func ReadSystemGetSnowflakePlatformInfo(d *schema.ResourceData, meta interface{} sel := snowflake.SystemGetSnowflakePlatformInfoQuery() row := snowflake.QueryRow(db, sel) - acc, err := client.ContextFunctions.Current(context.Background()) + acc, err := client.ContextFunctions.CurrentSessionDetails(context.Background()) if err != nil { // If not found, mark resource to be removed from state file during apply or refresh d.SetId("") diff --git a/pkg/datasources/warehouses.go b/pkg/datasources/warehouses.go index 7583b96890..9e5db5b32e 100644 --- a/pkg/datasources/warehouses.go +++ b/pkg/datasources/warehouses.go @@ -61,7 +61,7 @@ func ReadWarehouses(d *schema.ResourceData, meta interface{}) error { client := sdk.NewClientFromDB(db) ctx := context.Background() - account, err := client.ContextFunctions.Current(ctx) + account, err := client.ContextFunctions.CurrentSessionDetails(ctx) if err != nil { d.SetId("") return nil diff --git a/pkg/sdk/context_functions.go b/pkg/sdk/context_functions.go index 6234f0c0ea..714f3fbf7d 100644 --- a/pkg/sdk/context_functions.go +++ b/pkg/sdk/context_functions.go @@ -17,7 +17,7 @@ type ContextFunctions interface { CurrentRegion(ctx context.Context) (string, error) CurrentSession(ctx context.Context) (string, error) CurrentUser(ctx context.Context) (string, error) - Current(ctx context.Context) (*CurrentDetails, error) + CurrentSessionDetails(ctx context.Context) (*CurrentSessionDetails, error) // Session Object functions. CurrentDatabase(ctx context.Context) (string, error) @@ -32,7 +32,7 @@ type contextFunctions struct { client *Client } -type currentDetailsDBRow struct { +type currentSessionDetailsDBRow struct { CurrentAccount string `db:"CURRENT_ACCOUNT"` CurrentRole string `db:"CURRENT_ROLE"` CurrentRegion string `db:"CURRENT_REGION"` @@ -40,7 +40,7 @@ type currentDetailsDBRow struct { CurrentUser string `db:"CURRENT_USER"` } -type CurrentDetails struct { +type CurrentSessionDetails struct { Account string `db:"CURRENT_ACCOUNT"` Role string `db:"CURRENT_ROLE"` Region string `db:"CURRENT_REGION"` @@ -48,7 +48,7 @@ type CurrentDetails struct { User string `db:"CURRENT_USER"` } -func (acc *CurrentDetails) AccountURL() (string, error) { +func (acc *CurrentSessionDetails) AccountURL() (string, error) { if regionID, ok := regionMapping[strings.ToLower(acc.Region)]; ok { accountID := acc.Account if len(regionID) > 0 { @@ -159,13 +159,13 @@ func (c *contextFunctions) CurrentUser(ctx context.Context) (string, error) { return s.CurrentUser, nil } -func (c *contextFunctions) Current(ctx context.Context) (*CurrentDetails, error) { - s := ¤tDetailsDBRow{} +func (c *contextFunctions) CurrentSessionDetails(ctx context.Context) (*CurrentSessionDetails, error) { + s := ¤tSessionDetailsDBRow{} err := c.client.queryOne(ctx, s, "SELECT CURRENT_ACCOUNT() as CURRENT_ACCOUNT, CURRENT_ROLE() as CURRENT_ROLE, CURRENT_REGION() AS CURRENT_REGION, CURRENT_SESSION() as CURRENT_SESSION, CURRENT_USER() as CURRENT_USER") if err != nil { return nil, err } - return &CurrentDetails{ + return &CurrentSessionDetails{ Account: s.CurrentAccount, Role: s.CurrentRole, Region: s.CurrentRegion, diff --git a/pkg/sdk/testint/context_functions_integration_test.go b/pkg/sdk/testint/context_functions_integration_test.go index 2a86acdd1d..ba317a7ed8 100644 --- a/pkg/sdk/testint/context_functions_integration_test.go +++ b/pkg/sdk/testint/context_functions_integration_test.go @@ -49,11 +49,11 @@ func TestInt_CurrentUser(t *testing.T) { assert.NotEmpty(t, user) } -func TestInt_Current(t *testing.T) { +func TestInt_CurrentSessionDetails(t *testing.T) { client := testClient(t) ctx := testContext(t) - account, err := client.ContextFunctions.Current(ctx) + account, err := client.ContextFunctions.CurrentSessionDetails(ctx) require.NoError(t, err) assert.NotNil(t, account) assert.NotEmpty(t, account.Account)