diff --git a/Makefile b/Makefile index af6e1926ec..1593810d0b 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ test-client: ## runs test that checks sdk.Client without instrumentedsql SF_TF_NO_INSTRUMENTED_SQL=1 SF_TF_GOSNOWFLAKE_LOG_LEVEL=debug go test ./pkg/sdk/internal/client/... -v test-acceptance-%: ## run acceptance tests for the given resource only, e.g. test-acceptance-Warehouse - TF_ACC=1 SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true go test -run ^TestAcc_$*_ -v -timeout=20m ./... + TF_ACC=1 TF_LOG=DEBUG SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true go test -run ^TestAcc_$*_ -v -timeout=20m ./pkg/resources build-local: ## build the binary locally go build -o $(BASE_BINARY_NAME) . diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 69178128f1..47263e19ac 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -7,7 +7,6 @@ import ( "regexp" "strconv" "strings" - "time" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" ) @@ -138,19 +137,3 @@ func DecodeSnowflakeAccountIdentifier(identifier string) (sdk.AccountIdentifier, return sdk.AccountIdentifier{}, fmt.Errorf("unable to classify account identifier: %s, expected format: .", identifier) } } - -func Retry(attempts int, sleepDuration time.Duration, f func() (error, bool)) error { - for i := 0; i < attempts; i++ { - err, done := f() - if err != nil { - return err - } - if done { - return nil - } else { - log.Printf("[INFO] operation not finished yet, retrying in %v seconds\n", sleepDuration.Seconds()) - time.Sleep(sleepDuration) - } - } - return fmt.Errorf("giving up after %v attempts", attempts) -} diff --git a/pkg/internal/util/retry.go b/pkg/internal/util/retry.go new file mode 100644 index 0000000000..9fdf4215b4 --- /dev/null +++ b/pkg/internal/util/retry.go @@ -0,0 +1,23 @@ +package util + +import ( + "fmt" + "log" + "time" +) + +func Retry(attempts int, sleepDuration time.Duration, f func() (error, bool)) error { + for i := 0; i < attempts; i++ { + err, done := f() + if err != nil { + return err + } + if done { + return nil + } else { + log.Printf("[INFO] operation not finished yet, retrying in %v seconds\n", sleepDuration.Seconds()) + time.Sleep(sleepDuration) + } + } + return fmt.Errorf("giving up after %v attempts", attempts) +} diff --git a/pkg/resources/account.go b/pkg/resources/account.go index ac156df9fc..57791e88ac 100644 --- a/pkg/resources/account.go +++ b/pkg/resources/account.go @@ -6,9 +6,9 @@ import ( "strings" "time" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -289,7 +289,7 @@ func CreateAccount(d *schema.ResourceData, meta interface{}) error { } var account *sdk.Account - err = helpers.Retry(5, 3*time.Second, func() (error, bool) { + err = util.Retry(5, 3*time.Second, func() (error, bool) { account, err = client.Accounts.ShowByID(ctx, objectIdentifier) if err != nil { return nil, false @@ -313,7 +313,7 @@ func ReadAccount(d *schema.ResourceData, meta interface{}) error { var acc *sdk.Account var err error - err = helpers.Retry(5, 3*time.Second, func() (error, bool) { + err = util.Retry(5, 3*time.Second, func() (error, bool) { acc, err = client.Accounts.ShowByID(ctx, id) if err != nil { return nil, false diff --git a/pkg/resources/alert.go b/pkg/resources/alert.go index fe5fb55843..6e7f48763f 100644 --- a/pkg/resources/alert.go +++ b/pkg/resources/alert.go @@ -8,9 +8,9 @@ import ( "strings" "time" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" "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/schema" @@ -383,7 +383,7 @@ func waitResumeAlert(ctx context.Context, client *sdk.Client, id sdk.SchemaObjec } return nil, alert.State == sdk.AlertStateStarted } - return helpers.Retry(5, 10*time.Second, resumeAlert) + return util.Retry(5, 10*time.Second, resumeAlert) } func waitSuspendAlert(ctx context.Context, client *sdk.Client, id sdk.SchemaObjectIdentifier) error { @@ -399,5 +399,5 @@ func waitSuspendAlert(ctx context.Context, client *sdk.Client, id sdk.SchemaObje } return nil, alert.State == sdk.AlertStateSuspended } - return helpers.Retry(5, 10*time.Second, suspendAlert) + return util.Retry(5, 10*time.Second, suspendAlert) } diff --git a/pkg/resources/managed_account.go b/pkg/resources/managed_account.go index 196d14beca..bbc1751152 100644 --- a/pkg/resources/managed_account.go +++ b/pkg/resources/managed_account.go @@ -7,6 +7,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -131,7 +132,7 @@ func ReadManagedAccount(d *schema.ResourceData, meta interface{}) error { // TODO [SNOW-1003380]: discuss it as a provider-wide topic during resources redesign. var managedAccount *sdk.ManagedAccount var err error - err = helpers.Retry(5, 3*time.Second, func() (error, bool) { + err = util.Retry(5, 3*time.Second, func() (error, bool) { managedAccount, err = client.ManagedAccounts.ShowByID(ctx, objectIdentifier) if err != nil { return nil, false diff --git a/pkg/resources/task.go b/pkg/resources/task.go index 11cace7614..3b9cb73351 100644 --- a/pkg/resources/task.go +++ b/pkg/resources/task.go @@ -8,9 +8,9 @@ import ( "strconv" "time" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -388,7 +388,7 @@ func waitForTaskStart(ctx context.Context, client *sdk.Client, id sdk.SchemaObje if err != nil { return fmt.Errorf("error starting task %s err = %w", id.FullyQualifiedName(), err) } - return helpers.Retry(5, 5*time.Second, func() (error, bool) { + return util.Retry(5, 5*time.Second, func() (error, bool) { task, err := client.Tasks.ShowByID(ctx, id) if err != nil { return fmt.Errorf("error starting task %s err = %w", id.FullyQualifiedName(), err), false diff --git a/pkg/resources/warehouse.go b/pkg/resources/warehouse.go index b44b843e8f..71f77d3a69 100644 --- a/pkg/resources/warehouse.go +++ b/pkg/resources/warehouse.go @@ -114,23 +114,23 @@ var warehouseSchema = map[string]*schema.Schema{ strings.ToLower(string(sdk.ObjectParameterMaxConcurrencyLevel)): { Type: schema.TypeInt, Optional: true, + Computed: true, ValidateFunc: validation.IntAtLeast(1), Description: "Object parameter that specifies the concurrency level for SQL statements (i.e. queries and DML) executed by a warehouse.", - Default: -1, }, strings.ToLower(string(sdk.ObjectParameterStatementQueuedTimeoutInSeconds)): { Type: schema.TypeInt, Optional: true, + Computed: true, ValidateFunc: validation.IntAtLeast(0), Description: "Object parameter that specifies the time, in seconds, a SQL statement (query, DDL, DML, etc.) can be queued on a warehouse before it is canceled by the system.", - Default: -1, }, strings.ToLower(string(sdk.ObjectParameterStatementTimeoutInSeconds)): { Type: schema.TypeInt, Optional: true, + Computed: true, ValidateFunc: validation.IntBetween(0, 604800), Description: "Specifies the time, in seconds, after which a running SQL statement (query, DDL, DML, etc.) is canceled by the system", - Default: -1, }, showOutputAttributeName: { Type: schema.TypeList, @@ -150,6 +150,29 @@ var warehouseSchema = map[string]*schema.Schema{ }, } +// TODO: merge with DatabaseParametersCustomDiff and extract common +var warehouseParametersCustomDiff = func(ctx context.Context, d *schema.ResourceDiff, meta any) error { + if d.Id() == "" { + return nil + } + + client := meta.(*provider.Context).Client + params, err := client.Parameters.ShowParameters(context.Background(), &sdk.ShowParametersOptions{ + In: &sdk.ParametersIn{ + Warehouse: helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier), + }, + }) + if err != nil { + return err + } + + return customdiff.All( + IntParameterValueComputedIf("max_concurrency_level", params, sdk.ParameterTypeWarehouse, sdk.AccountParameterMaxConcurrencyLevel), + IntParameterValueComputedIf("statement_queued_timeout_in_seconds", params, sdk.ParameterTypeWarehouse, sdk.AccountParameterStatementQueuedTimeoutInSeconds), + IntParameterValueComputedIf("statement_timeout_in_seconds", params, sdk.ParameterTypeWarehouse, sdk.AccountParameterStatementTimeoutInSeconds), + )(ctx, d, meta) +} + // Warehouse returns a pointer to the resource representing a warehouse. func Warehouse() *schema.Resource { return &schema.Resource{ @@ -172,6 +195,7 @@ func Warehouse() *schema.Resource { customdiff.ForceNewIfChange("warehouse_size", func(ctx context.Context, old, new, meta any) bool { return old.(string) != "" && new.(string) == "" }), + warehouseParametersCustomDiff, ), StateUpgraders: []schema.StateUpgrader{ @@ -299,14 +323,14 @@ func CreateWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag if v := d.Get("query_acceleration_max_scale_factor").(int); v != -1 { createOptions.QueryAccelerationMaxScaleFactor = sdk.Int(v) } - if v := d.Get("max_concurrency_level").(int); v != -1 { - createOptions.MaxConcurrencyLevel = sdk.Int(v) + if v := GetPropertyAsPointerWithPossibleZeroValues[int](d, "max_concurrency_level"); v != nil { + createOptions.MaxConcurrencyLevel = v } - if v := d.Get("statement_queued_timeout_in_seconds").(int); v != -1 { - createOptions.StatementQueuedTimeoutInSeconds = sdk.Int(v) + if v := GetPropertyAsPointerWithPossibleZeroValues[int](d, "statement_queued_timeout_in_seconds"); v != nil { + createOptions.StatementQueuedTimeoutInSeconds = v } - if v := d.Get("statement_timeout_in_seconds").(int); v != -1 { - createOptions.StatementTimeoutInSeconds = sdk.Int(v) + if v := GetPropertyAsPointerWithPossibleZeroValues[int](d, "statement_timeout_in_seconds"); v != nil { + createOptions.StatementTimeoutInSeconds = v } err := client.Warehouses.Create(ctx, id, createOptions) @@ -318,6 +342,19 @@ func CreateWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag return GetReadWarehouseFunc(false)(ctx, d, meta) } +// TODO: move +func GetPropertyAsPointerWithPossibleZeroValues[T any](d *schema.ResourceData, property string) *T { + if d.GetRawConfig().AsValueMap()[property].IsNull() { + return nil + } + value := d.Get(property) + typedValue, ok := value.(T) + if !ok { + return nil + } + return &typedValue +} + func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client @@ -357,16 +394,11 @@ func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFun showMapping{"auto_suspend", "auto_suspend", w.AutoSuspend, w.AutoSuspend, nil}, showMapping{"auto_resume", "auto_resume", w.AutoResume, fmt.Sprintf("%t", w.AutoResume), nil}, showMapping{"resource_monitor", "resource_monitor", w.ResourceMonitor.Name(), w.ResourceMonitor.Name(), nil}, - showMapping{"comment", "comment", w.Comment, w.Comment, nil}, showMapping{"enable_query_acceleration", "enable_query_acceleration", w.EnableQueryAcceleration, fmt.Sprintf("%t", w.EnableQueryAcceleration), nil}, showMapping{"query_acceleration_max_scale_factor", "query_acceleration_max_scale_factor", w.QueryAccelerationMaxScaleFactor, w.QueryAccelerationMaxScaleFactor, nil}, ); err != nil { return diag.FromErr(err) } - - if err = markChangedParameters(sdk.WarehouseParameters, warehouseParameters, d, sdk.ParameterTypeWarehouse); err != nil { - return diag.FromErr(err) - } } // These are all identity sets, needed for the case where: @@ -416,11 +448,6 @@ func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFun return diag.FromErr(err) } } - if v := d.GetRawConfig().AsValueMap()["comment"]; !v.IsNull() { - if err = d.Set("comment", v.AsString()); err != nil { - return diag.FromErr(err) - } - } if v := d.GetRawConfig().AsValueMap()["enable_query_acceleration"]; !v.IsNull() { if err = d.Set("enable_query_acceleration", v.AsString()); err != nil { return diag.FromErr(err) @@ -434,6 +461,17 @@ func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFun } } + if err = d.Set("name", w.Name); err != nil { + return diag.FromErr(err) + } + if err = d.Set("comment", w.Comment); err != nil { + return diag.FromErr(err) + } + + if diags := handleWarehouseParameterRead(d, warehouseParameters); diags != nil { + return diags + } + if err = d.Set(showOutputAttributeName, []map[string]any{schemas.WarehouseToSchema(w)}); err != nil { return diag.FromErr(err) } @@ -446,6 +484,26 @@ func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFun } } +func handleWarehouseParameterRead(d *schema.ResourceData, warehouseParameters []*sdk.Parameter) diag.Diagnostics { + for _, parameter := range warehouseParameters { + switch parameter.Key { + case + string(sdk.ObjectParameterMaxConcurrencyLevel), + string(sdk.ObjectParameterStatementQueuedTimeoutInSeconds), + string(sdk.ObjectParameterStatementTimeoutInSeconds): + value, err := strconv.Atoi(parameter.Value) + if err != nil { + return diag.FromErr(err) + } + if err := d.Set(strings.ToLower(parameter.Key), value); err != nil { + return diag.FromErr(err) + } + } + } + + return nil +} + // UpdateWarehouse implements schema.UpdateFunc. func UpdateWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client @@ -587,12 +645,9 @@ func UpdateWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag unset.StatementQueuedTimeoutInSeconds = sdk.Bool(true) } } - if d.HasChange("statement_timeout_in_seconds") { - if v := d.Get("statement_timeout_in_seconds").(int); v != -1 { - set.StatementTimeoutInSeconds = sdk.Int(v) - } else { - unset.StatementTimeoutInSeconds = sdk.Bool(true) - } + + if updateParamDiags := handleWarehouseParametersChanges(d, &set, &unset); len(updateParamDiags) > 0 { + return updateParamDiags } // Apply SET and UNSET changes @@ -616,6 +671,14 @@ func UpdateWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag return GetReadWarehouseFunc(false)(ctx, d, meta) } +func handleWarehouseParametersChanges(d *schema.ResourceData, set *sdk.WarehouseSet, unset *sdk.WarehouseUnset) diag.Diagnostics { + return JoinDiags( + handleValuePropertyChange[int](d, "max_concurrency_level", &set.MaxConcurrencyLevel, &unset.MaxConcurrencyLevel), + handleValuePropertyChange[int](d, "statement_queued_timeout_in_seconds", &set.StatementQueuedTimeoutInSeconds, &unset.StatementQueuedTimeoutInSeconds), + handleValuePropertyChange[int](d, "statement_timeout_in_seconds", &set.StatementTimeoutInSeconds, &unset.StatementTimeoutInSeconds), + ) +} + // DeleteWarehouse implements schema.DeleteFunc. func DeleteWarehouse(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client diff --git a/pkg/resources/warehouse_acceptance_test.go b/pkg/resources/warehouse_acceptance_test.go index 3d9b998d6a..0c9c217fc6 100644 --- a/pkg/resources/warehouse_acceptance_test.go +++ b/pkg/resources/warehouse_acceptance_test.go @@ -60,9 +60,9 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { resource.TestCheckResourceAttr("snowflake_warehouse.w", "enable_query_acceleration", "unknown"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "query_acceleration_max_scale_factor", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "8"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "0"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "show_output.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "show_output.0.type", string(sdk.WarehouseTypeStandard)), @@ -101,10 +101,17 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "enable_query_acceleration", "false"), importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "query_acceleration_max_scale_factor", "8"), - // parameters are not set on the object level, so they won't be imported - importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "max_concurrency_level", "-1"), - importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "statement_queued_timeout_in_seconds", "-1"), - importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "statement_timeout_in_seconds", "-1"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "max_concurrency_level", "8"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "statement_queued_timeout_in_seconds", "0"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "statement_timeout_in_seconds", "172800"), + + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.max_concurrency_level.0.value", "8"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.max_concurrency_level.0.level", ""), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.statement_queued_timeout_in_seconds.0.value", "0"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.statement_queued_timeout_in_seconds.0.level", ""), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.statement_timeout_in_seconds.0.value", "172800"), + importchecks.TestCheckResourceAttrInstanceState(warehouseId.Name(), "parameters.0.statement_timeout_in_seconds.0.level", ""), ), }, // RENAME @@ -142,11 +149,17 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { resource.TestCheckResourceAttr("snowflake_warehouse.w", "comment", comment), resource.TestCheckResourceAttr("snowflake_warehouse.w", "enable_query_acceleration", "false"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "query_acceleration_max_scale_factor", "8"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "8"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "0"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), - // parameters will still remain unset - resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.value", "8"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.level", ""), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.value", "0"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.level", ""), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "172800"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.level", ""), ), }, // add parameters - update expected (different level even with same values) @@ -155,9 +168,10 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "warehouse_type", "warehouse_size", "max_cluster_count", "min_cluster_count", "scaling_policy", "auto_suspend", "auto_resume", "enable_query_acceleration", "query_acceleration_max_scale_factor", "max_concurrency_level", "statement_queued_timeout_in_seconds", "statement_timeout_in_seconds", "show_output"), - planchecks.ExpectChange("snowflake_warehouse.w", "max_concurrency_level", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("8")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("0")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("172800")), + // this is this only situation in which there will be a strange output in the plan + planchecks.ExpectComputed("snowflake_warehouse.w", "max_concurrency_level", true), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", true), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), }, }, Config: warehouseFullDefaultConfig(name2, comment), @@ -176,10 +190,19 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { resource.TestCheckResourceAttr("snowflake_warehouse.w", "enable_query_acceleration", "false"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "query_acceleration_max_scale_factor", "8"), - // parameters were changed + // parameters have the same values... resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "8"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "0"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), + + // ... but are set on different level + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.value", "8"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.level", string(sdk.ParameterTypeWarehouse)), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.value", "0"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.level", string(sdk.ParameterTypeWarehouse)), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "172800"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.level", string(sdk.ParameterTypeWarehouse)), ), }, // CHANGE PROPERTIES (normal and parameters) @@ -221,6 +244,14 @@ func TestAcc_Warehouse_BasicFlows(t *testing.T) { resource.TestCheckResourceAttr("snowflake_warehouse.w", "max_concurrency_level", "4"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "5"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "86400"), + + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.value", "4"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.max_concurrency_level.0.level", string(sdk.ParameterTypeWarehouse)), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.value", "5"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_queued_timeout_in_seconds.0.level", string(sdk.ParameterTypeWarehouse)), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "86400"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.level", string(sdk.ParameterTypeWarehouse)), ), }, // CHANGE max_concurrency_level EXTERNALLY (proves https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2318) @@ -931,16 +962,16 @@ func TestAcc_Warehouse_ZeroValues(t *testing.T) { planchecks.PrintPlanDetails("snowflake_warehouse.w", "auto_suspend", "query_acceleration_max_scale_factor", "statement_queued_timeout_in_seconds", "statement_timeout_in_seconds", "show_output"), planchecks.ExpectChange("snowflake_warehouse.w", "auto_suspend", tfjson.ActionUpdate, sdk.String("0"), sdk.String("-1")), planchecks.ExpectChange("snowflake_warehouse.w", "query_acceleration_max_scale_factor", tfjson.ActionUpdate, sdk.String("0"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("0"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("0"), sdk.String("-1")), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", true), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), planchecks.ExpectComputed("snowflake_warehouse.w", "show_output", true), }, }, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_warehouse.w", "auto_suspend", "-1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "query_acceleration_max_scale_factor", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "-1"), - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", "0"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "show_output.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "show_output.0.auto_suspend", "600"), @@ -961,8 +992,8 @@ func TestAcc_Warehouse_ZeroValues(t *testing.T) { planchecks.PrintPlanDetails("snowflake_warehouse.w", "auto_suspend", "query_acceleration_max_scale_factor", "statement_queued_timeout_in_seconds", "statement_timeout_in_seconds", "show_output"), planchecks.ExpectChange("snowflake_warehouse.w", "auto_suspend", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("0")), planchecks.ExpectChange("snowflake_warehouse.w", "query_acceleration_max_scale_factor", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("0")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("0")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("0")), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", true), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("172800"), sdk.String("0")), planchecks.ExpectComputed("snowflake_warehouse.w", "show_output", true), }, }, @@ -1137,8 +1168,8 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "statement_timeout_in_seconds", "parameters"), - planchecks.ExpectDrift("snowflake_warehouse.w", "statement_timeout_in_seconds", sdk.String("43200"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("43200")), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("43200"), nil), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), planchecks.ExpectComputed("snowflake_warehouse.w", "parameters", true), }, }, @@ -1162,12 +1193,12 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "statement_timeout_in_seconds", "parameters"), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("43200"), sdk.String("-1")), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("43200"), nil), planchecks.ExpectComputed("snowflake_warehouse.w", "parameters", true), }, }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "172800"), @@ -1180,19 +1211,20 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ImportState: true, ImportStateCheck: importchecks.ComposeImportStateCheck( importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), - importchecks.TestCheckResourceAttrInstanceState(id.Name(), "statement_timeout_in_seconds", "-1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "statement_timeout_in_seconds", "172800"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.#", "1"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.0.statement_timeout_in_seconds.0.value", "172800"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.0.statement_timeout_in_seconds.0.level", ""), ), }, - // change the param value in config to snowflake default + // change the param value in config to snowflake default (expecting action because of the different level) { Config: warehouseWithParameterConfig(id.Name(), 172800), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "statement_timeout_in_seconds", "parameters"), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("-1"), sdk.String("172800")), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("172800"), nil), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), planchecks.ExpectComputed("snowflake_warehouse.w", "parameters", true), }, }, @@ -1214,12 +1246,13 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "statement_timeout_in_seconds", "parameters"), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("172800"), sdk.String("-1")), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("172800"), nil), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), planchecks.ExpectComputed("snowflake_warehouse.w", "parameters", true), }, }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "172800"), @@ -1243,7 +1276,7 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { }, }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "86400"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "86400"), @@ -1256,7 +1289,7 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ImportState: true, ImportStateCheck: importchecks.ComposeImportStateCheck( importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), - importchecks.TestCheckResourceAttrInstanceState(id.Name(), "statement_timeout_in_seconds", "-1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "statement_timeout_in_seconds", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.#", "1"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.0.statement_timeout_in_seconds.0.value", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "parameters.0.statement_timeout_in_seconds.0.level", string(sdk.ParameterTypeAccount)), @@ -1271,12 +1304,13 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ planchecks.PrintPlanDetails("snowflake_warehouse.w", "statement_timeout_in_seconds", "parameters"), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("86400"), sdk.String("-1")), + planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("86400"), nil), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), planchecks.ExpectComputed("snowflake_warehouse.w", "parameters", true), }, }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "86400"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "86400"), @@ -1297,7 +1331,7 @@ func TestAcc_Warehouse_Parameter(t *testing.T) { }, }, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "-1"), + resource.TestCheckResourceAttr("snowflake_warehouse.w", "statement_timeout_in_seconds", "172800"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.#", "1"), resource.TestCheckResourceAttr("snowflake_warehouse.w", "parameters.0.statement_timeout_in_seconds.0.value", "172800"), @@ -1692,9 +1726,9 @@ func TestAcc_Warehouse_migrateFromVersion092_defaultsRemoved(t *testing.T) { planchecks.ExpectChange("snowflake_warehouse.w", "enable_query_acceleration", tfjson.ActionUpdate, sdk.String("false"), sdk.String("unknown")), planchecks.ExpectChange("snowflake_warehouse.w", "query_acceleration_max_scale_factor", tfjson.ActionUpdate, sdk.String("8"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "max_concurrency_level", tfjson.ActionUpdate, sdk.String("8"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("0"), sdk.String("-1")), - planchecks.ExpectChange("snowflake_warehouse.w", "statement_timeout_in_seconds", tfjson.ActionUpdate, sdk.String("172800"), sdk.String("-1")), + planchecks.ExpectComputed("snowflake_warehouse.w", "max_concurrency_level", true), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_queued_timeout_in_seconds", true), + planchecks.ExpectComputed("snowflake_warehouse.w", "statement_timeout_in_seconds", true), }, }, Check: resource.ComposeTestCheckFunc( diff --git a/pkg/sdk/warehouses.go b/pkg/sdk/warehouses.go index 86ae91da7f..0d0c2c0e83 100644 --- a/pkg/sdk/warehouses.go +++ b/pkg/sdk/warehouses.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/util" ) var ( @@ -324,6 +326,18 @@ func (c *warehouses) Alter(ctx context.Context, id AccountObjectIdentifier, opts log.Printf("[DEBUG] error occurred during warehouse resumption, err=%v", err) } }() + + // needed to make sure that warehouse is suspended + err = util.Retry(3, 1*time.Second, func() (error, bool) { + warehouse, err = c.ShowByID(ctx, id) + if err != nil || warehouse.State != WarehouseStateSuspended { + return nil, false + } + return nil, true + }) + if err != nil { + return err + } } }