Skip to content

Commit

Permalink
feat: add support for notify_users to snowflake_resource_monitor
Browse files Browse the repository at this point in the history
…resource (#1340)

* Add NotifyUsers field to resourceMonitor struct

* Add notify_users property to resources/resource_monitor schema

* Set notify_users field in CreateResourceMonitor

* Add test for altering pkg/snowflake/resource_monitor

* Update snowflake/resource_monitor creation test for notify_users

* Update resource_monitor's CreateBuilder to allow string lists

* Add notify_users to TestResourceMonitorCreate

* Switch resource_monitor.notify_users to schema.TypeSet

* Allow reading notify_users from a ResourceMonitor resource

* Change TestResourceMonitorCreate to use conventional name styles

* Add notify_users information to expectReadResourceMonitor

* Add notify_users testing to TestAcc_ResourceMonitor

* Fix method used to test for Set element existence

* Update TestResourceMonitor to use more conventional user names

* Remove notify_users setup/check from existing test

* Add fn to generate test config for resource monitors with notify_users

* Add (unconventional) TestAcc_ResourceMonitorNotifyUsers test

* Add notify_users to snowflake_resource_monitor example

* Add notify_users to resource_monitor docs

* Run `go fmt` on changed files

* Fix indentation on inline terraform declaration

* Run `make docs`
  • Loading branch information
nafarlee authored Nov 7, 2022
1 parent 5551b2f commit 7094f15
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 10 deletions.
3 changes: 3 additions & 0 deletions docs/resources/resource_monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ resource "snowflake_resource_monitor" "monitor" {
notify_triggers = [40]
suspend_triggers = [50]
suspend_immediate_triggers = [90]
notify_users = ["USERONE", "USERTWO"]
}
```

Expand All @@ -40,6 +42,7 @@ resource "snowflake_resource_monitor" "monitor" {
- `end_timestamp` (String) The date and time when the resource monitor suspends the assigned warehouses.
- `frequency` (String) The frequency interval at which the credit usage resets to 0. If you set a frequency for a resource monitor, you must also set START_TIMESTAMP.
- `notify_triggers` (Set of Number) A list of percentage thresholds at which to send an alert to subscribed users.
- `notify_users` (Set of String) Specifies the list of users to receive email notifications on resource monitors.
- `set_for_account` (Boolean) Specifies whether the resource monitor should be applied globally to your Snowflake account.
- `start_timestamp` (String) The date and time when the resource monitor starts monitoring credit usage for the assigned warehouses.
- `suspend_immediate_triggers` (Set of Number) A list of percentage thresholds at which to immediately suspend all warehouses.
Expand Down
2 changes: 2 additions & 0 deletions examples/resources/snowflake_resource_monitor/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ resource "snowflake_resource_monitor" "monitor" {
notify_triggers = [40]
suspend_triggers = [50]
suspend_immediate_triggers = [90]

notify_users = ["USERONE", "USERTWO"]
}
19 changes: 19 additions & 0 deletions pkg/resources/resource_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ var resourceMonitorSchema = map[string]*schema.Schema{
Description: "Identifier for the resource monitor; must be unique for your account.",
ForceNew: true,
},
"notify_users": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
Description: "Specifies the list of users to receive email notifications on resource monitors.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"credit_quota": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -108,6 +117,9 @@ func CreateResourceMonitor(d *schema.ResourceData, meta interface{}) error {

cb := snowflake.ResourceMonitor(name).Create()
// Set optionals
if v, ok := d.GetOk("notify_users"); ok {
cb.SetStringList("notify_users", expandStringList(v.(*schema.Set).List()))
}
if v, ok := d.GetOk("credit_quota"); ok {
cb.SetInt("credit_quota", v.(int))
}
Expand Down Expand Up @@ -194,6 +206,13 @@ func ReadResourceMonitor(d *schema.ResourceData, meta interface{}) error {
return err
}

if len(rm.NotifyUsers.String) > 0 {
err = d.Set("notify_users", strings.Split(rm.NotifyUsers.String, ", "))
if err != nil {
return err
}
}

// Snowflake returns credit_quota as a float, but only accepts input as an int
if rm.CreditQuota.Valid {
cqf, err := strconv.ParseFloat(rm.CreditQuota.String, 64)
Expand Down
52 changes: 52 additions & 0 deletions pkg/resources/resource_monitor_acceptance_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package resources_test

import (
"encoding/json"
"fmt"
"os"
"strings"
"testing"

Expand Down Expand Up @@ -44,3 +46,53 @@ resource "snowflake_resource_monitor" "test" {
}
`, accName)
}

func TestAcc_ResourceMonitorNotifyUsers(t *testing.T) {
userEnv := os.Getenv("RESOURCE_MONITOR_NOTIFY_USERS_TEST")
if userEnv == "" {
t.Skip("Skipping TestAcc_ResourceMonitorNotifyUsers")
}
users := strings.Split(userEnv, ",")
name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
config, err := resourceMonitorNotifyUsersConfig(name, users)
if err != nil {
t.Error(err)
}
checks := []resource.TestCheckFunc{
resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name),
resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "set_for_account", "false"),
}
for _, s := range users {
checks = append(checks, resource.TestCheckTypeSetElemAttr("snowflake_resource_monitor.test", "notify_users.*", s))
}
resource.ParallelTest(t, resource.TestCase{
Providers: providers(),
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(checks...),
},
{
ResourceName: "snowflake_resource_monitor.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func resourceMonitorNotifyUsersConfig(accName string, accNotifyUsers []string) (string, error) {
notifyUsers, err := json.Marshal(accNotifyUsers)
if err != nil {
return "", err
}
config := fmt.Sprintf(`
resource "snowflake_resource_monitor" "test" {
name = "%v"
set_for_account = false
notify_users = %v
}
`, accName, string(notifyUsers))
return config, nil
}
7 changes: 4 additions & 3 deletions pkg/resources/resource_monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestResourceMonitorCreate(t *testing.T) {

in := map[string]interface{}{
"name": "good_name",
"notify_users": []interface{}{"USERONE", "USERTWO"},
"credit_quota": 100,
"notify_triggers": []interface{}{75, 88},
"suspend_triggers": []interface{}{99},
Expand All @@ -37,7 +38,7 @@ func TestResourceMonitorCreate(t *testing.T) {

WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectExec(
`^CREATE RESOURCE MONITOR "good_name" CREDIT_QUOTA=100 TRIGGERS ON 99 PERCENT DO SUSPEND ON 105 PERCENT DO SUSPEND_IMMEDIATE ON 88 PERCENT DO NOTIFY ON 75 PERCENT DO NOTIFY$`,
`^CREATE RESOURCE MONITOR "good_name" CREDIT_QUOTA=100 NOTIFY_USERS=\('USERTWO', 'USERONE'\) TRIGGERS ON 99 PERCENT DO SUSPEND ON 105 PERCENT DO SUSPEND_IMMEDIATE ON 88 PERCENT DO NOTIFY ON 75 PERCENT DO NOTIFY$`,
).WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`^ALTER ACCOUNT SET RESOURCE_MONITOR = "good_name"$`).WillReturnResult(sqlmock.NewResult(1, 1))

Expand All @@ -51,10 +52,10 @@ func expectReadResourceMonitor(mock sqlmock.Sqlmock) {
rows := sqlmock.NewRows([]string{
"name", "credit_quota", "used_credits", "remaining_credits", "level",
"frequency", "start_time", "end_time", "notify_at", "suspend_at",
"suspend_immediately_at", "created_on", "owner", "comment",
"suspend_immediately_at", "created_on", "owner", "comment", "notify_users",
}).AddRow(
"good_name", 100.00, 0.00, 100.00, "ACCOUNT", "MONTHLY", "2001-01-01 00:00:00.000 -0700",
"", "75%,88%", "99%", "105%", "2001-01-01 00:00:00.000 -0700", "ACCOUNTADMIN", "")
"", "75%,88%", "99%", "105%", "2001-01-01 00:00:00.000 -0700", "ACCOUNTADMIN", "", "USERONE, USERTWO")
mock.ExpectQuery(`^SHOW RESOURCE MONITORS LIKE 'good_name'$`).WillReturnRows(rows)
}

Expand Down
18 changes: 12 additions & 6 deletions pkg/snowflake/resource_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ const (
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),
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),
}
Expand Down Expand Up @@ -108,6 +109,10 @@ func (rcb *ResourceMonitorCreateBuilder) Statement() string {
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")
}
Expand Down Expand Up @@ -144,6 +149,7 @@ type resourceMonitor struct {
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) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/snowflake/resource_monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ func TestResourceMonitor(t *testing.T) {
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.ResourceMonitor("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 TRIGGERS ON 80 PERCENT DO NOTIFY ON 90 PERCENT DO NOTIFY ON 95 PERCENT DO SUSPEND ON 100 PERCENT DO SUSPEND_IMMEDIATE`, q)
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) {
Expand Down

0 comments on commit 7094f15

Please sign in to comment.