From 20da3ed2abe281b670e1978fff2df2e6abaea4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Fri, 6 Sep 2024 09:29:34 +0200 Subject: [PATCH 1/7] wip --- .../model/gen/templates/definition.tmpl | 16 ++++++++++ .../model/resource_monitor_model_gen.go | 19 ++++++++++++ .../config/model/user_model_gen.go | 19 ++++++++++++ .../config/model/warehouse_model_gen.go | 19 ++++++++++++ .../TestAcc_ResourceMonitor/complete/test.tf | 17 +++++++++++ .../complete/variables.tf | 30 +++++++++++++++++++ .../only_triggers/test.tf | 12 ++++++++ .../only_triggers/variables.tf | 10 +++++++ 8 files changed, 142 insertions(+) create mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf diff --git a/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl b/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl index c61398e7dd..413bf574a0 100644 --- a/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl +++ b/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl @@ -51,3 +51,19 @@ func {{ .Name }}WithDefaultMeta( {{- end -}} return {{ $modelVar }} } + +func (r *{{ $modelName }}) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} diff --git a/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go index e2ccebc599..af1db9310a 100644 --- a/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go @@ -3,6 +3,9 @@ package model import ( + "reflect" + "strings" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -45,6 +48,22 @@ func ResourceMonitorWithDefaultMeta( return r } +func (r *ResourceMonitorModel) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} + ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go index 32eb8218ee..72ba22b52b 100644 --- a/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go @@ -3,6 +3,9 @@ package model import ( + "reflect" + "strings" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -116,6 +119,22 @@ func UserWithDefaultMeta( return u } +func (r *UserModel) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} + ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go index 388b71ce33..39760f3521 100644 --- a/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go @@ -3,6 +3,9 @@ package model import ( + "reflect" + "strings" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -52,6 +55,22 @@ func WarehouseWithDefaultMeta( return w } +func (r *WarehouseModel) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} + ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf new file mode 100644 index 0000000000..29fd7e5e9b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf @@ -0,0 +1,17 @@ +resource "snowflake_resource_monitor" "test" { + name = var.name + notify_users = var.notify_users + credit_quota = var.credit_quota + frequency = var.frequency + start_timestamp = var.start_timestamp + end_timestamp = var.end_timestamp + + dynamic "trigger" { + for_each = var.trigger + + content { + threshold = trigger.value.threshold + on_threshold_reached = trigger.value.on_threshold_reached + } + } +} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf new file mode 100644 index 0000000000..4b8a6a8a88 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf @@ -0,0 +1,30 @@ +variable "name" { + type = string +} + +variable "notify_users" { + type = set(string) +} + +variable "credit_quota" { + type = number +} + +variable "frequency" { + type = string +} + +variable "start_timestamp" { + type = string +} + +variable "end_timestamp" { + type = string +} + +variable "trigger" { + type = set(object({ + threshold = number + on_threshold_reached = string + })) +} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf new file mode 100644 index 0000000000..77b7f7c873 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf @@ -0,0 +1,12 @@ +resource "snowflake_resource_monitor" "test" { + name = var.name + + dynamic "trigger" { + for_each = var.trigger + + content { + threshold = trigger.value.threshold + on_threshold_reached = trigger.value.on_threshold_reached + } + } +} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf new file mode 100644 index 0000000000..a9b74bf060 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf @@ -0,0 +1,10 @@ +variable "name" { + type = string +} + +variable "trigger" { + type = set(object({ + threshold = number + on_threshold_reached = string + })) +} From 6d6bf4f5715c37f76164013bb9bdc37e52e851a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Mon, 9 Sep 2024 13:07:26 +0200 Subject: [PATCH 2/7] wip --- .../config/model/database_role_model_gen.go | 19 ++++++++++++ .../config/model/view_model_gen.go | 19 ++++++++++++ .../TestAcc_ResourceMonitor/complete/test.tf | 17 ----------- .../complete/variables.tf | 30 ------------------- .../only_triggers/test.tf | 12 -------- .../only_triggers/variables.tf | 10 ------- 6 files changed, 38 insertions(+), 69 deletions(-) delete mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf diff --git a/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go index df3106e1cc..753f3bc5e7 100644 --- a/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go @@ -3,6 +3,9 @@ package model import ( + "reflect" + "strings" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -43,6 +46,22 @@ func DatabaseRoleWithDefaultMeta( return d } +func (r *DatabaseRoleModel) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} + ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go index 1afe8859d8..f5a3db8962 100644 --- a/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go @@ -3,6 +3,9 @@ package model import ( + "reflect" + "strings" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -63,6 +66,22 @@ func ViewWithDefaultMeta( return v } +func (r *ViewModel) ToConfigVariables() tfconfig.Variables { + variables := make(tfconfig.Variables) + rType := reflect.TypeOf(r).Elem() + rValue := reflect.ValueOf(r).Elem() + for i := 0; i < rType.NumField(); i++ { + field := rType.Field(i) + if jsonTag, ok := field.Tag.Lookup("json"); ok { + name := strings.Split(jsonTag, ",")[0] + if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { + variables[name] = fieldValue + } + } + } + return variables +} + ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf deleted file mode 100644 index 29fd7e5e9b..0000000000 --- a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/test.tf +++ /dev/null @@ -1,17 +0,0 @@ -resource "snowflake_resource_monitor" "test" { - name = var.name - notify_users = var.notify_users - credit_quota = var.credit_quota - frequency = var.frequency - start_timestamp = var.start_timestamp - end_timestamp = var.end_timestamp - - dynamic "trigger" { - for_each = var.trigger - - content { - threshold = trigger.value.threshold - on_threshold_reached = trigger.value.on_threshold_reached - } - } -} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf deleted file mode 100644 index 4b8a6a8a88..0000000000 --- a/pkg/resources/testdata/TestAcc_ResourceMonitor/complete/variables.tf +++ /dev/null @@ -1,30 +0,0 @@ -variable "name" { - type = string -} - -variable "notify_users" { - type = set(string) -} - -variable "credit_quota" { - type = number -} - -variable "frequency" { - type = string -} - -variable "start_timestamp" { - type = string -} - -variable "end_timestamp" { - type = string -} - -variable "trigger" { - type = set(object({ - threshold = number - on_threshold_reached = string - })) -} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf deleted file mode 100644 index 77b7f7c873..0000000000 --- a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/test.tf +++ /dev/null @@ -1,12 +0,0 @@ -resource "snowflake_resource_monitor" "test" { - name = var.name - - dynamic "trigger" { - for_each = var.trigger - - content { - threshold = trigger.value.threshold - on_threshold_reached = trigger.value.on_threshold_reached - } - } -} diff --git a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf b/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf deleted file mode 100644 index a9b74bf060..0000000000 --- a/pkg/resources/testdata/TestAcc_ResourceMonitor/only_triggers/variables.tf +++ /dev/null @@ -1,10 +0,0 @@ -variable "name" { - type = string -} - -variable "trigger" { - type = set(object({ - threshold = number - on_threshold_reached = string - })) -} From 092a477c7ebb5d73e5aaf5fd3c274c57b7e545fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Tue, 10 Sep 2024 09:54:11 +0200 Subject: [PATCH 3/7] wip --- MIGRATION_GUIDE.md | 27 ++++++ docs/data-sources/database_roles.md | 44 +++++++++- docs/data-sources/resource_monitors.md | 67 +++++++++++++-- docs/resources/resource_monitor.md | 22 +++-- .../snowflake_database_roles/data-source.tf | 46 +++++++++- .../data-source.tf | 41 ++++++++- .../snowflake_resource_monitor/resource.tf | 22 +++-- .../resource_monitor_show_output_ext.go | 17 +++- pkg/datasources/resource_monitors.go | 84 +++++++++---------- .../resource_monitors_acceptance_test.go | 51 ++++++++--- 10 files changed, 339 insertions(+), 82 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 974e1789f7..9887f5de19 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -4,6 +4,33 @@ This document is meant to help you migrate your Terraform config to the new newe describe deprecations or breaking changes and help you to change your configuration to keep the same (or similar) behavior across different versions. +## v0.95.0 ➞ v0.96.0 + +### *(breaking change)* resource_monitor resource +Removed fields: +- `set_for_account` +- `warehouses` +- `suspend_triggers` (now, `suspend_trigger` should be used) +- `suspend_immediate_triggers` (now, `suspend_immediate_trigger` should be used) + +### *(breaking change)* resource_monitor data source +Changes: +- New filtering option `like` +- Now, the output of `SHOW RESOURCE MONITORS` is now inside `resource_monitors.*.show_output`. Here's the list of currently available fields: + - `name` + - `credit_quota` + - `used_credits` + - `remaining_credits` + - `level` + - `frequency` + - `start_time` + - `end_time` + - `suspend_at` + - `suspend_immediate_at` + - `created_on` + - `owner` + - `comment` + ## v0.94.x ➞ v0.95.0 ### *(breaking change)* database roles data source; field rename, schema structure changes, and adding missing filtering options diff --git a/docs/data-sources/database_roles.md b/docs/data-sources/database_roles.md index ea95e8737b..b62577cac9 100644 --- a/docs/data-sources/database_roles.md +++ b/docs/data-sources/database_roles.md @@ -14,8 +14,48 @@ description: |- ## Example Usage ```terraform -data "snowflake_database_roles" "db_roles" { - database = "MYDB" +# Simple usage +data "snowflake_database_roles" "simple" { + database = "database-name" +} + +output "simple_output" { + value = data.snowflake_database_roles.simple.database_roles +} + +# Filtering (like) +data "snowflake_database_roles" "like" { + database = "database-name" + like = "database_role-name" +} + +output "like_output" { + value = data.snowflake_database_roles.like.database_roles +} + +# Ensure the number of database roles is equal to at least one element (with the use of postcondition) +data "snowflake_database_roles" "assert_with_postcondition" { + database = "database-name" + like = "database_role-name-%" + lifecycle { + postcondition { + condition = length(self.database_roles) > 0 + error_message = "there should be at least one database role" + } + } +} + +# Ensure the number of database roles is equal to at exactly one element (with the use of check block) +check "database_role_check" { + data "snowflake_resource_monitors" "assert_with_check_block" { + database = "database-name" + like = "database_role-name" + } + + assert { + condition = length(data.snowflake_database_roles.assert_with_check_block.database_roles) == 1 + error_message = "Database roles filtered by '${data.snowflake_database_roles.assert_with_check_block.like}' returned ${length(data.snowflake_database_roles.assert_with_check_block.database_roles)} database roles where one was expected" + } } ``` diff --git a/docs/data-sources/resource_monitors.md b/docs/data-sources/resource_monitors.md index da386955ea..cfd067376b 100644 --- a/docs/data-sources/resource_monitors.md +++ b/docs/data-sources/resource_monitors.md @@ -2,34 +2,91 @@ page_title: "snowflake_resource_monitors Data Source - terraform-provider-snowflake" subcategory: "" description: |- - + Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for SHOW RESOURCE MONITORS https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors query (like is all supported). The results of SHOW is encapsulated in one output collection. --- # snowflake_resource_monitors (Data Source) - +Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is all supported). The results of SHOW is encapsulated in one output collection. ## Example Usage ```terraform -data "snowflake_resource_monitors" "current" { +# Simple usage +data "snowflake_resource_monitors" "simple" { +} + +output "simple_output" { + value = data.snowflake_resource_monitors.simple.resource_monitors +} + +# Filtering (like) +data "snowflake_resource_monitors" "like" { + like = "resource-monitor-name" +} + +output "like_output" { + value = data.snowflake_resource_monitors.like.resource_monitors +} + +# Ensure the number of resource monitors is equal to at least one element (with the use of postcondition) +data "snowflake_resource_monitors" "assert_with_postcondition" { + like = "resource-monitor-name-%" + lifecycle { + postcondition { + condition = length(self.resource_monitors) > 0 + error_message = "there should be at least one resource monitor" + } + } +} + +# Ensure the number of resource monitors is equal to at exactly one element (with the use of check block) +check "resource_monitor_check" { + data "snowflake_resource_monitors" "assert_with_check_block" { + like = "resource-monitor-name" + } + + assert { + condition = length(data.snowflake_resource_monitors.assert_with_check_block.resource_monitors) == 1 + error_message = "Resource monitors filtered by '${data.snowflake_resource_monitors.assert_with_check_block.like}' returned ${length(data.snowflake_resource_monitors.assert_with_check_block.resource_monitors)} resource monitors where one was expected" + } } ``` ## Schema +### Optional + +- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`). + ### Read-Only - `id` (String) The ID of this resource. -- `resource_monitors` (List of Object) The resource monitors in the database (see [below for nested schema](#nestedatt--resource_monitors)) +- `resource_monitors` (List of Object) Holds the aggregated output of all resource monitor details queries. (see [below for nested schema](#nestedatt--resource_monitors)) ### Nested Schema for `resource_monitors` Read-Only: +- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--resource_monitors--show_output)) + + +### Nested Schema for `resource_monitors.show_output` + +Read-Only: + - `comment` (String) -- `credit_quota` (String) +- `created_on` (String) +- `credit_quota` (Number) +- `end_time` (String) - `frequency` (String) +- `level` (String) - `name` (String) +- `owner` (String) +- `remaining_credits` (Number) +- `start_time` (String) +- `suspend_at` (Number) +- `suspend_immediate_at` (Number) +- `used_credits` (Number) diff --git a/docs/resources/resource_monitor.md b/docs/resources/resource_monitor.md index fab6feaf15..5d31d7c551 100644 --- a/docs/resources/resource_monitor.md +++ b/docs/resources/resource_monitor.md @@ -12,17 +12,25 @@ description: |- ## Example Usage ```terraform -resource "snowflake_resource_monitor" "monitor" { - name = "monitor" +resource "snowflake_resource_monitor" "minimal" { + name = "resource-monitor-name" + credit_quota = 100 + notify_triggers = [100] + suspend_immediate_trigger = 150 + notify_users = ["USERONE", "USERTWO"] +} + +resource "snowflake_resource_monitor" "complete" { + name = "resource-monitor-name" credit_quota = 100 frequency = "DAILY" - start_timestamp = "2020-12-07 00:00" - end_timestamp = "2021-12-07 00:00" + start_timestamp = "2030-12-07 00:00" + end_timestamp = "2035-12-07 00:00" - notify_triggers = [40, 50] - suspend_triggers = 50 - suspend_immediate_triggers = 90 + notify_triggers = [40, 50] + suspend_trigger = 50 + suspend_immediate_trigger = 90 notify_users = ["USERONE", "USERTWO"] } diff --git a/examples/data-sources/snowflake_database_roles/data-source.tf b/examples/data-sources/snowflake_database_roles/data-source.tf index d87ea770ae..b291b602c1 100644 --- a/examples/data-sources/snowflake_database_roles/data-source.tf +++ b/examples/data-sources/snowflake_database_roles/data-source.tf @@ -1,3 +1,43 @@ -data "snowflake_database_roles" "db_roles" { - database = "MYDB" -} \ No newline at end of file +# Simple usage +data "snowflake_database_roles" "simple" { + database = "database-name" +} + +output "simple_output" { + value = data.snowflake_database_roles.simple.database_roles +} + +# Filtering (like) +data "snowflake_database_roles" "like" { + database = "database-name" + like = "database_role-name" +} + +output "like_output" { + value = data.snowflake_database_roles.like.database_roles +} + +# Ensure the number of database roles is equal to at least one element (with the use of postcondition) +data "snowflake_database_roles" "assert_with_postcondition" { + database = "database-name" + like = "database_role-name-%" + lifecycle { + postcondition { + condition = length(self.database_roles) > 0 + error_message = "there should be at least one database role" + } + } +} + +# Ensure the number of database roles is equal to at exactly one element (with the use of check block) +check "database_role_check" { + data "snowflake_resource_monitors" "assert_with_check_block" { + database = "database-name" + like = "database_role-name" + } + + assert { + condition = length(data.snowflake_database_roles.assert_with_check_block.database_roles) == 1 + error_message = "Database roles filtered by '${data.snowflake_database_roles.assert_with_check_block.like}' returned ${length(data.snowflake_database_roles.assert_with_check_block.database_roles)} database roles where one was expected" + } +} diff --git a/examples/data-sources/snowflake_resource_monitors/data-source.tf b/examples/data-sources/snowflake_resource_monitors/data-source.tf index aec8f1fcf6..4ad25784cc 100644 --- a/examples/data-sources/snowflake_resource_monitors/data-source.tf +++ b/examples/data-sources/snowflake_resource_monitors/data-source.tf @@ -1,2 +1,39 @@ -data "snowflake_resource_monitors" "current" { -} \ No newline at end of file +# Simple usage +data "snowflake_resource_monitors" "simple" { +} + +output "simple_output" { + value = data.snowflake_resource_monitors.simple.resource_monitors +} + +# Filtering (like) +data "snowflake_resource_monitors" "like" { + like = "resource-monitor-name" +} + +output "like_output" { + value = data.snowflake_resource_monitors.like.resource_monitors +} + +# Ensure the number of resource monitors is equal to at least one element (with the use of postcondition) +data "snowflake_resource_monitors" "assert_with_postcondition" { + like = "resource-monitor-name-%" + lifecycle { + postcondition { + condition = length(self.resource_monitors) > 0 + error_message = "there should be at least one resource monitor" + } + } +} + +# Ensure the number of resource monitors is equal to at exactly one element (with the use of check block) +check "resource_monitor_check" { + data "snowflake_resource_monitors" "assert_with_check_block" { + like = "resource-monitor-name" + } + + assert { + condition = length(data.snowflake_resource_monitors.assert_with_check_block.resource_monitors) == 1 + error_message = "Resource monitors filtered by '${data.snowflake_resource_monitors.assert_with_check_block.like}' returned ${length(data.snowflake_resource_monitors.assert_with_check_block.resource_monitors)} resource monitors where one was expected" + } +} diff --git a/examples/resources/snowflake_resource_monitor/resource.tf b/examples/resources/snowflake_resource_monitor/resource.tf index 7bcd191351..f94aaed5ea 100644 --- a/examples/resources/snowflake_resource_monitor/resource.tf +++ b/examples/resources/snowflake_resource_monitor/resource.tf @@ -1,14 +1,22 @@ -resource "snowflake_resource_monitor" "monitor" { - name = "monitor" +resource "snowflake_resource_monitor" "minimal" { + name = "resource-monitor-name" + credit_quota = 100 + notify_triggers = [100] + suspend_immediate_trigger = 150 + notify_users = ["USERONE", "USERTWO"] +} + +resource "snowflake_resource_monitor" "complete" { + name = "resource-monitor-name" credit_quota = 100 frequency = "DAILY" - start_timestamp = "2020-12-07 00:00" - end_timestamp = "2021-12-07 00:00" + start_timestamp = "2030-12-07 00:00" + end_timestamp = "2035-12-07 00:00" - notify_triggers = [40, 50] - suspend_triggers = 50 - suspend_immediate_triggers = 90 + notify_triggers = [40, 50] + suspend_trigger = 50 + suspend_immediate_trigger = 90 notify_users = ["USERONE", "USERTWO"] } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go index 02acd913a9..aee983b4b9 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go @@ -1,6 +1,21 @@ package resourceshowoutputassert -import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" +) + +// ResourceMonitorDatasourceShowOutput is a temporary workaround to have better show output assertions in data source acceptance tests. +func ResourceMonitorDatasourceShowOutput(t *testing.T, name string) *ResourceMonitorShowOutputAssert { + t.Helper() + + u := ResourceMonitorShowOutputAssert{ + ResourceAssert: assert.NewDatasourceAssert("data."+name, "show_output", "resource_monitors.0."), + } + u.AddAssertion(assert.ValueSet("show_output.#", "1")) + return &u +} func (r *ResourceMonitorShowOutputAssert) HasStartTimeNotEmpty() *ResourceMonitorShowOutputAssert { r.AddAssertion(assert.ResourceShowOutputValuePresent("start_time")) diff --git a/pkg/datasources/resource_monitors.go b/pkg/datasources/resource_monitors.go index d07265e482..b6dff050ef 100644 --- a/pkg/datasources/resource_monitors.go +++ b/pkg/datasources/resource_monitors.go @@ -2,39 +2,35 @@ package datasources import ( "context" - "fmt" - "log" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var resourceMonitorsSchema = map[string]*schema.Schema{ + "like": { + Type: schema.TypeString, + Optional: true, + Description: "Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).", + }, "resource_monitors": { Type: schema.TypeList, Computed: true, - Description: "The resource monitors in the database", + Description: "Holds the aggregated output of all resource monitor details queries.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Computed: true, - }, - "frequency": { - Type: schema.TypeString, - Computed: true, - }, - "credit_quota": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Computed: true, + resources.ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of SHOW RESOURCE MONITORS.", + Elem: &schema.Resource{ + Schema: schemas.ShowResourceMonitorSchema, + }, }, }, }, @@ -43,41 +39,41 @@ var resourceMonitorsSchema = map[string]*schema.Schema{ func ResourceMonitors() *schema.Resource { return &schema.Resource{ - Read: ReadResourceMonitors, - Schema: resourceMonitorsSchema, + ReadContext: ReadResourceMonitors, + Schema: resourceMonitorsSchema, + Description: "Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is all supported). The results of SHOW is encapsulated in one output collection.", } } -func ReadResourceMonitors(d *schema.ResourceData, meta interface{}) error { +func ReadResourceMonitors(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - ctx := context.Background() - account, err := client.ContextFunctions.CurrentSessionDetails(ctx) - if err != nil { - log.Print("[DEBUG] unable to retrieve current account") - d.SetId("") - return nil - } + opts := new(sdk.ShowResourceMonitorOptions) - d.SetId(fmt.Sprintf("%s.%s", account.Account, account.Region)) + if likePattern, ok := d.GetOk("like"); ok { + opts.Like = &sdk.Like{ + Pattern: sdk.String(likePattern.(string)), + } + } - extractedResourceMonitors, err := client.ResourceMonitors.Show(ctx, &sdk.ShowResourceMonitorOptions{}) + resourceMonitors, err := client.ResourceMonitors.Show(ctx, opts) if err != nil { - log.Printf("[DEBUG] unable to parse resource monitors in account (%s)", d.Id()) - d.SetId("") - return nil + return diag.FromErr(err) } + d.SetId("resource_monitors_read") - resourceMonitors := make([]map[string]any, len(extractedResourceMonitors)) - - 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, + flattenedResourceMonitors := make([]map[string]any, len(resourceMonitors)) + for i, resourceMonitor := range resourceMonitors { + resourceMonitor := resourceMonitor + flattenedResourceMonitors[i] = map[string]any{ + resources.ShowOutputAttributeName: []map[string]any{schemas.ResourceMonitorToSchema(&resourceMonitor)}, } } - return d.Set("resource_monitors", resourceMonitors) + err = d.Set("resource_monitors", flattenedResourceMonitors) + if err != nil { + return diag.FromErr(err) + } + + return nil } diff --git a/pkg/datasources/resource_monitors_acceptance_test.go b/pkg/datasources/resource_monitors_acceptance_test.go index b1abf7df28..01d2310aa9 100644 --- a/pkg/datasources/resource_monitors_acceptance_test.go +++ b/pkg/datasources/resource_monitors_acceptance_test.go @@ -4,6 +4,10 @@ import ( "fmt" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -11,7 +15,9 @@ import ( ) func TestAcc_ResourceMonitors(t *testing.T) { - resourceMonitorName := acc.TestClient().Ids.Alpha() + prefix := "data_source_resource_monitor_" + resourceMonitorName := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) + resourceMonitorName2 := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -19,28 +25,51 @@ func TestAcc_ResourceMonitors(t *testing.T) { TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: resourceMonitors(resourceMonitorName), + Config: resourceMonitors(resourceMonitorName.Name(), resourceMonitorName2.Name(), prefix+"%"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_resource_monitors.s", "resource_monitors.#"), - resource.TestCheckResourceAttrSet("data.snowflake_resource_monitors.s", "resource_monitors.0.name"), + resource.TestCheckResourceAttr("data.snowflake_resource_monitors.test", "resource_monitors.#", "2"), + ), + }, + { + Config: resourceMonitors(resourceMonitorName.Name(), resourceMonitorName2.Name(), resourceMonitorName.Name()), + Check: assert.AssertThat(t, + resourceshowoutputassert.ResourceMonitorDatasourceShowOutput(t, "snowflake_resource_monitors.test"). + HasName(resourceMonitorName.Name()). + HasCreditQuota(5). + HasUsedCredits(0). + HasRemainingCredits(5). + HasLevel(""). + HasFrequency(sdk.FrequencyMonthly). + HasStartTimeNotEmpty(). + HasEndTime(""). + HasSuspendAt(0). + HasSuspendImmediateAt(0). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), ), }, }, }) } -func resourceMonitors(resourceMonitorName string) string { +func resourceMonitors(resourceMonitorName, resourceMonitorName2, searchPrefix string) string { return fmt.Sprintf(` - resource snowflake_resource_monitor "s"{ - name = "%v" + resource "snowflake_resource_monitor" "rm1" { + name = "%s" credit_quota = 5 } - data snowflake_resource_monitors "s" { - depends_on = [snowflake_resource_monitor.s] + resource "snowflake_resource_monitor" "rm2" { + name = "%s" + credit_quota = 15 + } + + data "snowflake_resource_monitors" "test" { + depends_on = [ snowflake_resource_monitor.rm1, snowflake_resource_monitor.rm2 ] + like = "%s" } - `, resourceMonitorName) + `, resourceMonitorName, resourceMonitorName2, searchPrefix) } From c40a23e55d9c928b9afcf38e00fad6bf61cdae10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Wed, 11 Sep 2024 12:13:15 +0200 Subject: [PATCH 4/7] Add resource monitor data source --- MIGRATION_GUIDE.md | 4 +- docs/data-sources/resource_monitors.md | 2 + docs/resources/resource_monitor.md | 7 +++- .../bettertestspoc/config/config.go | 2 + .../config/model/database_role_model_gen.go | 19 --------- .../model/gen/templates/definition.tmpl | 16 -------- .../config/model/user_model_gen.go | 19 --------- .../config/model/view_model_gen.go | 19 --------- .../config/model/warehouse_model_gen.go | 19 --------- .../data-sources/resource_monitors.md.tmpl | 24 ++++++++++++ templates/resources/resource_monitor.md.tmpl | 39 +++++++++++++++++++ 11 files changed, 75 insertions(+), 95 deletions(-) create mode 100644 templates/data-sources/resource_monitors.md.tmpl create mode 100644 templates/resources/resource_monitor.md.tmpl diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 9887f5de19..82191961f6 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -8,8 +8,8 @@ across different versions. ### *(breaking change)* resource_monitor resource Removed fields: -- `set_for_account` -- `warehouses` +- `set_for_account` (will be settable on account resource, right now, the preferred way is to set it through unsafe_execute resource) +- `warehouses` (can be set on warehouse resource, optionally through unsafe_execute resource) - `suspend_triggers` (now, `suspend_trigger` should be used) - `suspend_immediate_triggers` (now, `suspend_immediate_trigger` should be used) diff --git a/docs/data-sources/resource_monitors.md b/docs/data-sources/resource_monitors.md index cfd067376b..a804228a3d 100644 --- a/docs/data-sources/resource_monitors.md +++ b/docs/data-sources/resource_monitors.md @@ -5,6 +5,8 @@ description: |- Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for SHOW RESOURCE MONITORS https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors query (like is all supported). The results of SHOW is encapsulated in one output collection. --- +!> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. + # snowflake_resource_monitors (Data Source) Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is all supported). The results of SHOW is encapsulated in one output collection. diff --git a/docs/resources/resource_monitor.md b/docs/resources/resource_monitor.md index 5d31d7c551..df8e281f01 100644 --- a/docs/resources/resource_monitor.md +++ b/docs/resources/resource_monitor.md @@ -5,6 +5,12 @@ description: |- --- +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. + +**! Warning !** Due to Snowflake limitations, the following actions are not supported: +- Cannot create resource monitors with only triggers set, any other attribute has to be set. +- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). It has to be re-created without triggers to fully unset them. + # snowflake_resource_monitor (Resource) @@ -35,7 +41,6 @@ resource "snowflake_resource_monitor" "complete" { notify_users = ["USERONE", "USERTWO"] } ``` - -> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). diff --git a/pkg/acceptance/bettertestspoc/config/config.go b/pkg/acceptance/bettertestspoc/config/config.go index 7bc3a98531..3135e9a2a8 100644 --- a/pkg/acceptance/bettertestspoc/config/config.go +++ b/pkg/acceptance/bettertestspoc/config/config.go @@ -99,6 +99,8 @@ func FromModel(t *testing.T, model ResourceModel) string { return s } +// ConfigVariablesFromModel constructs config.Variables needed in acceptance tests that are using ConfigVariables in +// combination with ConfigDirectory. It's necessary for cases not supported by FromModel, like lists of objects. func ConfigVariablesFromModel(t *testing.T, model ResourceModel) tfconfig.Variables { t.Helper() variables := make(tfconfig.Variables) diff --git a/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go index 753f3bc5e7..df3106e1cc 100644 --- a/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/database_role_model_gen.go @@ -3,9 +3,6 @@ package model import ( - "reflect" - "strings" - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -46,22 +43,6 @@ func DatabaseRoleWithDefaultMeta( return d } -func (r *DatabaseRoleModel) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} - ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl b/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl index 413bf574a0..c61398e7dd 100644 --- a/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl +++ b/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl @@ -51,19 +51,3 @@ func {{ .Name }}WithDefaultMeta( {{- end -}} return {{ $modelVar }} } - -func (r *{{ $modelName }}) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} diff --git a/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go index 72ba22b52b..32eb8218ee 100644 --- a/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/user_model_gen.go @@ -3,9 +3,6 @@ package model import ( - "reflect" - "strings" - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -119,22 +116,6 @@ func UserWithDefaultMeta( return u } -func (r *UserModel) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} - ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go index f5a3db8962..1afe8859d8 100644 --- a/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/view_model_gen.go @@ -3,9 +3,6 @@ package model import ( - "reflect" - "strings" - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -66,22 +63,6 @@ func ViewWithDefaultMeta( return v } -func (r *ViewModel) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} - ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go index 39760f3521..388b71ce33 100644 --- a/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/warehouse_model_gen.go @@ -3,9 +3,6 @@ package model import ( - "reflect" - "strings" - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -55,22 +52,6 @@ func WarehouseWithDefaultMeta( return w } -func (r *WarehouseModel) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} - ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/templates/data-sources/resource_monitors.md.tmpl b/templates/data-sources/resource_monitors.md.tmpl new file mode 100644 index 0000000000..abd91a8e36 --- /dev/null +++ b/templates/data-sources/resource_monitors.md.tmpl @@ -0,0 +1,24 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/data-sources/%s/data-source.tf" .Name)}} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/resource_monitor.md.tmpl b/templates/resources/resource_monitor.md.tmpl new file mode 100644 index 0000000000..06d9767169 --- /dev/null +++ b/templates/resources/resource_monitor.md.tmpl @@ -0,0 +1,39 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. + +**! Warning !** Due to Snowflake limitations, the following actions are not supported: +- Cannot create resource monitors with only triggers set, any other attribute has to be set. +- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). It has to be re-created without triggers to fully unset them. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources). + + +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} From bcd583c658c9d23f0a84ab8e8cc448fd496c2205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Thu, 12 Sep 2024 12:03:53 +0200 Subject: [PATCH 5/7] Changes after review --- docs/data-sources/database_roles.md | 31 ++++++++---- docs/data-sources/resource_monitors.md | 4 +- docs/resources/resource_monitor.md | 9 ++-- .../snowflake_database_roles/data-source.tf | 27 +++++++--- .../snowflake_resource_monitor/resource.tf | 9 ++-- .../model/resource_monitor_model_gen.go | 19 ------- pkg/datasources/database_roles.go | 1 + pkg/datasources/resource_monitors.go | 2 +- .../resource_monitors_acceptance_test.go | 3 ++ .../resource_monitor_acceptance_test.go | 49 +++++++++++++++++++ 10 files changed, 106 insertions(+), 48 deletions(-) diff --git a/docs/data-sources/database_roles.md b/docs/data-sources/database_roles.md index b62577cac9..baeb080b28 100644 --- a/docs/data-sources/database_roles.md +++ b/docs/data-sources/database_roles.md @@ -2,21 +2,21 @@ page_title: "snowflake_database_roles Data Source - terraform-provider-snowflake" subcategory: "" description: |- - + Datasource used to get details of filtered database roles. Filtering is aligned with the current possibilities for SHOW DATABASE ROLES https://docs.snowflake.com/en/sql-reference/sql/show-database-roles query (like and limit are supported). The results of SHOW is encapsulated in show_output collection. --- !> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. # snowflake_database_roles (Data Source) - +Datasource used to get details of filtered database roles. Filtering is aligned with the current possibilities for [SHOW DATABASE ROLES](https://docs.snowflake.com/en/sql-reference/sql/show-database-roles) query (`like` and `limit` are supported). The results of SHOW is encapsulated in show_output collection. ## Example Usage ```terraform # Simple usage data "snowflake_database_roles" "simple" { - database = "database-name" + in_database = "database-name" } output "simple_output" { @@ -25,18 +25,31 @@ output "simple_output" { # Filtering (like) data "snowflake_database_roles" "like" { - database = "database-name" - like = "database_role-name" + in_database = "database-name" + like = "database_role-name" } output "like_output" { value = data.snowflake_database_roles.like.database_roles } +# Filtering (limit) +data "snowflake_database_roles" "limit" { + in_database = "database-name" + limit { + rows = 10 + from = "prefix-" + } +} + +output "limit_output" { + value = data.snowflake_database_roles.limit.database_roles +} + # Ensure the number of database roles is equal to at least one element (with the use of postcondition) data "snowflake_database_roles" "assert_with_postcondition" { - database = "database-name" - like = "database_role-name-%" + in_database = "database-name" + like = "database_role-name-%" lifecycle { postcondition { condition = length(self.database_roles) > 0 @@ -48,8 +61,8 @@ data "snowflake_database_roles" "assert_with_postcondition" { # Ensure the number of database roles is equal to at exactly one element (with the use of check block) check "database_role_check" { data "snowflake_resource_monitors" "assert_with_check_block" { - database = "database-name" - like = "database_role-name" + in_database = "database-name" + like = "database_role-name" } assert { diff --git a/docs/data-sources/resource_monitors.md b/docs/data-sources/resource_monitors.md index a804228a3d..f0da9f3394 100644 --- a/docs/data-sources/resource_monitors.md +++ b/docs/data-sources/resource_monitors.md @@ -2,14 +2,14 @@ page_title: "snowflake_resource_monitors Data Source - terraform-provider-snowflake" subcategory: "" description: |- - Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for SHOW RESOURCE MONITORS https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors query (like is all supported). The results of SHOW is encapsulated in one output collection. + Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for SHOW RESOURCE MONITORS https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors query (like is supported). The results of SHOW is encapsulated in show_output collection. --- !> **V1 release candidate** This data source was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. # snowflake_resource_monitors (Data Source) -Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is all supported). The results of SHOW is encapsulated in one output collection. +Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is supported). The results of SHOW is encapsulated in show_output collection. ## Example Usage diff --git a/docs/resources/resource_monitor.md b/docs/resources/resource_monitor.md index df8e281f01..4a14f3a5c4 100644 --- a/docs/resources/resource_monitor.md +++ b/docs/resources/resource_monitor.md @@ -19,11 +19,10 @@ description: |- ```terraform resource "snowflake_resource_monitor" "minimal" { - name = "resource-monitor-name" - credit_quota = 100 - notify_triggers = [100] - suspend_immediate_trigger = 150 - notify_users = ["USERONE", "USERTWO"] + name = "resource-monitor-name" + credit_quota = 100 + suspend_trigger = 100 + notify_users = ["USERONE", "USERTWO"] } resource "snowflake_resource_monitor" "complete" { diff --git a/examples/data-sources/snowflake_database_roles/data-source.tf b/examples/data-sources/snowflake_database_roles/data-source.tf index b291b602c1..ff07fdc68b 100644 --- a/examples/data-sources/snowflake_database_roles/data-source.tf +++ b/examples/data-sources/snowflake_database_roles/data-source.tf @@ -1,6 +1,6 @@ # Simple usage data "snowflake_database_roles" "simple" { - database = "database-name" + in_database = "database-name" } output "simple_output" { @@ -9,18 +9,31 @@ output "simple_output" { # Filtering (like) data "snowflake_database_roles" "like" { - database = "database-name" - like = "database_role-name" + in_database = "database-name" + like = "database_role-name" } output "like_output" { value = data.snowflake_database_roles.like.database_roles } +# Filtering (limit) +data "snowflake_database_roles" "limit" { + in_database = "database-name" + limit { + rows = 10 + from = "prefix-" + } +} + +output "limit_output" { + value = data.snowflake_database_roles.limit.database_roles +} + # Ensure the number of database roles is equal to at least one element (with the use of postcondition) data "snowflake_database_roles" "assert_with_postcondition" { - database = "database-name" - like = "database_role-name-%" + in_database = "database-name" + like = "database_role-name-%" lifecycle { postcondition { condition = length(self.database_roles) > 0 @@ -32,8 +45,8 @@ data "snowflake_database_roles" "assert_with_postcondition" { # Ensure the number of database roles is equal to at exactly one element (with the use of check block) check "database_role_check" { data "snowflake_resource_monitors" "assert_with_check_block" { - database = "database-name" - like = "database_role-name" + in_database = "database-name" + like = "database_role-name" } assert { diff --git a/examples/resources/snowflake_resource_monitor/resource.tf b/examples/resources/snowflake_resource_monitor/resource.tf index f94aaed5ea..90f7912ef2 100644 --- a/examples/resources/snowflake_resource_monitor/resource.tf +++ b/examples/resources/snowflake_resource_monitor/resource.tf @@ -1,9 +1,8 @@ resource "snowflake_resource_monitor" "minimal" { - name = "resource-monitor-name" - credit_quota = 100 - notify_triggers = [100] - suspend_immediate_trigger = 150 - notify_users = ["USERONE", "USERTWO"] + name = "resource-monitor-name" + credit_quota = 100 + suspend_trigger = 100 + notify_users = ["USERONE", "USERTWO"] } resource "snowflake_resource_monitor" "complete" { diff --git a/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go index af1db9310a..e2ccebc599 100644 --- a/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/resource_monitor_model_gen.go @@ -3,9 +3,6 @@ package model import ( - "reflect" - "strings" - tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" @@ -48,22 +45,6 @@ func ResourceMonitorWithDefaultMeta( return r } -func (r *ResourceMonitorModel) ToConfigVariables() tfconfig.Variables { - variables := make(tfconfig.Variables) - rType := reflect.TypeOf(r).Elem() - rValue := reflect.ValueOf(r).Elem() - for i := 0; i < rType.NumField(); i++ { - field := rType.Field(i) - if jsonTag, ok := field.Tag.Lookup("json"); ok { - name := strings.Split(jsonTag, ",")[0] - if fieldValue, ok := rValue.Field(i).Interface().(tfconfig.Variable); ok { - variables[name] = fieldValue - } - } - } - return variables -} - ///////////////////////////////// // below all the proper values // ///////////////////////////////// diff --git a/pkg/datasources/database_roles.go b/pkg/datasources/database_roles.go index ce45c48257..615951e890 100644 --- a/pkg/datasources/database_roles.go +++ b/pkg/datasources/database_roles.go @@ -66,6 +66,7 @@ func DatabaseRoles() *schema.Resource { return &schema.Resource{ ReadContext: ReadDatabaseRoles, Schema: databaseRolesSchema, + Description: "Datasource used to get details of filtered database roles. Filtering is aligned with the current possibilities for [SHOW DATABASE ROLES](https://docs.snowflake.com/en/sql-reference/sql/show-database-roles) query (`like` and `limit` are supported). The results of SHOW is encapsulated in show_output collection.", } } diff --git a/pkg/datasources/resource_monitors.go b/pkg/datasources/resource_monitors.go index b6dff050ef..0af5724842 100644 --- a/pkg/datasources/resource_monitors.go +++ b/pkg/datasources/resource_monitors.go @@ -41,7 +41,7 @@ func ResourceMonitors() *schema.Resource { return &schema.Resource{ ReadContext: ReadResourceMonitors, Schema: resourceMonitorsSchema, - Description: "Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is all supported). The results of SHOW is encapsulated in one output collection.", + Description: "Datasource used to get details of filtered resource monitors. Filtering is aligned with the current possibilities for [SHOW RESOURCE MONITORS](https://docs.snowflake.com/en/sql-reference/sql/show-resource-monitors) query (`like` is supported). The results of SHOW is encapsulated in show_output collection.", } } diff --git a/pkg/datasources/resource_monitors_acceptance_test.go b/pkg/datasources/resource_monitors_acceptance_test.go index 01d2310aa9..d1cc0681de 100644 --- a/pkg/datasources/resource_monitors_acceptance_test.go +++ b/pkg/datasources/resource_monitors_acceptance_test.go @@ -26,15 +26,18 @@ func TestAcc_ResourceMonitors(t *testing.T) { tfversion.RequireAbove(tfversion.Version1_5_0), }, Steps: []resource.TestStep{ + // Filter by prefix pattern (expect 2 items) { Config: resourceMonitors(resourceMonitorName.Name(), resourceMonitorName2.Name(), prefix+"%"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("data.snowflake_resource_monitors.test", "resource_monitors.#", "2"), ), }, + // Filter by exact name (expect 1 item) { Config: resourceMonitors(resourceMonitorName.Name(), resourceMonitorName2.Name(), resourceMonitorName.Name()), Check: assert.AssertThat(t, + assert.Check(resource.TestCheckResourceAttr("data.snowflake_resource_monitors.test", "resource_monitors.#", "1")), resourceshowoutputassert.ResourceMonitorDatasourceShowOutput(t, "snowflake_resource_monitors.test"). HasName(resourceMonitorName.Name()). HasCreditQuota(5). diff --git a/pkg/resources/resource_monitor_acceptance_test.go b/pkg/resources/resource_monitor_acceptance_test.go index c9461d9345..183e35cf04 100644 --- a/pkg/resources/resource_monitor_acceptance_test.go +++ b/pkg/resources/resource_monitor_acceptance_test.go @@ -1,6 +1,7 @@ package resources_test import ( + "fmt" "regexp" "testing" "time" @@ -779,3 +780,51 @@ func TestAcc_ResourceMonitor_Issue1500_AlteringWithOnlyTriggers(t *testing.T) { }, }) } + +// proves that fields that were present in the previous versions are not kept in the state after the upgrade +func TestAcc_ResourceMonitor_SetForWarehouse(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.90.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: fmt.Sprintf(` +resource "snowflake_resource_monitor" "test" { + name = "%s" + credit_quota = 100 + suspend_trigger = 100 + warehouses = [ "SNOWFLAKE" ] +} +`, id.Name()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "warehouses.#", "1"), + ), + }, + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: fmt.Sprintf(` +resource "snowflake_resource_monitor" "test" { + name = "%s" + credit_quota = 100 + suspend_trigger = 100 +} +`, id.Name()), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "warehouses"), + resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "warehouses.#"), + ), + }, + }, + }) +} From 86260ac3d983a3720e6db4eef73d31cb9f8d4aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Fri, 13 Sep 2024 09:59:49 +0200 Subject: [PATCH 6/7] Changes after review --- MIGRATION_GUIDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 82191961f6..3a95ebfc26 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,7 +9,7 @@ across different versions. ### *(breaking change)* resource_monitor resource Removed fields: - `set_for_account` (will be settable on account resource, right now, the preferred way is to set it through unsafe_execute resource) -- `warehouses` (can be set on warehouse resource, optionally through unsafe_execute resource) +- `warehouses` (can be set on warehouse resource, optionally through unsafe_execute resource only if the warehouse is not managed by Terraform) - `suspend_triggers` (now, `suspend_trigger` should be used) - `suspend_immediate_triggers` (now, `suspend_immediate_trigger` should be used) From 45206759ece96a879b5fdc204f2b90e16885cdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Fri, 13 Sep 2024 11:47:44 +0200 Subject: [PATCH 7/7] Changes after review --- docs/resources/resource_monitor.md | 11 +- .../snowflake_resource_monitor/resource.tf | 7 ++ pkg/resources/custom_diffs.go | 12 +++ pkg/resources/custom_diffs_test.go | 84 +++++++++++++++ pkg/resources/resource_monitor.go | 12 +-- .../resource_monitor_acceptance_test.go | 101 ++++++++++++++++++ templates/resources/resource_monitor.md.tmpl | 4 +- 7 files changed, 221 insertions(+), 10 deletions(-) diff --git a/docs/resources/resource_monitor.md b/docs/resources/resource_monitor.md index 4a14f3a5c4..dea0fa29b3 100644 --- a/docs/resources/resource_monitor.md +++ b/docs/resources/resource_monitor.md @@ -7,9 +7,11 @@ description: |- !> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. +~> **Note** For more details about resource monitor usage, please visit [this guide on Snowflake documentation page](https://docs.snowflake.com/en/user-guide/resource-monitors). + **! Warning !** Due to Snowflake limitations, the following actions are not supported: - Cannot create resource monitors with only triggers set, any other attribute has to be set. -- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). It has to be re-created without triggers to fully unset them. +- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). That's why when you unset all the triggers on a resource monitor, it will be automatically recreated. # snowflake_resource_monitor (Resource) @@ -18,7 +20,14 @@ description: |- ## Example Usage ```terraform +// Note: Without credit quota and triggers specified in the configuration, the resource monitor is not performing any work. +// More on resource monitor usage: https://docs.snowflake.com/en/user-guide/resource-monitors. resource "snowflake_resource_monitor" "minimal" { + name = "resource-monitor-name" +} + +// Note: Resource monitors have to be attached to account or warehouse to be able to track credit usage. +resource "snowflake_resource_monitor" "minimal_working" { name = "resource-monitor-name" credit_quota = 100 suspend_trigger = 100 diff --git a/examples/resources/snowflake_resource_monitor/resource.tf b/examples/resources/snowflake_resource_monitor/resource.tf index 90f7912ef2..45273e869d 100644 --- a/examples/resources/snowflake_resource_monitor/resource.tf +++ b/examples/resources/snowflake_resource_monitor/resource.tf @@ -1,4 +1,11 @@ +// Note: Without credit quota and triggers specified in the configuration, the resource monitor is not performing any work. +// More on resource monitor usage: https://docs.snowflake.com/en/user-guide/resource-monitors. resource "snowflake_resource_monitor" "minimal" { + name = "resource-monitor-name" +} + +// Note: Resource monitors have to be attached to account or warehouse to be able to track credit usage. +resource "snowflake_resource_monitor" "minimal_working" { name = "resource-monitor-name" credit_quota = 100 suspend_trigger = 100 diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go index e7c8bce2c3..e685bae834 100644 --- a/pkg/resources/custom_diffs.go +++ b/pkg/resources/custom_diffs.go @@ -165,3 +165,15 @@ func ParametersCustomDiff[T ~string](parametersProvider func(context.Context, Re return customdiff.All(diffFunctions...)(ctx, d, meta) } } + +func ForceNewIfAllKeysAreNotSet(key string, keys ...string) schema.CustomizeDiffFunc { + return customdiff.ForceNewIf(key, func(ctx context.Context, d *schema.ResourceDiff, meta any) bool { + allUnset := true + for _, k := range keys { + if _, ok := d.GetOk(k); ok { + allUnset = false + } + } + return allUnset + }) +} diff --git a/pkg/resources/custom_diffs_test.go b/pkg/resources/custom_diffs_test.go index 14fcb2c848..19d5a338e9 100644 --- a/pkg/resources/custom_diffs_test.go +++ b/pkg/resources/custom_diffs_test.go @@ -5,6 +5,8 @@ import ( "strings" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -553,3 +555,85 @@ func Test_ComputedIfAnyAttributeChanged(t *testing.T) { assert.Nil(t, diff.Attributes["computed_value"]) }) } + +func TestForceNewIfAllKeysAreNotSet(t *testing.T) { + tests := []struct { + name string + stateValue map[string]string + rawConfigValue map[string]any + wantForceNew bool + }{ + { + name: "all values set to unset", + stateValue: map[string]string{ + "value": "123", + "value2": "string value", + "value3": "[one two]", + }, + rawConfigValue: map[string]any{}, + wantForceNew: true, + }, + { + name: "only value set to unset", + stateValue: map[string]string{ + "value": "123", + }, + rawConfigValue: map[string]any{}, + wantForceNew: true, + }, + { + name: "only value2 set to unset", + stateValue: map[string]string{ + "value2": "string value", + }, + rawConfigValue: map[string]any{}, + wantForceNew: true, + }, + { + name: "only value3 set to unset", + stateValue: map[string]string{ + "value3": "[one two]", + }, + rawConfigValue: map[string]any{}, + // We expect here to not re-create because value3 doesn't have a custom diff on it + // and the rest custom diffs don't work when the values are not set. + wantForceNew: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &schema.Provider{ + ResourcesMap: map[string]*schema.Resource{ + "test": { + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeInt, + }, + "value2": { + Type: schema.TypeString, + }, + "value3": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + CustomizeDiff: customdiff.All( + resources.ForceNewIfAllKeysAreNotSet("value", "value", "value2", "value3"), + resources.ForceNewIfAllKeysAreNotSet("value2", "value", "value2", "value3"), + ), + }, + }, + } + diff := calculateDiffFromAttributes( + t, + p, + tt.stateValue, + tt.rawConfigValue, + ) + assert.Equal(t, tt.wantForceNew, diff.RequiresNew()) + }) + } +} diff --git a/pkg/resources/resource_monitor.go b/pkg/resources/resource_monitor.go index f56c965418..f2b8d63db0 100644 --- a/pkg/resources/resource_monitor.go +++ b/pkg/resources/resource_monitor.go @@ -109,6 +109,9 @@ func ResourceMonitor() *schema.Resource { CustomizeDiff: customdiff.All( ComputedIfAnyAttributeChanged(resourceMonitorSchema, ShowOutputAttributeName, "notify_users", "credit_quota", "frequency", "start_timestamp", "end_timestamp", "notify_triggers", "suspend_trigger", "suspend_immediate_trigger"), + ForceNewIfAllKeysAreNotSet("notify_triggers", "notify_triggers", "suspend_trigger", "suspend_immediate_trigger"), + ForceNewIfAllKeysAreNotSet("suspend_trigger", "notify_triggers", "suspend_trigger", "suspend_immediate_trigger"), + ForceNewIfAllKeysAreNotSet("suspend_immediate_trigger", "notify_triggers", "suspend_trigger", "suspend_immediate_trigger"), ), } } @@ -383,15 +386,8 @@ func UpdateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any if len(triggers) > 0 { opts.Triggers = triggers - } else { - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Failed to update resource monitor.", - Detail: "Due to Snowflake limitations triggers cannot be completely removed form resource monitor after having at least 1 trigger. The only way it to re-create resource monitor without any triggers specified.", - }, - } } + // Else ForceNew, because Snowflake doesn't allow fully unsetting the triggers } // This is to prevent SQL compilation errors from Snowflake, because you cannot only alter triggers. diff --git a/pkg/resources/resource_monitor_acceptance_test.go b/pkg/resources/resource_monitor_acceptance_test.go index 183e35cf04..d720c91a2d 100644 --- a/pkg/resources/resource_monitor_acceptance_test.go +++ b/pkg/resources/resource_monitor_acceptance_test.go @@ -781,6 +781,107 @@ func TestAcc_ResourceMonitor_Issue1500_AlteringWithOnlyTriggers(t *testing.T) { }) } +func TestAcc_ResourceMonitor_RemovingAllTriggers(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + configModelWithNotifyTriggers := model.ResourceMonitor("test", id.Name()). + WithCreditQuota(100). + WithNotifyTriggersValue(configvariable.SetVariable( + configvariable.IntegerVariable(100), + configvariable.IntegerVariable(110), + )) + + configModelWithSuspendTrigger := model.ResourceMonitor("test", id.Name()). + WithCreditQuota(100). + WithSuspendTrigger(120) + + configModelWithSuspendImmediateTrigger := model.ResourceMonitor("test", id.Name()). + WithCreditQuota(100). + WithSuspendImmediateTrigger(120) + + configModelWithAllTriggers := model.ResourceMonitor("test", id.Name()). + WithCreditQuota(100). + WithNotifyTriggersValue(configvariable.SetVariable( + configvariable.IntegerVariable(100), + configvariable.IntegerVariable(110), + )). + WithSuspendTrigger(120). + WithSuspendImmediateTrigger(150) + + configModelWithoutTriggers := model.ResourceMonitor("test", id.Name()). + WithCreditQuota(100) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), + Steps: []resource.TestStep{ + // Config with all triggers + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithAllTriggers), + }, + // No triggers (force new expected) + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_resource_monitor.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithoutTriggers), + }, + // Config with only notify triggers + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithNotifyTriggers), + }, + // No triggers (force new expected) + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_resource_monitor.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithoutTriggers), + }, + // Config with only suspend trigger + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithSuspendTrigger), + }, + // No triggers (force new expected) + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_resource_monitor.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithoutTriggers), + }, + // Config with only suspend immediate trigger + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithSuspendImmediateTrigger), + }, + // No triggers (force new expected) + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_resource_monitor.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModelWithoutTriggers), + }, + }, + }) +} + // proves that fields that were present in the previous versions are not kept in the state after the upgrade func TestAcc_ResourceMonitor_SetForWarehouse(t *testing.T) { id := acc.TestClient().Ids.RandomAccountObjectIdentifier() diff --git a/templates/resources/resource_monitor.md.tmpl b/templates/resources/resource_monitor.md.tmpl index 06d9767169..0e6a0993bb 100644 --- a/templates/resources/resource_monitor.md.tmpl +++ b/templates/resources/resource_monitor.md.tmpl @@ -11,9 +11,11 @@ description: |- !> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0950--v0960) to use it. +~> **Note** For more details about resource monitor usage, please visit [this guide on Snowflake documentation page](https://docs.snowflake.com/en/user-guide/resource-monitors). + **! Warning !** Due to Snowflake limitations, the following actions are not supported: - Cannot create resource monitors with only triggers set, any other attribute has to be set. -- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). It has to be re-created without triggers to fully unset them. +- Once a resource monitor has at least one trigger assigned, it cannot fully unset them (has to have at least one trigger, doesn't matter of which type). That's why when you unset all the triggers on a resource monitor, it will be automatically recreated. # {{.Name}} ({{.Type}})