From 048b5106dc451ea999f1c314071f587cbd2eddc2 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] wip --- pkg/acceptance/bettertestspoc/README.md | 4 +- .../resource_monitor_snowflake_gen.go | 14 +- .../assert/resource_assertions.go | 19 +- .../resource_monitor_resource_ext.go | 23 + .../resource_monitor_show_output_ext.go | 23 + .../bettertestspoc/config/model/gen/model.go | 3 +- .../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 + pkg/resources/resource_monitor.go | 277 +++++--- .../resource_monitor_acceptance_test.go | 666 ++++++++++++------ .../TestAcc_ResourceMonitor/complete/test.tf | 17 + .../complete/variables.tf | 30 + .../only_triggers/test.tf | 12 + .../only_triggers/variables.tf | 10 + pkg/schemas/resource_monitor_gen.go | 8 +- pkg/sdk/resource_monitors.go | 23 +- .../resource_monitors_integration_test.go | 23 +- 19 files changed, 869 insertions(+), 356 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go 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/README.md b/pkg/acceptance/bettertestspoc/README.md index 09cae6cd388..1ddb5311c24 100644 --- a/pkg/acceptance/bettertestspoc/README.md +++ b/pkg/acceptance/bettertestspoc/README.md @@ -113,12 +113,12 @@ func (w *WarehouseModel) WithWarehouseSizeEnum(warehouseSize sdk.WarehouseSize) Each of the above assertion types/config models has its own generator and cleanup entry in our Makefile. You can generate config models with: ```shell - make clean-resource-model-builder generate-resource-model-builder + make clean-resource-model-builders generate-resource-model-builders ``` You can use cli flags: ```shell - make clean-resource-model-builder generate-resource-model-builder SF_TF_GENERATOR_ARGS='--dry-run --verbose' + make clean-resource-model-builders generate-resource-model-builders SF_TF_GENERATOR_ARGS='--dry-run --verbose' ``` To clean/generate all from this package run diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/resource_monitor_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/resource_monitor_snowflake_gen.go index 4705dac15d9..68caed2c640 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/resource_monitor_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/resource_monitor_snowflake_gen.go @@ -46,11 +46,8 @@ func (r *ResourceMonitorAssert) HasName(expected string) *ResourceMonitorAssert func (r *ResourceMonitorAssert) HasCreditQuota(expected float64) *ResourceMonitorAssert { r.AddAssertion(func(t *testing.T, o *sdk.ResourceMonitor) error { t.Helper() - if o.CreditQuota == nil { - return fmt.Errorf("expected credit quota to have value; got: nil") - } - if *o.CreditQuota != expected { - return fmt.Errorf("expected credit quota: %v; got: %v", expected, *o.CreditQuota) + if o.CreditQuota != expected { + return fmt.Errorf("expected credit quota: %v; got: %v", expected, o.CreditQuota) } return nil }) @@ -118,11 +115,8 @@ func (r *ResourceMonitorAssert) HasStartTime(expected string) *ResourceMonitorAs func (r *ResourceMonitorAssert) HasEndTime(expected string) *ResourceMonitorAssert { r.AddAssertion(func(t *testing.T, o *sdk.ResourceMonitor) error { t.Helper() - if o.EndTime == nil { - return fmt.Errorf("expected end time to have value; got: nil") - } - if *o.EndTime != expected { - return fmt.Errorf("expected end time: %v; got: %v", expected, *o.EndTime) + if o.EndTime != expected { + return fmt.Errorf("expected end time: %v; got: %v", expected, o.EndTime) } return nil }) diff --git a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go index 3a404a4bcf1..79f4e47ac04 100644 --- a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go +++ b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go @@ -59,9 +59,9 @@ func NewDatasourceAssert(name string, prefix string, additionalPrefix string) *R type resourceAssertionType string const ( + resourceAssertionTypeValuePresent = "VALUE_PRESENT" resourceAssertionTypeValueSet = "VALUE_SET" resourceAssertionTypeValueNotSet = "VALUE_NOT_SET" - resourceAssertionTypeValuePresent = "VALUE_PRESENT" ) type ResourceAssertion struct { @@ -75,6 +75,10 @@ func (r *ResourceAssert) AddAssertion(assertion ResourceAssertion) { r.assertions = append(r.assertions, assertion) } +func ValuePresent(fieldName string) ResourceAssertion { + return ResourceAssertion{fieldName: fieldName, resourceAssertionType: resourceAssertionTypeValuePresent} +} + func ValueSet(fieldName string, expected string) ResourceAssertion { return ResourceAssertion{fieldName: fieldName, expectedValue: expected, resourceAssertionType: resourceAssertionTypeValueSet} } @@ -105,7 +109,10 @@ func ResourceShowOutputValueSet(fieldName string, expected string) ResourceAsser return ResourceAssertion{fieldName: showOutputPrefix + fieldName, expectedValue: expected, resourceAssertionType: resourceAssertionTypeValueSet} } -// TODO [SNOW-1501905]: generate assertions with resourceAssertionTypeValuePresent +func ResourceShowOutputValueNotSet(fieldName string) ResourceAssertion { + return ResourceAssertion{fieldName: showOutputPrefix + fieldName, resourceAssertionType: resourceAssertionTypeValueNotSet} +} + func ResourceShowOutputValuePresent(fieldName string) ResourceAssertion { return ResourceAssertion{fieldName: showOutputPrefix + fieldName, resourceAssertionType: resourceAssertionTypeValuePresent} } @@ -181,9 +188,13 @@ func (r *ResourceAssert) ToTerraformImportStateCheckFunc(t *testing.T) resource. result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %w", r.id, r.prefix, i+1, len(r.assertions), err)) } case resourceAssertionTypeValueNotSet: - panic("implement") + if err := importchecks.TestCheckResourceAttrNotInInstanceState(r.id, a.fieldName)(s); err != nil { + result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %w", r.id, r.prefix, i+1, len(r.assertions), err)) + } case resourceAssertionTypeValuePresent: - panic("implement") + if err := importchecks.TestCheckResourceAttrInstanceStateSet(r.id, a.fieldName)(s); err != nil { + result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %w", r.id, r.prefix, i+1, len(r.assertions), err)) + } } } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/resource_monitor_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/resource_monitor_resource_ext.go index aeb7f6ac95f..ed763e98fa0 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/resource_monitor_resource_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/resource_monitor_resource_ext.go @@ -1,17 +1,40 @@ package resourceassert import ( + "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "strconv" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" ) +func (r *ResourceMonitorResourceAssert) HasStartTimestampNotEmpty() *ResourceMonitorResourceAssert { + r.AddAssertion(assert.ValuePresent("start_timestamp")) + return r +} + +func (r *ResourceMonitorResourceAssert) HasEndTimestampNotEmpty() *ResourceMonitorResourceAssert { + r.AddAssertion(assert.ValuePresent("end_timestamp")) + return r +} + func (r *ResourceMonitorResourceAssert) HasNotifyUsersLen(len int) *ResourceMonitorResourceAssert { r.AddAssertion(assert.ValueSet("notify_users.#", strconv.FormatInt(int64(len), 10))) return r } +func (r *ResourceMonitorResourceAssert) HasNotifyUser(index int, userName string) *ResourceMonitorResourceAssert { + r.AddAssertion(assert.ValueSet(fmt.Sprintf("notify_users.%d", index), userName)) + return r +} + func (r *ResourceMonitorResourceAssert) HasTriggerLen(len int) *ResourceMonitorResourceAssert { r.AddAssertion(assert.ValueSet("trigger.#", strconv.FormatInt(int64(len), 10))) return r } + +func (r *ResourceMonitorResourceAssert) HasTrigger(index int, threshold int, action sdk.TriggerAction) *ResourceMonitorResourceAssert { + r.AddAssertion(assert.ValueSet(fmt.Sprintf("trigger.%d.threshold", index), strconv.FormatInt(int64(threshold), 10))) + r.AddAssertion(assert.ValueSet(fmt.Sprintf("trigger.%d.on_threshold_reached", index), string(action))) + return r +} 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 new file mode 100644 index 00000000000..02acd913a97 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/resource_monitor_show_output_ext.go @@ -0,0 +1,23 @@ +package resourceshowoutputassert + +import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + +func (r *ResourceMonitorShowOutputAssert) HasStartTimeNotEmpty() *ResourceMonitorShowOutputAssert { + r.AddAssertion(assert.ResourceShowOutputValuePresent("start_time")) + return r +} + +func (r *ResourceMonitorShowOutputAssert) HasEndTimeNotEmpty() *ResourceMonitorShowOutputAssert { + r.AddAssertion(assert.ResourceShowOutputValuePresent("end_time")) + return r +} + +func (r *ResourceMonitorShowOutputAssert) HasCreatedOnNotEmpty() *ResourceMonitorShowOutputAssert { + r.AddAssertion(assert.ResourceShowOutputValuePresent("created_on")) + return r +} + +func (r *ResourceMonitorShowOutputAssert) HasOwnerNotEmpty() *ResourceMonitorShowOutputAssert { + r.AddAssertion(assert.ResourceShowOutputValuePresent("owner")) + return r +} diff --git a/pkg/acceptance/bettertestspoc/config/model/gen/model.go b/pkg/acceptance/bettertestspoc/config/model/gen/model.go index 2e5a2da5253..5681469382c 100644 --- a/pkg/acceptance/bettertestspoc/config/model/gen/model.go +++ b/pkg/acceptance/bettertestspoc/config/model/gen/model.go @@ -69,7 +69,8 @@ func ModelFromResourceSchemaDetails(resourceSchemaDetails genhelpers.ResourceSch Name: resourceSchemaDetails.ObjectName(), Attributes: attributes, PreambleModel: PreambleModel{ - PackageName: packageWithGenerateDirective, + PackageName: packageWithGenerateDirective, + AdditionalStandardImports: []string{"reflect", "strings"}, }, } } diff --git a/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl b/pkg/acceptance/bettertestspoc/config/model/gen/templates/definition.tmpl index c61398e7ddc..413bf574a01 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 480fd6ff42d..3e6b65fd880 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" @@ -43,6 +46,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 32eb8218eee..72ba22b52b8 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 388b71ce330..39760f3521a 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/resource_monitor.go b/pkg/resources/resource_monitor.go index 633541f0452..9840dbea229 100644 --- a/pkg/resources/resource_monitor.go +++ b/pkg/resources/resource_monitor.go @@ -4,21 +4,18 @@ import ( "context" "errors" "fmt" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "reflect" - "strings" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const StringDefaultValue = "Snowflake default value" - var resourceMonitorSchema = map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -27,7 +24,6 @@ var resourceMonitorSchema = map[string]*schema.Schema{ Description: "Identifier for the resource monitor; must be unique for your account.", DiffSuppressFunc: suppressIdentifierQuoting, }, - // TODO: This can be set to empty "notify_users": { Type: schema.TypeSet, Optional: true, @@ -39,40 +35,39 @@ var resourceMonitorSchema = map[string]*schema.Schema{ "credit_quota": { Type: schema.TypeInt, Optional: true, - Default: IntDefault, ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(1)), + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("credit_quota"), Description: "The number of credits allocated to the resource monitor per frequency interval. When total usage for all warehouses assigned to the monitor reaches this number for the current frequency interval, the resource monitor is considered to be at 100% of quota.", }, + // TODO: Describe that default it's MONTHLY, but after unsetting this field it will be the thing was previously set in the configuration; because no unset is available "frequency": { - Type: schema.TypeString, - Optional: true, - // TODO: No default ? By default it's MONTHLY - Default: StringDefaultValue, + Type: schema.TypeString, + Optional: true, RequiredWith: []string{"start_timestamp"}, ValidateDiagFunc: sdkValidation(sdk.ToResourceMonitorFrequency), DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToResourceMonitorFrequency), IgnoreChangeToCurrentSnowflakeValueInShow("frequency")), Description: "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`. If you specify `NEVER` for the frequency, the credit usage for the warehouse does not reset.", }, + // TODO: Describe that default it's MONTHLY, but after unsetting this field it will be the thing was previously set in the configuration; because no unset is available + // TODO: Describe that it's advised for now to specify full dates of format 2024-10-04 00:00 otherwise diffs may occur "start_timestamp": { - // TODO: Skip checking if start_timestamp == IMMEDIATELY - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"frequency"}, - DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { - return strings.ToUpper(oldValue) == "IMMEDIATELY" - }, - // TODO: Not detecting external changes, because it's hard - Description: "The date and time when the resource monitor starts monitoring credit usage for the assigned warehouses. If you set a `start_timestamp` for a resource monitor, you must also set `frequency`.", + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"frequency"}, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("start_time"), + Description: "The date and time when the resource monitor starts monitoring credit usage for the assigned warehouses. If you set a `start_timestamp` for a resource monitor, you must also set `frequency`.", }, + // TODO: Describe that it's advised for now to specify full dates of format 2024-10-04 00:00 otherwise diffs may occur "end_timestamp": { - Type: schema.TypeString, - Optional: true, - // TODO: Not detecting external changes, because it's hard - Description: "The date and time when the resource monitor suspends the assigned warehouses.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("end_time"), + Description: "The date and time when the resource monitor suspends the assigned warehouses.", }, "trigger": { Type: schema.TypeSet, Optional: true, + // TODO: Throw error on CREATE with only triggers (SQL compilation error). // TODO: Throw error on 0 triggers alter Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -103,21 +98,54 @@ var resourceMonitorSchema = map[string]*schema.Schema{ func ResourceMonitor() *schema.Resource { return &schema.Resource{ CreateContext: CreateResourceMonitor, - ReadContext: ReadResourceMonitor, + ReadContext: ReadResourceMonitor(true), UpdateContext: UpdateResourceMonitor, DeleteContext: DeleteResourceMonitor, Schema: resourceMonitorSchema, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportResourceMonitor, }, CustomizeDiff: customdiff.All( - ComputedIfAnyAttributeChanged(ShowOutputAttributeName, "notify_users", "credit_quota", "frequency", "start_timestamp", "end_timestamp", "trigger"), + ComputedIfAnyAttributeChanged(resourceMonitorSchema, ShowOutputAttributeName, "notify_users", "credit_quota", "frequency", "start_timestamp", "end_timestamp", "trigger"), ), } } +func ImportResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting resource monitor import") + client := meta.(*provider.Context).Client + + id, err := sdk.ParseAccountObjectIdentifier(d.Id()) + if err != nil { + return nil, err + } + + resourceMonitor, err := client.ResourceMonitors.ShowByID(ctx, id) + if err != nil { + return nil, err + } + + if err := d.Set("name", id.Name()); err != nil { + return nil, err + } + if err := d.Set("credit_quota", resourceMonitor.CreditQuota); err != nil { + return nil, err + } + if err := d.Set("frequency", resourceMonitor.Frequency); err != nil { + return nil, err + } + if err := d.Set("start_timestamp", resourceMonitor.StartTime); err != nil { + return nil, err + } + if err := d.Set("end_timestamp", resourceMonitor.EndTime); err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + func CreateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client id := sdk.NewAccountObjectIdentifier(d.Get("name").(string)) @@ -125,8 +153,8 @@ func CreateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any opts := new(sdk.CreateResourceMonitorOptions) with := new(sdk.ResourceMonitorWith) - if v := d.Get("credit_quota").(int); v != IntDefault { - with.CreditQuota = sdk.Pointer(v) + if v, ok := d.GetOk("credit_quota"); ok { + with.CreditQuota = sdk.Pointer(v.(int)) } if v, ok := d.GetOk("notify_users"); ok { @@ -137,11 +165,11 @@ func CreateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any Name: sdk.NewAccountObjectIdentifier(userId), } } - opts.With.NotifyUsers = &sdk.NotifyUsers{Users: users} + with.NotifyUsers = &sdk.NotifyUsers{Users: users} } - if v := d.Get("frequency").(string); v != SnowflakeReaderAccountType { - frequency, err := sdk.ToResourceMonitorFrequency(v) + if v, ok := d.GetOk("frequency"); ok { + frequency, err := sdk.ToResourceMonitorFrequency(v.(string)) if err != nil { return diag.FromErr(err) } @@ -175,87 +203,95 @@ func CreateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any d.SetId(helpers.EncodeResourceIdentifier(id)) - return ReadResourceMonitor(ctx, d, meta) + return ReadResourceMonitor(false)(ctx, d, meta) } -func ReadResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - client := meta.(*provider.Context).Client - id, err := sdk.ParseAccountObjectIdentifier(d.Id()) - if err != nil { - return diag.FromErr(err) - } +func ReadResourceMonitor(withExternalChangesMarking bool) schema.ReadContextFunc { + return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id, err := sdk.ParseAccountObjectIdentifier(d.Id()) + if err != nil { + return diag.FromErr(err) + } - resourceMonitor, err := client.ResourceMonitors.ShowByID(ctx, id) - if err != nil { - if errors.Is(err, sdk.ErrObjectNotFound) { - d.SetId("") - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Failed to query resource monitor. Marking the resource as removed.", - Detail: fmt.Sprintf("Resource Monitor: %s, Err: %s", id.FullyQualifiedName(), err), - }, + resourceMonitor, err := client.ResourceMonitors.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { // TODO: Test for sdk.ErrObjectNotExistOrAuthorized + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to query resource monitor. Marking the resource as removed.", + Detail: fmt.Sprintf("Resource Monitor: %s, Err: %s", id.FullyQualifiedName(), err), + }, + } } + return diag.FromErr(err) } - return diag.FromErr(err) - } - if err := d.Set("notify_users", resourceMonitor.NotifyUsers); err != nil { - return diag.FromErr(err) - } - - if resourceMonitor.CreditQuota != nil { - if err := d.Set("credit_quota", *resourceMonitor.CreditQuota); err != nil { + if err := d.Set("notify_users", resourceMonitor.NotifyUsers); err != nil { return diag.FromErr(err) } - } else { - if err := d.Set("credit_quota", IntDefault); err != nil { + + if withExternalChangesMarking { + if err = handleExternalChangesToObjectInShow(d, + showMapping{"credit_quota", "credit_quota", resourceMonitor.CreditQuota, resourceMonitor.CreditQuota, nil}, + showMapping{"frequency", "frequency", string(resourceMonitor.Frequency), resourceMonitor.Frequency, nil}, + showMapping{"start_time", "start_timestamp", resourceMonitor.StartTime, resourceMonitor.StartTime, nil}, + showMapping{"end_time", "end_timestamp", resourceMonitor.EndTime, resourceMonitor.EndTime, nil}, + ); err != nil { + return diag.FromErr(err) + } + } + + if err = setStateToValuesFromConfig(d, warehouseSchema, []string{ + "credit_quota", + "frequency", + "start_timestamp", + "end_timestamp", + }); err != nil { return diag.FromErr(err) } - } - if err := d.Set("frequency", resourceMonitor.Frequency); err != nil { - return diag.FromErr(err) - } + var triggers []any - var triggers []any + if len(resourceMonitor.NotifyAt) > 0 { + for _, notifyAt := range resourceMonitor.NotifyAt { + triggers = append(triggers, map[string]any{ + "threshold": notifyAt, + "on_threshold_reached": sdk.TriggerActionNotify, + }) + } + } - if len(resourceMonitor.NotifyAt) > 0 { - for _, notifyAt := range resourceMonitor.NotifyAt { + if resourceMonitor.SuspendAt != nil { triggers = append(triggers, map[string]any{ - "threshold": notifyAt, - "on_threshold_reached": sdk.TriggerActionNotify, + "threshold": resourceMonitor.SuspendAt, + "on_threshold_reached": sdk.TriggerActionSuspend, }) } - } - if resourceMonitor.SuspendAt != nil { - triggers = append(triggers, map[string]any{ - "threshold": resourceMonitor.SuspendAt, - "on_threshold_reached": sdk.TriggerActionSuspend, - }) - } + if resourceMonitor.SuspendImmediateAt != nil { + triggers = append(triggers, map[string]any{ + "threshold": resourceMonitor.SuspendImmediateAt, + "on_threshold_reached": sdk.TriggerActionSuspendImmediate, + }) + } - if resourceMonitor.SuspendImmediateAt != nil { - triggers = append(triggers, map[string]any{ - "threshold": resourceMonitor.SuspendImmediateAt, - "on_threshold_reached": sdk.TriggerActionSuspendImmediate, - }) - } + if err := d.Set("trigger", triggers); err != nil { + return diag.FromErr(err) + } - if err := d.Set("trigger", triggers); err != nil { - return diag.FromErr(err) - } + if err = d.Set(ShowOutputAttributeName, []map[string]any{schemas.ResourceMonitorToSchema(resourceMonitor)}); err != nil { + return diag.FromErr(err) + } - if err = d.Set(ShowOutputAttributeName, []map[string]any{schemas.ResourceMonitorToSchema(resourceMonitor)}); err != nil { - return diag.FromErr(err) - } + if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { + return diag.FromErr(err) + } - if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil { - return diag.FromErr(err) + return nil } - - return nil } func UpdateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { @@ -267,20 +303,23 @@ func UpdateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any } var runSetStatement bool + var runUnsetStatement bool opts := sdk.AlterResourceMonitorOptions{} set := sdk.ResourceMonitorSet{} + unset := sdk.ResourceMonitorUnset{} if d.HasChange("credit_quota") { runSetStatement = true - if v := d.Get("credit_quota").(int); v != IntDefault { - set.CreditQuota = sdk.Pointer(d.Get("credit_quota").(int)) + if v, ok := d.GetOk("credit_quota"); ok { + set.CreditQuota = sdk.Pointer(v.(int)) } else { - // TODO: Set to null - //set.CreditQuota = sdk.Pointer(d.Get("credit_quota").(int)) + runUnsetStatement = true + unset.CreditQuota = sdk.Bool(true) } } - if d.HasChange("frequency") || d.HasChange("start_timestamp") { + if (d.HasChange("frequency") || d.HasChange("start_timestamp")) && + (d.Get("frequency").(string) != "" && d.Get("start_timestamp").(string) != "") { runSetStatement = true frequency, err := sdk.ToResourceMonitorFrequency(d.Get("frequency").(string)) if err != nil { @@ -291,26 +330,31 @@ func UpdateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any } if d.HasChange("end_timestamp") { - runSetStatement = true - if v := d.Get("end_timestamp").(string); v != "" { - set.EndTimestamp = sdk.Pointer(d.Get("end_timestamp").(string)) + if v, ok := d.GetOk("end_timestamp"); ok { + runSetStatement = true + set.EndTimestamp = sdk.Pointer(v.(string)) } else { - // TODO: Set to null - //set.EndTimestamp = sdk.Pointer(d.Get("end_timestamp").(string)) + runUnsetStatement = true + unset.EndTimestamp = sdk.Bool(true) } } if d.HasChange("notify_users") { - runSetStatement = true userIds := expandStringList(d.Get("notify_users").(*schema.Set).List()) - users := make([]sdk.NotifiedUser, len(userIds)) - for i, userId := range userIds { - users[i] = sdk.NotifiedUser{ - Name: sdk.NewAccountObjectIdentifier(userId), + if len(userIds) > 0 { + runSetStatement = true + users := make([]sdk.NotifiedUser, len(userIds)) + for i, userId := range userIds { + users[i] = sdk.NotifiedUser{ + Name: sdk.NewAccountObjectIdentifier(userId), + } } - } - set.NotifyUsers = &sdk.NotifyUsers{ - Users: users, + set.NotifyUsers = &sdk.NotifyUsers{ + Users: users, + } + } else { + runUnsetStatement = true + unset.NotifyUsers = sdk.Bool(true) } } @@ -342,7 +386,16 @@ func UpdateResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any } } - return ReadResourceMonitor(ctx, d, meta) + if runUnsetStatement { + if unset != (sdk.ResourceMonitorUnset{}) { + opts.Unset = &unset + } + if err := client.ResourceMonitors.Alter(ctx, id, &opts); err != nil { + return diag.FromErr(err) + } + } + + return ReadResourceMonitor(false)(ctx, d, meta) } func DeleteResourceMonitor(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { diff --git a/pkg/resources/resource_monitor_acceptance_test.go b/pkg/resources/resource_monitor_acceptance_test.go index 0a64051fabe..bea09f60635 100644 --- a/pkg/resources/resource_monitor_acceptance_test.go +++ b/pkg/resources/resource_monitor_acceptance_test.go @@ -1,28 +1,28 @@ package resources_test import ( - "encoding/json" "fmt" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + configvariable "github.com/hashicorp/terraform-plugin-testing/config" "regexp" "strings" "testing" + "time" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" - "github.com/stretchr/testify/require" ) -func TestAcc_ResourceMonitor(t *testing.T) { +func TestAcc_ResourceMonitor_Basic(t *testing.T) { id := acc.TestClient().Ids.RandomAccountObjectIdentifier() configModel := model.ResourceMonitor("test", id.Name()) @@ -40,63 +40,73 @@ func TestAcc_ResourceMonitor(t *testing.T) { resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). HasNameString(id.Name()). HasFullyQualifiedNameString(id.FullyQualifiedName()). - HasCreditQuotaString("-1"). + HasNoCreditQuota(). HasNotifyUsersLen(0). - HasFrequencyString(string(sdk.FrequencyMonthly)). - HasNoStartTimestamp(). // TODO: Should be end of the last month + HasNoFrequency(). + HasNoStartTimestamp(). HasNoEndTimestamp(). HasTriggerLen(0), - resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(0). + HasUsedCredits(0). + HasRemainingCredits(0). + HasLevel(""). + HasFrequency(sdk.FrequencyMonthly). + HasStartTimeNotEmpty(). + HasEndTime(""). + HasSuspendAt(0). + HasSuspendImmediateAt(0). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), + ), + }, + { + ResourceName: "snowflake_resource_monitor.test", + ImportState: true, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedResourceMonitorResource(t, helpers.EncodeResourceIdentifier(id)). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("0"). + HasNotifyUsersLen(0). + HasFrequencyString(string(sdk.FrequencyMonthly)). + HasStartTimestampNotEmpty(). + HasEndTimestampString(""). + HasTriggerLen(0), ), - //Check: resource.ComposeTestCheckFunc( - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", id.Name()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "fully_qualified_name", id.Name()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "notify_users.#", "0"), - // resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "credit_quota"), - // resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "frequency"), - // resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "start_timestamp"), - // resource.TestCheckNoResourceAttr("snowflake_resource_monitor.test", "end_timestamp"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "trigger.#", "0"), - //), }, - // CHANGE PROPERTIES - //{ - // Config: resourceMonitorConfig2(id.Name(), 75), - // Check: resource.ComposeTestCheckFunc( - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", id.Name()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "fully_qualified_name", id.FullyQualifiedName()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "credit_quota", "150"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "set_for_account", "true"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "notify_triggers.0", "50"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "suspend_trigger", "75"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "suspend_immediate_trigger", "95"), - // ), - //}, - //// CHANGE JUST suspend_trigger; proves https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2316 - //{ - // Config: resourceMonitorConfig2(id.Name(), 60), - // Check: resource.ComposeTestCheckFunc( - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", id.Name()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "fully_qualified_name", id.FullyQualifiedName()), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "credit_quota", "150"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "set_for_account", "true"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "notify_triggers.0", "50"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "suspend_trigger", "60"), - // resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "suspend_immediate_trigger", "95"), - // ), - //}, - //// IMPORT - //{ - // ResourceName: "snowflake_resource_monitor.test", - // ImportState: true, - // ImportStateVerify: true, - //}, }, }) } -func TestAcc_ResourceMonitorChangeStartEndTimestamp(t *testing.T) { - name := acc.TestClient().Ids.Alpha() +func TestAcc_ResourceMonitor_Complete(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + configModel := model.ResourceMonitor("test", id.Name()). + WithNotifyUsersValue(configvariable.SetVariable(configvariable.StringVariable("JAN_CIESLAK"))). + WithCreditQuota(10). + WithFrequency(string(sdk.FrequencyWeekly)). + WithStartTimestamp(time.Now().Add(time.Hour * 24 * 30).Format("2006-01-02 15:01")). + WithEndTimestamp(time.Now().Add(time.Hour * 24 * 60).Format("2006-01-02 15:01")). + WithTriggerValue(configvariable.SetVariable( + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(100), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(110), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(120), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspend)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(150), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspendImmediate)), + }), + )) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -107,91 +117,128 @@ func TestAcc_ResourceMonitorChangeStartEndTimestamp(t *testing.T) { CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), Steps: []resource.TestStep{ { - Config: resourceMonitorConfigInitialTimestamp(name), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "frequency", "WEEKLY"), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "start_timestamp", "2050-01-01 12:00"), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "end_timestamp", "2055-01-01 12:00"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ResourceMonitor/complete"), + ConfigVariables: configModel.ToConfigVariables(), + Check: assert.AssertThat(t, + resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("10"). + HasNotifyUsersLen(1). + HasNotifyUser(0, "JAN_CIESLAK"). + HasFrequencyString(string(sdk.FrequencyWeekly)). + HasStartTimestampString(time.Now().Add(time.Hour*24*30).Format("2006-01-02 15:01")). + HasEndTimestampString(time.Now().Add(time.Hour*24*60).Format("2006-01-02 15:01")). + HasTriggerLen(4). + HasTrigger(0, 100, sdk.TriggerActionNotify). + HasTrigger(1, 110, sdk.TriggerActionNotify). + HasTrigger(2, 120, sdk.TriggerActionSuspend). + HasTrigger(3, 150, sdk.TriggerActionSuspendImmediate), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(10). + HasUsedCredits(0). + HasRemainingCredits(10). + HasLevel(""). + HasFrequency(sdk.FrequencyWeekly). + HasStartTimeNotEmpty(). + HasEndTimeNotEmpty(). + HasSuspendAt(120). + HasSuspendImmediateAt(150). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), ), }, { - Config: resourceMonitorConfigUpdatedTimestamp(name), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "frequency", "WEEKLY"), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "start_timestamp", "2055-01-01 12:00"), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "end_timestamp", "2056-01-01 12:00"), + ResourceName: "snowflake_resource_monitor.test", + ImportState: true, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ResourceMonitor/complete"), + ConfigVariables: configModel.ToConfigVariables(), + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedResourceMonitorResource(t, helpers.EncodeResourceIdentifier(id)). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("10"). + HasNotifyUsersLen(1). + HasNotifyUser(0, "JAN_CIESLAK"). + HasFrequencyString(string(sdk.FrequencyWeekly)). + HasStartTimestampNotEmpty(). + HasEndTimestampNotEmpty(). + HasTriggerLen(4). + HasTrigger(0, 100, sdk.TriggerActionNotify). + HasTrigger(1, 110, sdk.TriggerActionNotify). + HasTrigger(2, 120, sdk.TriggerActionSuspend). + HasTrigger(3, 150, sdk.TriggerActionSuspendImmediate), ), }, - // IMPORT - { - ResourceName: "snowflake_resource_monitor.test", - ImportState: true, - ImportStateVerify: true, - }, }, }) } -func resourceMonitorConfigUpdatedTimestamp(accName string) string { - return fmt.Sprintf(` -resource "snowflake_warehouse" "warehouse" { - name = "test%v" - comment = "foo" - warehouse_size = "XSMALL" -} +func TestAcc_ResourceMonitor_Updates(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() -resource "snowflake_resource_monitor" "test" { - name = "%v" - frequency = "WEEKLY" - start_timestamp = "2055-01-01 12:00" - end_timestamp = "2056-01-01 12:00" + configModelNothingSet := model.ResourceMonitor("test", id.Name()) -} -`, accName, accName) -} + configModelEverythingSet := model.ResourceMonitor("test", id.Name()). + WithNotifyUsersValue(configvariable.SetVariable(configvariable.StringVariable("JAN_CIESLAK"))). + WithCreditQuota(10). + WithFrequency(string(sdk.FrequencyWeekly)). + WithStartTimestamp(time.Now().Add(time.Hour * 24 * 30).Format("2006-01-02 15:01")). + WithEndTimestamp(time.Now().Add(time.Hour * 24 * 60).Format("2006-01-02 15:01")). + WithTriggerValue(configvariable.SetVariable( + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(100), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(110), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(120), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspend)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(150), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspendImmediate)), + }), + )) -// fix 2 added empy notifiy user -// Config for changed timestamp frequency validation test -func resourceMonitorConfigInitialTimestamp(accName string) string { - return fmt.Sprintf(` -resource "snowflake_warehouse" "warehouse" { - name = "test" - comment = "foo" - warehouse_size = "XSMALL" -} - -resource "snowflake_resource_monitor" "test" { - name = "%v" - frequency = "WEEKLY" - start_timestamp = "2050-01-01 12:00" - end_timestamp = "2055-01-01 12:00" + configModelUpdated := model.ResourceMonitor("test", id.Name()). + WithNotifyUsersValue(configvariable.SetVariable(configvariable.StringVariable("JAN_CIESLAK"), configvariable.StringVariable("ARTUR_SAWICKI"))). + WithCreditQuota(20). + WithFrequency(string(sdk.FrequencyMonthly)). + WithStartTimestamp(time.Now().Add(time.Hour * 24 * 40).Format("2006-01-02 15:01")). + WithEndTimestamp(time.Now().Add(time.Hour * 24 * 70).Format("2006-01-02 15:01")). + WithTriggerValue(configvariable.SetVariable( + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(110), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(120), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(130), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspend)), + }), + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(160), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionSuspendImmediate)), + }), + )) -} -`, accName) -} + configModelEverythingUnset := model.ResourceMonitor("test", id.Name()). + WithTriggerValue(configvariable.SetVariable( + configvariable.ObjectVariable(map[string]configvariable.Variable{ + "threshold": configvariable.IntegerVariable(100), + "on_threshold_reached": configvariable.StringVariable(string(sdk.TriggerActionNotify)), + }), + )) -func TestAcc_ResourceMonitorUpdateNotifyUsers(t *testing.T) { - userEnv := testenvs.GetOrSkipTest(t, testenvs.ResourceMonitorNotifyUsers) - users := strings.Split(userEnv, ",") - name := acc.TestClient().Ids.Alpha() - 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)) - } - empty := []string{} - emptyUsersConfig, err := resourceMonitorNotifyUsersConfig(name, empty) - if err != nil { - t.Error(err) - } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, @@ -201,62 +248,146 @@ func TestAcc_ResourceMonitorUpdateNotifyUsers(t *testing.T) { CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), Steps: []resource.TestStep{ { - Config: emptyUsersConfig, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name), - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "set_for_account", "false"), + Config: config.FromModel(t, configModelNothingSet), + Check: assert.AssertThat(t, + resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasNoCreditQuota(). + HasNotifyUsersLen(0). + HasNoFrequency(). + HasNoStartTimestamp(). + HasNoEndTimestamp(). + HasTriggerLen(0), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(0). + HasUsedCredits(0). + HasRemainingCredits(0). + HasLevel(""). + HasFrequency(sdk.FrequencyMonthly). + HasStartTimeNotEmpty(). + HasEndTime(""). + HasSuspendAt(0). + HasSuspendImmediateAt(0). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), ), }, + // Set { - Config: config, - Check: resource.ComposeTestCheckFunc(checks...), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ResourceMonitor/complete"), + ConfigVariables: configModelEverythingSet.ToConfigVariables(), + Check: assert.AssertThat(t, + resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("10"). + HasNotifyUsersLen(1). + HasNotifyUser(0, "JAN_CIESLAK"). + HasFrequencyString(string(sdk.FrequencyWeekly)). + HasStartTimestampString(time.Now().Add(time.Hour*24*30).Format("2006-01-02 15:01")). + HasEndTimestampString(time.Now().Add(time.Hour*24*60).Format("2006-01-02 15:01")). + HasTriggerLen(4). + HasTrigger(0, 100, sdk.TriggerActionNotify). + HasTrigger(1, 110, sdk.TriggerActionNotify). + HasTrigger(2, 120, sdk.TriggerActionSuspend). + HasTrigger(3, 150, sdk.TriggerActionSuspendImmediate), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(10). + HasUsedCredits(0). + HasRemainingCredits(10). + HasLevel(""). + HasFrequency(sdk.FrequencyWeekly). + HasStartTimeNotEmpty(). + HasEndTimeNotEmpty(). + HasSuspendAt(120). + HasSuspendImmediateAt(150). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), + ), }, + // Update { - ResourceName: "snowflake_resource_monitor.test", - ImportState: true, - ImportStateVerify: true, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ResourceMonitor/complete"), + ConfigVariables: configModelUpdated.ToConfigVariables(), + Check: assert.AssertThat(t, + resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("20"). + HasNotifyUsersLen(2). + HasNotifyUser(0, "ARTUR_SAWICKI"). + HasNotifyUser(1, "JAN_CIESLAK"). + HasFrequencyString(string(sdk.FrequencyMonthly)). + HasStartTimestampString(time.Now().Add(time.Hour*24*40).Format("2006-01-02 15:01")). + HasEndTimestampString(time.Now().Add(time.Hour*24*70).Format("2006-01-02 15:01")). + HasTriggerLen(4). + HasTrigger(0, 110, sdk.TriggerActionNotify). + HasTrigger(1, 120, sdk.TriggerActionNotify). + HasTrigger(2, 130, sdk.TriggerActionSuspend). + HasTrigger(3, 160, sdk.TriggerActionSuspendImmediate), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(20). + HasUsedCredits(0). + HasRemainingCredits(20). + HasLevel(""). + HasFrequency(sdk.FrequencyMonthly). + HasStartTimeNotEmpty(). + HasEndTimeNotEmpty(). + HasSuspendAt(130). + HasSuspendImmediateAt(160). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), + ), + }, + // Unset + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ResourceMonitor/only_triggers"), + ConfigVariables: configModelEverythingUnset.ToConfigVariables(), + Check: assert.AssertThat(t, + resourceassert.ResourceMonitorResource(t, "snowflake_resource_monitor.test"). + HasNameString(id.Name()). + HasFullyQualifiedNameString(id.FullyQualifiedName()). + HasCreditQuotaString("0"). + HasNotifyUsersLen(0). + HasFrequencyString(""). + HasStartTimestampString(""). + HasEndTimestampString(""). + HasTriggerLen(1). + HasTrigger(0, 100, sdk.TriggerActionNotify), + resourceshowoutputassert.ResourceMonitorShowOutput(t, "snowflake_resource_monitor.test"). + HasName(id.Name()). + HasCreditQuota(0). + HasUsedCredits(0). + HasRemainingCredits(0). + HasLevel(""). + HasFrequency(sdk.FrequencyMonthly). + HasStartTimeNotEmpty(). + HasEndTime(""). + HasSuspendAt(0). + HasSuspendImmediateAt(0). + HasCreatedOnNotEmpty(). + HasOwnerNotEmpty(). + HasComment(""), + ), }, }, }) } -func resourceMonitorConfig(accName string, warehouse string) string { - return fmt.Sprintf(` -resource "snowflake_resource_monitor" "test" { - name = "%v" - credit_quota = 100 - set_for_account = false - notify_triggers = [40] - suspend_trigger = 80 - suspend_immediate_trigger = 90 - warehouses = ["%s"] -} -`, accName, warehouse) -} - -func resourceMonitorConfig2(accName string, suspendTrigger int) string { - return fmt.Sprintf(` -resource "snowflake_resource_monitor" "test" { - name = "%v" - credit_quota = 150 - set_for_account = true - notify_triggers = [50] - warehouses = [] - suspend_trigger = %d - suspend_immediate_trigger = 95 -} -`, accName, suspendTrigger) -} - // TestAcc_ResourceMonitor_issue2167 proves https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2167 issue. // Second step is purposely error, because tests TestAcc_ResourceMonitorUpdateNotifyUsers and TestAcc_ResourceMonitorNotifyUsers are still skipped. // It can be fixed with them. func TestAcc_ResourceMonitor_issue2167(t *testing.T) { - name := acc.TestClient().Ids.Alpha() - configNoUsers, err := resourceMonitorNotifyUsersConfig(name, []string{}) - require.NoError(t, err) - config, err := resourceMonitorNotifyUsersConfig(name, []string{"non_existing_user"}) - require.NoError(t, err) + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + configNoUsers := model.ResourceMonitor("test", id.Name()).WithNotifyUsersValue(configvariable.SetVariable()) + configWithNonExistingUser := model.ResourceMonitor("test", id.Name()).WithNotifyUsersValue(configvariable.SetVariable(configvariable.StringVariable("non_existing_user"))) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -267,66 +398,201 @@ func TestAcc_ResourceMonitor_issue2167(t *testing.T) { CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), Steps: []resource.TestStep{ { - Config: configNoUsers, + Config: config.FromModel(t, configNoUsers), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name), + resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", id.Name()), ), }, { - Config: config, + Config: config.FromModel(t, configWithNonExistingUser), ExpectError: regexp.MustCompile(`.*090268 \(22023\): User non_existing_user does not exist.*`), }, }, }) } -func TestAcc_ResourceMonitorNotifyUsers(t *testing.T) { - userEnv := testenvs.GetOrSkipTest(t, testenvs.ResourceMonitorNotifyUsers) - users := strings.Split(userEnv, ",") - name := acc.TestClient().Ids.Alpha() - 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)) - } +// proves https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1990 is fixed +func TestAcc_ResourceMonitor_Issue1990_RemovingResourceMonitorOutsideOfTerraform(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + configModel := model.ResourceMonitor("test", id.Name()) + resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.ResourceMonitor), + Steps: []resource.TestStep{ + // Create resource monitor + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.69.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: config.FromModel(t, configModel), + }, + // Same configuration, but we drop resource monitor externally + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.69.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + PreConfig: func() { + acc.TestClient().ResourceMonitor.DropResourceMonitorFunc(t, id)() + }, + Config: config.FromModel(t, configModel), + ExpectError: regexp.MustCompile("object does not exist or not authorized"), + }, + // Same configuration, but it's the last version where it's still not working + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.95.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: config.FromModel(t, configModel), + ExpectError: regexp.MustCompile("object does not exist or not authorized"), + }, + // Same configuration, but it's the latest version of the provider (0.96.0 and above) + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModel), + }, + }, + }) +} + +// TODO: Timestamp issues +// TODO: Reference related issues +func TestAcc_ResourceMonitor_Issue_TimestampInfinitePlan(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + configModel := model.ResourceMonitor("test", id.Name()) + + // Steps + // old version + // - create with and without timestamps + // - different formats + // - same format as in Snowflake + // new version + // - create with and without timestamps + // - different formats + // - same format as in Snowflake + + 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{ + // Create resource monitor { - Config: config, - Check: resource.ComposeTestCheckFunc(checks...), + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.69.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: config.FromModel(t, configModel), + }, + // Same configuration, but we drop resource monitor externally + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.69.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + PreConfig: func() { + acc.TestClient().ResourceMonitor.DropResourceMonitorFunc(t, id)() + }, + Config: config.FromModel(t, configModel), + ExpectError: regexp.MustCompile("object does not exist or not authorized"), }, + // Same configuration, but it's the last version where it's still not working { - ResourceName: "snowflake_resource_monitor.test", - ImportState: true, - ImportStateVerify: true, + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.95.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: config.FromModel(t, configModel), + ExpectError: regexp.MustCompile("object does not exist or not authorized"), + }, + // Same configuration, but it's the latest version of the provider (0.96.0 and above) + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: config.FromModel(t, configModel), }, }, }) } -func resourceMonitorNotifyUsersConfig(accName string, accNotifyUsers []string) (string, error) { - notifyUsers, err := json.Marshal(accNotifyUsers) - if err != nil { - return "", err +// TODO: Issue #1500 (creating and altering resource monitor with only triggers) +// - On create we have required_with validation, so it's not possible +// - On update we can set e.g. credit_quota to the same (or new) value and it will work. + +// proves https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1500 is fixed +func TestAcc_ResourceMonitor_Issue1500_CreatingAndAlteringResourceMonitorWithOnlyTriggers(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + //configModel := model.ResourceMonitor("test", id.Name()) + triggers := []map[string]any{ + { + "threshold": 100, + "on_threshold_reached": string(sdk.TriggerActionNotify), + }, + { + "threshold": 120, + "on_threshold_reached": string(sdk.TriggerActionNotify), + }, } - config := fmt.Sprintf(` + + 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{ + // Create resource monitor with only triggers (old version) + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.55.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: resourceMonitorConfigWithOnlyTriggers(t, id.Name(), triggers), + ExpectError: regexp.MustCompile("LULULULUL"), + }, + }, + }) +} + +func resourceMonitorConfigWithOnlyTriggers(t *testing.T, name string, triggers []map[string]any) string { + t.Helper() + + triggersBuilder := new(strings.Builder) + for _, trigger := range triggers { + triggersBuilder.WriteString(fmt.Sprintf(` +trigger { + threshold = %d + on_threshold_reached = "%s" +} +`, trigger["threshold"], trigger["on_threshold_reached"])) + } + + return fmt.Sprintf(` resource "snowflake_resource_monitor" "test" { - name = "%v" - set_for_account = false - notify_users = %v + name = "%[1]s" + + %[2]s } -`, accName, string(notifyUsers)) - return config, nil +`, name, triggersBuilder.String()) } 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 00000000000..29fd7e5e9b0 --- /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 00000000000..4b8a6a8a88f --- /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 00000000000..77b7f7c8739 --- /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 00000000000..a9b74bf060a --- /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 + })) +} diff --git a/pkg/schemas/resource_monitor_gen.go b/pkg/schemas/resource_monitor_gen.go index b7025748b97..982c309f688 100644 --- a/pkg/schemas/resource_monitor_gen.go +++ b/pkg/schemas/resource_monitor_gen.go @@ -76,9 +76,7 @@ var _ = ShowResourceMonitorSchema func ResourceMonitorToSchema(resourceMonitor *sdk.ResourceMonitor) map[string]any { resourceMonitorSchema := make(map[string]any) resourceMonitorSchema["name"] = resourceMonitor.Name - if resourceMonitor.CreditQuota != nil { - resourceMonitorSchema["credit_quota"] = resourceMonitor.CreditQuota - } + resourceMonitorSchema["credit_quota"] = resourceMonitor.CreditQuota resourceMonitorSchema["used_credits"] = resourceMonitor.UsedCredits resourceMonitorSchema["remaining_credits"] = resourceMonitor.RemainingCredits if resourceMonitor.Level != nil { @@ -86,9 +84,7 @@ func ResourceMonitorToSchema(resourceMonitor *sdk.ResourceMonitor) map[string]an } resourceMonitorSchema["frequency"] = string(resourceMonitor.Frequency) resourceMonitorSchema["start_time"] = resourceMonitor.StartTime - if resourceMonitor.EndTime != nil { - resourceMonitorSchema["end_time"] = resourceMonitor.EndTime - } + resourceMonitorSchema["end_time"] = resourceMonitor.EndTime //resourceMonitorSchema["notify_at"] = resourceMonitor.NotifyAt if resourceMonitor.SuspendAt != nil { resourceMonitorSchema["suspend_at"] = resourceMonitor.SuspendAt diff --git a/pkg/sdk/resource_monitors.go b/pkg/sdk/resource_monitors.go index 8773e82c807..bed847e0740 100644 --- a/pkg/sdk/resource_monitors.go +++ b/pkg/sdk/resource_monitors.go @@ -5,7 +5,7 @@ import ( "database/sql" "errors" "fmt" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "strconv" "strings" "time" @@ -34,13 +34,13 @@ type resourceMonitors struct { type ResourceMonitor struct { Name string - CreditQuota *float64 + CreditQuota float64 UsedCredits float64 RemainingCredits float64 Level *ResourceMonitorLevel Frequency Frequency StartTime string - EndTime *string + EndTime string NotifyAt []int SuspendAt *int SuspendImmediateAt *int @@ -79,7 +79,7 @@ func (row *resourceMonitorRow) convert() (*ResourceMonitor, error) { if err != nil { return nil, err } - resourceMonitor.CreditQuota = &creditQuota + resourceMonitor.CreditQuota = creditQuota } if row.UsedCredits.Valid { @@ -116,19 +116,11 @@ func (row *resourceMonitorRow) convert() (*ResourceMonitor, error) { } if row.StartTime.Valid { - convertedStartTime, err := ParseTimestampWithOffset(row.StartTime.String, "2006-01-02 15:04") - if err != nil { - return nil, err - } - resourceMonitor.StartTime = convertedStartTime + resourceMonitor.StartTime = row.StartTime.String } if row.EndTime.Valid { - convertedEndTime, err := ParseTimestampWithOffset(row.EndTime.String, "2006-01-02 15:04") - if err != nil { - return nil, err - } - resourceMonitor.EndTime = &convertedEndTime + resourceMonitor.EndTime = row.EndTime.String } notifyTriggers, err := extractTriggerInts(row.NotifyAt) @@ -347,6 +339,7 @@ type ResourceMonitorSet struct { type ResourceMonitorUnset struct { CreditQuota *bool `ddl:"keyword" sql:"CREDIT_QUOTA = null"` EndTimestamp *bool `ddl:"keyword" sql:"END_TIMESTAMP = null"` + NotifyUsers *bool `ddl:"keyword" sql:"NOTIFY_USERS = ()"` } // DropResourceMonitorOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-resource-monitor. @@ -413,5 +406,5 @@ func (v *resourceMonitors) ShowByID(ctx context.Context, id AccountObjectIdentif if err != nil { return nil, err } - return collections.FindOne(resourceMonitors, func(r ResourceMonitor) bool { return r.ID().Name() == id.Name() }) + return collections.FindFirst(resourceMonitors, func(r ResourceMonitor) bool { return r.ID().Name() == id.Name() }) } diff --git a/pkg/sdk/testint/resource_monitors_integration_test.go b/pkg/sdk/testint/resource_monitors_integration_test.go index 937bc9f306b..723c78a7f1d 100644 --- a/pkg/sdk/testint/resource_monitors_integration_test.go +++ b/pkg/sdk/testint/resource_monitors_integration_test.go @@ -108,10 +108,10 @@ func TestInt_ResourceMonitorCreate(t *testing.T) { assert.Equal(t, name, resourceMonitor.Name) assert.NotEmpty(t, resourceMonitor.CreatedOn) assert.Equal(t, frequency, resourceMonitor.Frequency) - assert.Equal(t, creditQuota, int(*resourceMonitor.CreditQuota)) + assert.Equal(t, creditQuota, int(resourceMonitor.CreditQuota)) assert.NotEmpty(t, resourceMonitor.StartTime) assert.NotEmpty(t, resourceMonitor.EndTime) - assert.Equal(t, creditQuota, int(*resourceMonitor.CreditQuota)) + assert.Equal(t, creditQuota, int(resourceMonitor.CreditQuota)) var allThresholds []int allThresholds = append(allThresholds, *resourceMonitor.SuspendAt) allThresholds = append(allThresholds, *resourceMonitor.SuspendImmediateAt) @@ -245,7 +245,7 @@ func TestInt_ResourceMonitorAlter(t *testing.T) { require.NoError(t, err) resourceMonitor, err = client.ResourceMonitors.ShowByID(ctx, resourceMonitor.ID()) - assert.Equal(t, creditQuota, int(*resourceMonitor.CreditQuota)) + assert.Equal(t, creditQuota, int(resourceMonitor.CreditQuota)) err = client.ResourceMonitors.Alter(ctx, resourceMonitor.ID(), &sdk.AlterResourceMonitorOptions{ Unset: &sdk.ResourceMonitorUnset{ @@ -265,7 +265,7 @@ func TestInt_ResourceMonitorAlter(t *testing.T) { err := client.ResourceMonitors.Alter(ctx, resourceMonitor.ID(), &sdk.AlterResourceMonitorOptions{ Set: &sdk.ResourceMonitorSet{ NotifyUsers: &sdk.NotifyUsers{ - Users: []sdk.NotifiedUser{{Name: sdk.NewAccountObjectIdentifier("TERRAFORM_SVC_ACCOUNT")}}, + Users: []sdk.NotifiedUser{{Name: sdk.NewAccountObjectIdentifier("JAN_CIESLAK")}}, // TODO: Leave? }, }, }) @@ -274,7 +274,18 @@ func TestInt_ResourceMonitorAlter(t *testing.T) { resourceMonitor, err = client.ResourceMonitors.ShowByID(ctx, resourceMonitor.ID()) require.NoError(t, err) assert.Len(t, resourceMonitor.NotifyUsers, 1) - assert.Equal(t, "TERRAFORM_SVC_ACCOUNT", resourceMonitor.NotifyUsers[0]) + assert.Equal(t, "JAN_CIESLAK", resourceMonitor.NotifyUsers[0]) + + err = client.ResourceMonitors.Alter(ctx, resourceMonitor.ID(), &sdk.AlterResourceMonitorOptions{ + Unset: &sdk.ResourceMonitorUnset{ + NotifyUsers: sdk.Bool(true), + }, + }) + require.NoError(t, err) + + resourceMonitor, err = client.ResourceMonitors.ShowByID(ctx, resourceMonitor.ID()) + require.NoError(t, err) + assert.Len(t, resourceMonitor.NotifyUsers, 0) }) t.Run("when changing scheduling info", func(t *testing.T) { @@ -336,7 +347,7 @@ func TestInt_ResourceMonitorAlter(t *testing.T) { resourceMonitor, err = client.ResourceMonitors.ShowByID(ctx, resourceMonitor.ID()) require.NoError(t, err) assert.NotNil(t, resourceMonitor.CreditQuota) - assert.Equal(t, creditQuota, int(*resourceMonitor.CreditQuota)) + assert.Equal(t, creditQuota, int(resourceMonitor.CreditQuota)) assert.Len(t, resourceMonitor.NotifyUsers, 1) assert.Equal(t, "TERRAFORM_SVC_ACCOUNT", resourceMonitor.NotifyUsers[0]) assert.Len(t, resourceMonitor.NotifyAt, 1)