Skip to content

Commit

Permalink
fix: Handle optional account fields in the state upgrader correctly (#…
Browse files Browse the repository at this point in the history
…3330)

<!-- Feel free to delete comments as you fill this in -->
- Fix handling optional account fields in the state upgrader. This error happened after importing accounts where the optional fields were not set—they are not populated in Import/Read. This resulted in empty values and panics.
- The change has been verified manually. The acceptance test can be added in a follow-up (linked ticket) because of the potential limitations of the plugin SDK.
- Fix failing tests.
- Fix a few issues in docs.

## Test Plan
<!-- detail ways in which this PR has been tested or needs to be tested -->
* [ ] acceptance tests
<!-- add more below if you think they are relevant -->
* [ ] …

## References
<!-- issues documentation links, etc  -->

#3328 #3332
  • Loading branch information
sfc-gh-jmichalak authored and sfc-gh-jcieslak committed Jan 20, 2025
1 parent f7ff70a commit 4eae4c8
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 23 deletions.
9 changes: 7 additions & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ across different versions.
> [!TIP]
> We highly recommend upgrading the versions one by one instead of bulk upgrades.
## v1.0.1 ➞ v1.0.2

### Fixed migration of account resource
Previously, during upgrading the provider from v0.99.0, when account fields `must_change_password` or `is_org_admin` were not set in state, the provider panicked. It has been fixed in this version.

## v1.0.0 ➞ v1.0.1

### Fixes in account parameters
Expand Down Expand Up @@ -155,6 +160,8 @@ provider "snowflake" {
}
```

Do not forget to add this line to all provider configurations using these features, including [provider aliases](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations).

### Removed deprecated objects
All of the deprecated objects are removed from v1 release. This includes:
- Resources
Expand Down Expand Up @@ -669,15 +676,13 @@ If you use TOML configuration file, adjust it from
```toml
[default]
account = "ORGANIZATION-ACCOUNT"
}
```

to
```toml
[default]
organizationname = "ORGANIZATION"
accountname = "ACCOUNT"
}
```

If you use environmental variables, adjust them from
Expand Down
6 changes: 4 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ To export the variables into your provider:

```shell
export SNOWFLAKE_USER="..."
export SNOWFLAKE_PRIVATE_KEY="~/.ssh/snowflake_key"
export SNOWFLAKE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..."
# Alternatively, source from a file.
export SNOWFLAKE_PRIVATE_KEY=$(cat ~/.ssh/snowflake_key.p8)
```

### Keypair Authentication Passphrase
Expand All @@ -172,7 +174,7 @@ To export the variables into your provider:

```shell
export SNOWFLAKE_USER="..."
export SNOWFLAKE_PRIVATE_KEY="~/.ssh/snowflake_key.p8"
export SNOWFLAKE_PRIVATE_KEY=$(cat ~/.ssh/snowflake_key.p8)
export SNOWFLAKE_PRIVATE_KEY_PASSPHRASE="..."
```

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ resource "snowflake_schema" "schema" {

### Read-Only

- `describe_output` (List of Object) Outputs the result of `DESCRIBE SCHEMA` for the given object. In order to handle this output, one must grant sufficient privileges, e.g. [grant_ownership](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_ownership) on all objects in the schema. (see [below for nested schema](#nestedatt--describe_output))
- `describe_output` (List of Object) Outputs the result of `DESCRIBE SCHEMA` for the given object. In order to handle this output, one must grant sufficient privileges, e.g. [grant_ownership](./grant_ownership) on all objects in the schema. (see [below for nested schema](#nestedatt--describe_output))
- `fully_qualified_name` (String) Fully qualified name of the resource. For more information, see [object name resolution](https://docs.snowflake.com/en/sql-reference/name-resolution).
- `id` (String) The ID of this resource.
- `parameters` (List of Object) Outputs the result of `SHOW PARAMETERS IN SCHEMA` for the given object. (see [below for nested schema](#nestedatt--parameters))
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
page_title: "snowflake_tag Resource - terraform-provider-snowflake"
subcategory: ""
description: |-
Resource used to manage tags. For more information, check tag documentation https://docs.snowflake.com/en/sql-reference/sql/create-tag. For asssigning tags to Snowflake objects, see tag_association resource https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/tag_association.
Resource used to manage tags. For more information, check tag documentation https://docs.snowflake.com/en/sql-reference/sql/create-tag. For asssigning tags to Snowflake objects, see tag_association resource ./tag_association.
---

~> **Required warehouse** For this resource, the provider now uses [tag references](https://docs.snowflake.com/en/sql-reference/functions/tag_references) to get information about masking policies attached to tags. This function requires a warehouse in the connection. Please, make sure you have either set a `DEFAULT_WAREHOUSE` for the user, or specified a warehouse in the provider configuration.

# snowflake_tag (Resource)

Resource used to manage tags. For more information, check [tag documentation](https://docs.snowflake.com/en/sql-reference/sql/create-tag). For asssigning tags to Snowflake objects, see [tag_association resource](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/tag_association).
Resource used to manage tags. For more information, check [tag documentation](https://docs.snowflake.com/en/sql-reference/sql/create-tag). For asssigning tags to Snowflake objects, see [tag_association resource](./tag_association).

## Example Usage

Expand Down
4 changes: 3 additions & 1 deletion pkg/resources/account_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ resource "snowflake_account" "test" {
last_name = "%[7]s"
must_change_password = %[8]t
region = "%[9]s"
grace_period_in_days = %[10]d
grace_period_in_days = %[10]d
comment = "%[11]s"
}
`,
Expand All @@ -673,3 +673,5 @@ resource "snowflake_account" "test" {
comment,
)
}

// TODO(SNOW-1875369): add a state upgrader test for an imported account with optional parameters
8 changes: 6 additions & 2 deletions pkg/resources/account_state_upgraders.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ func v0_99_0_AccountStateUpgrader(ctx context.Context, state map[string]any, met
}

client := meta.(*provider.Context).Client
state["must_change_password"] = booleanStringFromBool(state["must_change_password"].(bool))
state["is_org_admin"] = booleanStringFromBool(state["is_org_admin"].(bool))
if v, ok := state["must_change_password"]; ok && v != nil {
state["must_change_password"] = booleanStringFromBool(v.(bool))
}
if v, ok := state["is_org_admin"]; ok && v != nil {
state["is_org_admin"] = booleanStringFromBool(v.(bool))
}
account, err := client.Accounts.ShowByID(ctx, sdk.NewAccountObjectIdentifier(state["name"].(string)))
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ var schemaSchema = map[string]*schema.Schema{
DescribeOutputAttributeName: {
Type: schema.TypeList,
Computed: true,
Description: "Outputs the result of `DESCRIBE SCHEMA` for the given object. In order to handle this output, one must grant sufficient privileges, e.g. [grant_ownership](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/grant_ownership) on all objects in the schema.",
Description: "Outputs the result of `DESCRIBE SCHEMA` for the given object. In order to handle this output, one must grant sufficient privileges, e.g. [grant_ownership](./grant_ownership) on all objects in the schema.",
Elem: &schema.Resource{
Schema: schemas.SchemaDescribeSchema,
},
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func Tag() *schema.Resource {
ReadContext: TrackingReadWrapper(resources.Tag, ReadContextTag),
UpdateContext: TrackingUpdateWrapper(resources.Tag, UpdateContextTag),
DeleteContext: TrackingDeleteWrapper(resources.Tag, DeleteContextTag),
Description: "Resource used to manage tags. For more information, check [tag documentation](https://docs.snowflake.com/en/sql-reference/sql/create-tag). For asssigning tags to Snowflake objects, see [tag_association resource](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/tag_association).",
Description: "Resource used to manage tags. For more information, check [tag documentation](https://docs.snowflake.com/en/sql-reference/sql/create-tag). For asssigning tags to Snowflake objects, see [tag_association resource](./tag_association).",

CustomizeDiff: TrackingCustomDiffWrapper(resources.Tag, customdiff.All(
ComputedIfAnyAttributeChanged(tagSchema, ShowOutputAttributeName, "name", "comment", "allowed_values"),
Expand Down
15 changes: 8 additions & 7 deletions pkg/sdk/tasks_gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ func TestTasks_CreateOrAlter(t *testing.T) {
t.Run("validation: opts.SessionParameters.SessionParameters should be valid", func(t *testing.T) {
opts := defaultOpts()
opts.SessionParameters = &SessionParameters{
JsonIndent: Int(25),
JsonIndent: Int(-1),
}
assertOptsInvalidJoinedErrors(t, opts, errIntBetween("SessionParameters", "JSONIndent", 0, 16))
assertOptsInvalidJoinedErrors(t, opts, errIntValue("SessionParameters", "JsonIndent", IntErrGreaterOrEqual, 0))
})

t.Run("basic", func(t *testing.T) {
Expand Down Expand Up @@ -263,11 +263,12 @@ func TestTasks_Alter(t *testing.T) {

t.Run("validation: opts.Set.SessionParameters.SessionParameters should be valid", func(t *testing.T) {
opts := defaultOpts()
opts.Set = &TaskSet{}
opts.Set.SessionParameters = &SessionParameters{
JsonIndent: Int(25),
opts.Set = &TaskSet{
SessionParameters: &SessionParameters{
JsonIndent: Int(-1),
},
}
assertOptsInvalidJoinedErrors(t, opts, errIntBetween("SessionParameters", "JSONIndent", 0, 16))
assertOptsInvalidJoinedErrors(t, opts, errIntValue("SessionParameters", "JsonIndent", IntErrGreaterOrEqual, 0))
})

t.Run("validation: at least one of the fields [opts.Unset.Warehouse opts.Unset.Schedule opts.Unset.Config opts.Unset.AllowOverlappingExecution opts.Unset.UserTaskTimeoutMs opts.Unset.SuspendTaskAfterNumFailures opts.Unset.ErrorIntegration opts.Unset.Comment opts.Unset.SessionParametersUnset] should be set", func(t *testing.T) {
Expand All @@ -280,7 +281,7 @@ func TestTasks_Alter(t *testing.T) {
opts := defaultOpts()
opts.Unset = &TaskUnset{}
opts.Unset.SessionParametersUnset = &SessionParametersUnset{}
assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("SessionParametersUnset", "AbortDetachedQuery", "Autocommit", "BinaryInputFormat", "BinaryOutputFormat", "ClientMemoryLimit", "ClientMetadataRequestUseConnectionCtx", "ClientPrefetchThreads", "ClientResultChunkSize", "ClientResultColumnCaseInsensitive", "ClientMetadataUseSessionDatabase", "ClientSessionKeepAlive", "ClientSessionKeepAliveHeartbeatFrequency", "ClientTimestampTypeMapping", "DateInputFormat", "DateOutputFormat", "EnableUnloadPhysicalTypeOptimization", "ErrorOnNondeterministicMerge", "ErrorOnNondeterministicUpdate", "GeographyOutputFormat", "GeometryOutputFormat", "JdbcTreatDecimalAsInt", "JdbcTreatTimestampNtzAsUtc", "JdbcUseSessionTimezone", "JSONIndent", "LockTimeout", "LogLevel", "MultiStatementCount", "NoorderSequenceAsDefault", "OdbcTreatDecimalAsInt", "QueryTag", "QuotedIdentifiersIgnoreCase", "RowsPerResultset", "S3StageVpceDnsName", "SearchPath", "SimulatedDataSharingConsumer", "StatementQueuedTimeoutInSeconds", "StatementTimeoutInSeconds", "StrictJSONOutput", "TimestampDayIsAlways24h", "TimestampInputFormat", "TimestampLTZOutputFormat", "TimestampNTZOutputFormat", "TimestampOutputFormat", "TimestampTypeMapping", "TimestampTZOutputFormat", "Timezone", "TimeInputFormat", "TimeOutputFormat", "TraceLevel", "TransactionAbortOnError", "TransactionDefaultIsolationLevel", "TwoDigitCenturyStart", "UnsupportedDDLAction", "UseCachedResult", "WeekOfYearPolicy", "WeekStart"))
assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("SessionParametersUnset", "AbortDetachedQuery", "ActivePythonProfiler", "Autocommit", "BinaryInputFormat", "BinaryOutputFormat", "ClientEnableLogInfoStatementParameters", "ClientMemoryLimit", "ClientMetadataRequestUseConnectionCtx", "ClientPrefetchThreads", "ClientResultChunkSize", "ClientResultColumnCaseInsensitive", "ClientMetadataUseSessionDatabase", "ClientSessionKeepAlive", "ClientSessionKeepAliveHeartbeatFrequency", "ClientTimestampTypeMapping", "CsvTimestampFormat", "DateInputFormat", "DateOutputFormat", "EnableUnloadPhysicalTypeOptimization", "ErrorOnNondeterministicMerge", "ErrorOnNondeterministicUpdate", "GeographyOutputFormat", "GeometryOutputFormat", "HybridTableLockTimeout", "JdbcTreatDecimalAsInt", "JdbcTreatTimestampNtzAsUtc", "JdbcUseSessionTimezone", "JsonIndent", "JsTreatIntegerAsBigInt", "LockTimeout", "LogLevel", "MultiStatementCount", "NoorderSequenceAsDefault", "OdbcTreatDecimalAsInt", "PythonProfilerModules", "PythonProfilerTargetStage", "QueryTag", "QuotedIdentifiersIgnoreCase", "RowsPerResultset", "S3StageVpceDnsName", "SearchPath", "SimulatedDataSharingConsumer", "StatementQueuedTimeoutInSeconds", "StatementTimeoutInSeconds", "StrictJsonOutput", "TimestampDayIsAlways24h", "TimestampInputFormat", "TimestampLTZOutputFormat", "TimestampNTZOutputFormat", "TimestampOutputFormat", "TimestampTypeMapping", "TimestampTZOutputFormat", "Timezone", "TimeInputFormat", "TimeOutputFormat", "TraceLevel", "TransactionAbortOnError", "TransactionDefaultIsolationLevel", "TwoDigitCenturyStart", "UnsupportedDDLAction", "UseCachedResult", "WeekOfYearPolicy", "WeekStart"))
})

t.Run("alter resume", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/sdk/testint/resource_monitors_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ func TestInt_ResourceMonitorAlter(t *testing.T) {
t.Cleanup(resourceMonitorCleanup)

frequency := sdk.FrequencyNever
startTimeStamp := "2025-01-01 12:34"
endTimeStamp := "2026-01-01 12:34"
startTimeStamp := "2050-01-01 12:34"
endTimeStamp := "2051-01-01 12:34"

err := client.ResourceMonitors.Alter(ctx, resourceMonitor.ID(), &sdk.AlterResourceMonitorOptions{
Set: &sdk.ResourceMonitorSet{
Expand Down
6 changes: 4 additions & 2 deletions templates/index.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ To export the variables into your provider:

```shell
export SNOWFLAKE_USER="..."
export SNOWFLAKE_PRIVATE_KEY="~/.ssh/snowflake_key"
export SNOWFLAKE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..."
# Alternatively, source from a file.
export SNOWFLAKE_PRIVATE_KEY=$(cat ~/.ssh/snowflake_key.p8)
```

### Keypair Authentication Passphrase
Expand All @@ -77,7 +79,7 @@ To export the variables into your provider:

```shell
export SNOWFLAKE_USER="..."
export SNOWFLAKE_PRIVATE_KEY="~/.ssh/snowflake_key.p8"
export SNOWFLAKE_PRIVATE_KEY=$(cat ~/.ssh/snowflake_key.p8)
export SNOWFLAKE_PRIVATE_KEY_PASSPHRASE="..."
```

Expand Down

0 comments on commit 4eae4c8

Please sign in to comment.