diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index b02fcedef1..a47e720d4a 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,6 +9,67 @@ across different versions. ## v0.99.0 ➞ v1.0.0 +### Preview features flag +All of the preview features objects are now disabled by default. This includes: +- Resources + - `snowflake_account_password_policy_attachment` + - `snowflake_alert` + - `snowflake_api_integration` + - `snowflake_cortex_search_service` + - `snowflake_dynamic_table` + - `snowflake_external_function` + - `snowflake_external_table` + - `snowflake_external_volume` + - `snowflake_failover_group` + - `snowflake_file_format` + - `snowflake_managed_account` + - `snowflake_materialized_view` + - `snowflake_network_policy_attachment` + - `snowflake_network_rule` + - `snowflake_email_notification_integration` + - `snowflake_notification_integration` + - `snowflake_object_parameter` + - `snowflake_password_policy` + - `snowflake_pipe` + - `snowflake_sequence` + - `snowflake_share` + - `snowflake_stage` + - `snowflake_storage_integration` + - `snowflake_table_column_masking_policy_application` + - `snowflake_table_constraint` + - `snowflake_user_public_keys` + - `snowflake_user_password_policy_attachment` +- Data sources + - `snowflake_current_account` + - `snowflake_alerts` + - `snowflake_cortex_search_services` + - `snowflake_database` + - `snowflake_database_role` + - `snowflake_dynamic_tables` + - `snowflake_external_functions` + - `snowflake_external_tables` + - `snowflake_failover_groups` + - `snowflake_file_formats` + - `snowflake_materialized_views` + - `snowflake_pipes` + - `snowflake_current_role` + - `snowflake_sequences` + - `snowflake_shares` + - `snowflake_parameters` + - `snowflake_stages` + - `snowflake_storage_integrations` + - `snowflake_system_generate_scim_access_token` + - `snowflake_system_get_aws_sns_iam_policy` + - `snowflake_system_get_privatelink_config` + - `snowflake_system_get_snowflake_platform_info` + +If you want to have them enabled, add the feature name to the provider configuration (with `_datasource` or `_resource` suffix), like this: +```terraform +provider "snowflake" { + preview_features_enabled = ["snowflake_current_account_datasource", "snowflake_alert_resource"] +} +``` + ### Removed deprecated objects All of the deprecated objects are removed from v1 release. This includes: diff --git a/Makefile b/Makefile index 66435284af..ae68a5fc7a 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ test: test-client ## run unit and integration tests go test -v -cover -timeout=45m ./... test-acceptance: ## run acceptance tests - TF_ACC=1 SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true TEST_SF_TF_REQUIRE_TEST_OBJECT_SUFFIX=1 go test -run "^TestAcc_" -v -cover -timeout=120m ./... + TF_ACC=1 SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true TEST_SF_TF_REQUIRE_TEST_OBJECT_SUFFIX=1 SF_TF_ACC_TEST_ENABLE_ALL_PREVIEW_FEATURES=true go test -run "^TestAcc_" -v -cover -timeout=120m ./... test-integration: ## run SDK integration tests TEST_SF_TF_REQUIRE_TEST_OBJECT_SUFFIX=1 go test -run "^TestInt_" -v -cover -timeout=45m ./... @@ -80,7 +80,7 @@ test-object-renaming: ## runs tests in object_renaming_acceptance_test.go TEST_SF_TF_ENABLE_OBJECT_RENAMING=1 go test ./pkg/resources/object_renaming_acceptace_test.go -v test-acceptance-%: ## run acceptance tests for the given resource only, e.g. test-acceptance-Warehouse - TF_ACC=1 TF_LOG=DEBUG SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true go test -run ^TestAcc_$*_ -v -timeout=20m ./pkg/resources + TF_ACC=1 TF_LOG=DEBUG SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE=true SF_TF_ACC_TEST_ENABLE_ALL_PREVIEW_FEATURES=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/docs/index.md b/docs/index.md index f6e9f2cfd6..233312cece 100644 --- a/docs/index.md +++ b/docs/index.md @@ -97,6 +97,7 @@ provider "snowflake" { - `passcode_in_password` (Boolean) False by default. Set to true if the MFA passcode is embedded to the configured password. Can also be sourced from the `SNOWFLAKE_PASSCODE_IN_PASSWORD` environment variable. - `password` (String, Sensitive) Password for user + password auth. Cannot be used with `browser_auth` or `private_key_path`. Can also be sourced from the `SNOWFLAKE_PASSWORD` environment variable. - `port` (Number) Specifies a custom port value used by the driver for privatelink connections. Can also be sourced from the `SNOWFLAKE_PORT` environment variable. +- `preview_features_enabled` (Set of String) A list of preview features that are handled by the provider. See [preview features list](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md). Preview features may have breaking changes in future releases, even without raising the major version. This field can not be set with environmental variables. Valid options are: `snowflake_current_account_datasource` | `snowflake_account_password_policy_attachment_resource` | `snowflake_alert_resource` | `snowflake_alerts_datasource` | `snowflake_api_integration_resource` | `snowflake_cortex_search_service_resource` | `snowflake_cortex_search_services_datasource` | `snowflake_database_datasource` | `snowflake_database_role_datasource` | `snowflake_dynamic_table_resource` | `snowflake_dynamic_tables_datasource` | `snowflake_external_function_resource` | `snowflake_external_functions_datasource` | `snowflake_external_table_resource` | `snowflake_external_tables_datasource` | `snowflake_external_volume_resource` | `snowflake_failover_group_resource` | `snowflake_failover_groups_datasource` | `snowflake_file_format_resource` | `snowflake_file_formats_datasource` | `snowflake_managed_account_resource` | `snowflake_materialized_view_resource` | `snowflake_materialized_views_datasource` | `snowflake_network_policy_attachment_resource` | `snowflake_network_rule_resource` | `snowflake_email_notification_integration_resource` | `snowflake_notification_integration_resource` | `snowflake_object_parameter_resource` | `snowflake_password_policy_resource` | `snowflake_pipe_resource` | `snowflake_pipes_datasource` | `snowflake_current_role_datasource` | `snowflake_sequence_resource` | `snowflake_sequences_datasource` | `snowflake_share_resource` | `snowflake_shares_datasource` | `snowflake_parameters_datasource` | `snowflake_stage_resource` | `snowflake_stages_datasource` | `snowflake_storage_integration_resource` | `snowflake_storage_integrations_datasource` | `snowflake_system_generate_scim_access_token_datasource` | `snowflake_system_get_aws_sns_iam_policy_datasource` | `snowflake_system_get_privatelink_config_datasource` | `snowflake_system_get_snowflake_platform_info_datasource` | `snowflake_table_column_masking_policy_application_resource` | `snowflake_table_constraint_resource` | `snowflake_user_public_keys_resource` | `snowflake_user_password_policy_attachment_resource`. - `private_key` (String, Sensitive) Private Key for username+private-key auth. Cannot be used with `password`. Can also be sourced from the `SNOWFLAKE_PRIVATE_KEY` environment variable. - `private_key_passphrase` (String, Sensitive) Supports the encryption ciphers aes-128-cbc, aes-128-gcm, aes-192-cbc, aes-192-gcm, aes-256-cbc, aes-256-gcm, and des-ede3-cbc. Can also be sourced from the `SNOWFLAKE_PRIVATE_KEY_PASSPHRASE` environment variable. - `profile` (String) Sets the profile to read from ~/.snowflake/config file. Can also be sourced from the `SNOWFLAKE_PROFILE` environment variable. diff --git a/pkg/acceptance/testenvs/testing_environment_variables.go b/pkg/acceptance/testenvs/testing_environment_variables.go index 997d8cd8a8..4abb131036 100644 --- a/pkg/acceptance/testenvs/testing_environment_variables.go +++ b/pkg/acceptance/testenvs/testing_environment_variables.go @@ -34,6 +34,7 @@ const ( EnableSweep env = "TEST_SF_TF_ENABLE_SWEEP" EnableManual env = "TEST_SF_TF_ENABLE_MANUAL_TESTS" ConfigureClientOnce env = "SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE" + EnableAllPreviewFeatures env = "SF_TF_ACC_TEST_ENABLE_ALL_PREVIEW_FEATURES" TestObjectsSuffix env = "TEST_SF_TF_TEST_OBJECT_SUFFIX" RequireTestObjectsSuffix env = "TEST_SF_TF_REQUIRE_TEST_OBJECT_SUFFIX" ) diff --git a/pkg/datasources/common.go b/pkg/datasources/common.go index 6f8ab20169..7c23885197 100644 --- a/pkg/datasources/common.go +++ b/pkg/datasources/common.go @@ -1,6 +1,8 @@ package datasources import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/previewfeatures" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -147,3 +149,17 @@ func handleExtendedIn(d *schema.ResourceData, setField **sdk.ExtendedIn) error { } return nil } + +func PreviewFeatureReadWrapper(featureRaw string, readFunc schema.ReadFunc) schema.ReadFunc { //nolint + return func(d *schema.ResourceData, meta interface{}) error { + enabled := meta.(*provider.Context).EnabledFeatures + feature, err := previewfeatures.StringToFeature(featureRaw) + if err != nil { + return err + } + if err := previewfeatures.EnsurePreviewFeatureEnabled(feature, enabled); err != nil { + return err + } + return readFunc(d, meta) + } +} diff --git a/pkg/datasources/current_account.go b/pkg/datasources/current_account.go index d6a0d1d291..b44cf15bb4 100644 --- a/pkg/datasources/current_account.go +++ b/pkg/datasources/current_account.go @@ -6,6 +6,7 @@ import ( "log" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/previewfeatures" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -33,7 +34,7 @@ var currentAccountSchema = map[string]*schema.Schema{ // CurrentAccount the Snowflake current account resource. func CurrentAccount() *schema.Resource { return &schema.Resource{ - Read: ReadCurrentAccount, + Read: PreviewFeatureReadWrapper(string(previewfeatures.CurrentAccountDatasource), ReadCurrentAccount), Schema: currentAccountSchema, } } diff --git a/pkg/datasources/current_account_acceptance_test.go b/pkg/datasources/current_account_acceptance_test.go index df58a2001b..3e10923ef6 100644 --- a/pkg/datasources/current_account_acceptance_test.go +++ b/pkg/datasources/current_account_acceptance_test.go @@ -16,7 +16,6 @@ func TestAcc_CurrentAccount(t *testing.T) { TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, Steps: []resource.TestStep{ { Config: currentAccount(), diff --git a/pkg/internal/provider/provider_context.go b/pkg/internal/provider/provider_context.go index 0fbdef8228..64c469fb2f 100644 --- a/pkg/internal/provider/provider_context.go +++ b/pkg/internal/provider/provider_context.go @@ -3,5 +3,6 @@ package provider import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" type Context struct { - Client *sdk.Client + Client *sdk.Client + EnabledFeatures []string } diff --git a/pkg/internal/tracking/context.go b/pkg/internal/tracking/context.go index 9519bf1bb4..b2b2229b85 100644 --- a/pkg/internal/tracking/context.go +++ b/pkg/internal/tracking/context.go @@ -8,7 +8,7 @@ import ( ) const ( - ProviderVersion string = "v0.99.0" // TODO(SNOW-1814934): Currently hardcoded, make it computed + ProviderVersion string = "v1.0.0-rc.1" // TODO(SNOW-1814934): Currently hardcoded, make it computed MetadataPrefix string = "terraform_provider_usage_tracking" ) diff --git a/pkg/provider/previewfeatures/preview_features.go b/pkg/provider/previewfeatures/preview_features.go new file mode 100644 index 0000000000..c59dd462e1 --- /dev/null +++ b/pkg/provider/previewfeatures/preview_features.go @@ -0,0 +1,137 @@ +package previewfeatures + +import ( + "fmt" + "slices" + "strings" +) + +type feature string + +const ( + CurrentAccountDatasource feature = "snowflake_current_account_datasource" + AccountPasswordPolicyAttachmentResource feature = "snowflake_account_password_policy_attachment_resource" + AlertResource feature = "snowflake_alert_resource" + AlertsDatasource feature = "snowflake_alerts_datasource" + ApiIntegrationResource feature = "snowflake_api_integration_resource" + CortexSearchServiceResource feature = "snowflake_cortex_search_service_resource" + CortexSearchServicesDatasource feature = "snowflake_cortex_search_services_datasource" + DatabaseDatasource feature = "snowflake_database_datasource" + DatabaseRoleDatasource feature = "snowflake_database_role_datasource" + DynamicTableResource feature = "snowflake_dynamic_table_resource" + DynamicTablesDatasource feature = "snowflake_dynamic_tables_datasource" + ExternalFunctionResource feature = "snowflake_external_function_resource" + ExternalFunctionsDatasource feature = "snowflake_external_functions_datasource" + ExternalTableResource feature = "snowflake_external_table_resource" + ExternalTablesDatasource feature = "snowflake_external_tables_datasource" + ExternalVolumeResource feature = "snowflake_external_volume_resource" + FailoverGroupResource feature = "snowflake_failover_group_resource" + FailoverGroupsDatasource feature = "snowflake_failover_groups_datasource" + FileFormatResource feature = "snowflake_file_format_resource" + FileFormatsDatasource feature = "snowflake_file_formats_datasource" + ManagedAccountResource feature = "snowflake_managed_account_resource" + MaterializedViewResource feature = "snowflake_materialized_view_resource" + MaterializedViewsDatasource feature = "snowflake_materialized_views_datasource" + NetworkPolicyAttachmentResource feature = "snowflake_network_policy_attachment_resource" + NetworkRuleResource feature = "snowflake_network_rule_resource" + EmailNotificationIntegrationResource feature = "snowflake_email_notification_integration_resource" + NotificationIntegrationResource feature = "snowflake_notification_integration_resource" + ObjectParameterResource feature = "snowflake_object_parameter_resource" + PasswordPolicyResource feature = "snowflake_password_policy_resource" + PipeResource feature = "snowflake_pipe_resource" + PipesDatasource feature = "snowflake_pipes_datasource" + CurrentRoleDatasource feature = "snowflake_current_role_datasource" + SequenceResource feature = "snowflake_sequence_resource" + SequencesDatasource feature = "snowflake_sequences_datasource" + ShareResource feature = "snowflake_share_resource" + SharesDatasource feature = "snowflake_shares_datasource" + ParametersDatasource feature = "snowflake_parameters_datasource" + StageResource feature = "snowflake_stage_resource" + StageDatasource feature = "snowflake_stages_datasource" + StorageIntegrationResource feature = "snowflake_storage_integration_resource" + StorageIntegrationsDatasource feature = "snowflake_storage_integrations_datasource" + SystemGenerateSCIMAccessTokenDatasource feature = "snowflake_system_generate_scim_access_token_datasource" + SystemGetAWSSNSIAMPolicyDatasource feature = "snowflake_system_get_aws_sns_iam_policy_datasource" + SystemGetPrivateLinkConfigDatasource feature = "snowflake_system_get_privatelink_config_datasource" + SystemGetSnowflakePlatformInfoDatasource feature = "snowflake_system_get_snowflake_platform_info_datasource" + TableColumnMaskingPolicyApplicationResource feature = "snowflake_table_column_masking_policy_application_resource" + TableConstraintResource feature = "snowflake_table_constraint_resource" + UserPublicKeysResource feature = "snowflake_user_public_keys_resource" + UserPasswordPolicyAttachmentResource feature = "snowflake_user_password_policy_attachment_resource" +) + +var allPreviewFeatures = []feature{ + CurrentAccountDatasource, + AccountPasswordPolicyAttachmentResource, + AlertResource, + AlertsDatasource, + ApiIntegrationResource, + CortexSearchServiceResource, + CortexSearchServicesDatasource, + DatabaseDatasource, + DatabaseRoleDatasource, + DynamicTableResource, + DynamicTablesDatasource, + ExternalFunctionResource, + ExternalFunctionsDatasource, + ExternalTableResource, + ExternalTablesDatasource, + ExternalVolumeResource, + FailoverGroupResource, + FailoverGroupsDatasource, + FileFormatResource, + FileFormatsDatasource, + ManagedAccountResource, + MaterializedViewResource, + MaterializedViewsDatasource, + NetworkPolicyAttachmentResource, + NetworkRuleResource, + EmailNotificationIntegrationResource, + NotificationIntegrationResource, + ObjectParameterResource, + PasswordPolicyResource, + PipeResource, + PipesDatasource, + CurrentRoleDatasource, + SequenceResource, + SequencesDatasource, + ShareResource, + SharesDatasource, + ParametersDatasource, + StageResource, + StageDatasource, + StorageIntegrationResource, + StorageIntegrationsDatasource, + SystemGenerateSCIMAccessTokenDatasource, + SystemGetAWSSNSIAMPolicyDatasource, + SystemGetPrivateLinkConfigDatasource, + SystemGetSnowflakePlatformInfoDatasource, + TableColumnMaskingPolicyApplicationResource, + TableConstraintResource, + UserPublicKeysResource, + UserPasswordPolicyAttachmentResource, +} +var AllPreviewFeatures = make([]string, len(allPreviewFeatures)) + +func init() { + for i, v := range allPreviewFeatures { + AllPreviewFeatures[i] = string(v) + } +} + +func EnsurePreviewFeatureEnabled(feat feature, enabledFeatures []string) error { + if !slices.ContainsFunc(enabledFeatures, func(s string) bool { + return s == string(feat) + }) { + return fmt.Errorf("%[1]s is currently a preview feature, and must be enabled by adding %[1]s to `preview_features_enabled` in Terraform configuration.", feat) + } + return nil +} + +func StringToFeature(featRaw string) (feature, error) { + feat := feature(strings.ToLower(featRaw)) + if !slices.Contains(allPreviewFeatures, feat) { + return "", fmt.Errorf("%[1]s is currently a preview feature, and must be enabled by adding %[1]s to `preview_features_enabled` in Terraform configuration.", feat) + } + return feat, nil +} diff --git a/pkg/provider/previewfeatures/preview_features_test.go b/pkg/provider/previewfeatures/preview_features_test.go new file mode 100644 index 0000000000..08a5d05dae --- /dev/null +++ b/pkg/provider/previewfeatures/preview_features_test.go @@ -0,0 +1,90 @@ +package previewfeatures + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_StringToFeature(t *testing.T) { + type test struct { + input string + want feature + } + + valid := []test{ + // Case insensitive. + {input: "SNOWFLAKE_CURRENT_ACCOUNT_DATASOURCE", want: CurrentAccountDatasource}, + + // Supported Values. + {input: "snowflake_current_account_datasource", want: CurrentAccountDatasource}, + {input: "snowflake_account_password_policy_attachment_resource", want: AccountPasswordPolicyAttachmentResource}, + {input: "snowflake_alert_resource", want: AlertResource}, + {input: "snowflake_alerts_datasource", want: AlertsDatasource}, + {input: "snowflake_api_integration_resource", want: ApiIntegrationResource}, + {input: "snowflake_cortex_search_service_resource", want: CortexSearchServiceResource}, + {input: "snowflake_cortex_search_services_datasource", want: CortexSearchServicesDatasource}, + {input: "snowflake_database_datasource", want: DatabaseDatasource}, + {input: "snowflake_database_role_datasource", want: DatabaseRoleDatasource}, + {input: "snowflake_dynamic_table_resource", want: DynamicTableResource}, + {input: "snowflake_dynamic_tables_datasource", want: DynamicTablesDatasource}, + {input: "snowflake_external_function_resource", want: ExternalFunctionResource}, + {input: "snowflake_external_functions_datasource", want: ExternalFunctionsDatasource}, + {input: "snowflake_external_table_resource", want: ExternalTableResource}, + {input: "snowflake_external_tables_datasource", want: ExternalTablesDatasource}, + {input: "snowflake_external_volume_resource", want: ExternalVolumeResource}, + {input: "snowflake_failover_group_resource", want: FailoverGroupResource}, + {input: "snowflake_failover_groups_datasource", want: FailoverGroupsDatasource}, + {input: "snowflake_file_format_resource", want: FileFormatResource}, + {input: "snowflake_file_formats_datasource", want: FileFormatsDatasource}, + {input: "snowflake_managed_account_resource", want: ManagedAccountResource}, + {input: "snowflake_materialized_view_resource", want: MaterializedViewResource}, + {input: "snowflake_materialized_views_datasource", want: MaterializedViewsDatasource}, + {input: "snowflake_network_policy_attachment_resource", want: NetworkPolicyAttachmentResource}, + {input: "snowflake_network_rule_resource", want: NetworkRuleResource}, + {input: "snowflake_email_notification_integration_resource", want: EmailNotificationIntegrationResource}, + {input: "snowflake_notification_integration_resource", want: NotificationIntegrationResource}, + {input: "snowflake_object_parameter_resource", want: ObjectParameterResource}, + {input: "snowflake_password_policy_resource", want: PasswordPolicyResource}, + {input: "snowflake_pipe_resource", want: PipeResource}, + {input: "snowflake_pipes_datasource", want: PipesDatasource}, + {input: "snowflake_current_role_datasource", want: CurrentRoleDatasource}, + {input: "snowflake_sequence_resource", want: SequenceResource}, + {input: "snowflake_sequences_datasource", want: SequencesDatasource}, + {input: "snowflake_share_resource", want: ShareResource}, + {input: "snowflake_shares_datasource", want: SharesDatasource}, + {input: "snowflake_parameters_datasource", want: ParametersDatasource}, + {input: "snowflake_stage_resource", want: StageResource}, + {input: "snowflake_stages_datasource", want: StageDatasource}, + {input: "snowflake_storage_integration_resource", want: StorageIntegrationResource}, + {input: "snowflake_storage_integrations_datasource", want: StorageIntegrationsDatasource}, + {input: "snowflake_system_generate_scim_access_token_datasource", want: SystemGenerateSCIMAccessTokenDatasource}, + {input: "snowflake_system_get_aws_sns_iam_policy_datasource", want: SystemGetAWSSNSIAMPolicyDatasource}, + {input: "snowflake_system_get_privatelink_config_datasource", want: SystemGetPrivateLinkConfigDatasource}, + {input: "snowflake_system_get_snowflake_platform_info_datasource", want: SystemGetSnowflakePlatformInfoDatasource}, + {input: "snowflake_table_column_masking_policy_application_resource", want: TableColumnMaskingPolicyApplicationResource}, + {input: "snowflake_table_constraint_resource", want: TableConstraintResource}, + {input: "snowflake_user_public_keys_resource", want: UserPublicKeysResource}, + {input: "snowflake_user_password_policy_attachment_resource", want: UserPasswordPolicyAttachmentResource}, + } + + invalid := []test{ + {input: ""}, + {input: "foo"}, + } + + for _, tc := range valid { + t.Run(tc.input, func(t *testing.T) { + got, err := StringToFeature(tc.input) + require.NoError(t, err) + require.Equal(t, tc.want, got) + }) + } + + for _, tc := range invalid { + t.Run(tc.input, func(t *testing.T) { + _, err := StringToFeature(tc.input) + require.Error(t, err) + }) + } +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index e787fd546b..c23782ad73 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -15,6 +15,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider/docs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider/validators" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/snowflakeenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/previewfeatures" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -356,6 +357,15 @@ func Provider() *schema.Provider { Optional: true, DefaultFunc: schema.EnvDefaultFunc(snowflakeenvs.Profile, "default"), }, + "preview_features_enabled": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: validators.StringInSlice(previewfeatures.AllPreviewFeatures, true), + }, + Description: fmt.Sprintf("A list of preview features that are handled by the provider. See [preview features list](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md). Preview features may have breaking changes in future releases, even without raising the major version. This field can not be set with environmental variables. Valid options are: %v.", docs.PossibleValuesListed(previewfeatures.AllPreviewFeatures)), + }, }, ResourcesMap: getResources(), DataSourcesMap: getDataSources(), @@ -506,15 +516,15 @@ func getDataSources() map[string]*schema.Resource { } var ( - configuredClient *sdk.Client configureClientError error //nolint:errname + configureProviderCtx *provider.Context ) func ConfigureProvider(ctx context.Context, s *schema.ResourceData) (any, diag.Diagnostics) { // hacky way to speed up our acceptance tests if os.Getenv("TF_ACC") != "" && os.Getenv("SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE") == "true" { - if configuredClient != nil { - return &provider.Context{Client: configuredClient}, nil + if configureProviderCtx != nil { + return configureProviderCtx, nil } if configureClientError != nil { return nil, diag.FromErr(configureClientError) @@ -536,12 +546,22 @@ func ConfigureProvider(ctx context.Context, s *schema.ResourceData) (any, diag.D client, clientErr := sdk.NewClient(config) + providerCtx := &provider.Context{Client: client} + + if v, ok := s.GetOk("preview_features_enabled"); ok { + providerCtx.EnabledFeatures = expandStringList(v.(*schema.Set).List()) + } + + if os.Getenv("TF_ACC") != "" && os.Getenv("SF_TF_ACC_TEST_ENABLE_ALL_PREVIEW_FEATURES") == "true" { + providerCtx.EnabledFeatures = previewfeatures.AllPreviewFeatures + } + // needed for tests verifying different provider setups if os.Getenv(resource.EnvTfAcc) != "" && os.Getenv(string(testenvs.ConfigureClientOnce)) == "true" { - configuredClient = client + configureProviderCtx = providerCtx configureClientError = clientErr } else { - configuredClient = nil + configureProviderCtx = nil configureClientError = nil } @@ -549,7 +569,19 @@ func ConfigureProvider(ctx context.Context, s *schema.ResourceData) (any, diag.D return nil, diag.FromErr(clientErr) } - return &provider.Context{Client: client}, nil + return providerCtx, nil +} + +// TODO: reuse with the function from resources package +func expandStringList(configured []interface{}) []string { + vs := make([]string, 0, len(configured)) + for _, v := range configured { + val, ok := v.(string) + if ok && val != "" { + vs = append(vs, val) + } + } + return vs } func getDriverConfigFromTOML(profile string) (*gosnowflake.Config, error) { diff --git a/pkg/provider/provider_acceptance_test.go b/pkg/provider/provider_acceptance_test.go index 9b8dcca9fa..51c2206ef1 100644 --- a/pkg/provider/provider_acceptance_test.go +++ b/pkg/provider/provider_acceptance_test.go @@ -649,10 +649,73 @@ func TestAcc_Provider_invalidConfigurations(t *testing.T) { // .* is used to match the error message regarding of the home user location ExpectError: regexp.MustCompile(`profile "non-existing" not found in file .*.snowflake/config`), }, + { + Config: providerConfigWithDatasourcePreviewFeatureEnabled(testprofiles.Default, "snowflake_invalid_feature"), + ExpectError: regexp.MustCompile(`expected .* preview_features_enabled.* to be one of((.|\n)*), got snowflake_invalid_feature`), + }, + }, + }) +} + +func TestAcc_Provider_PreviewFeaturesEnabled(t *testing.T) { + t.Setenv(string(testenvs.ConfigureClientOnce), "") + t.Setenv(string(testenvs.EnableAllPreviewFeatures), "") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + Config: providerConfigWithDatasourcePreviewFeatureEnabled(testprofiles.Default, "snowflake_current_account"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_current_account.t", "account"), + ), + }, + }, + }) +} + +func TestAcc_Provider_PreviewFeaturesDisabled(t *testing.T) { + t.Setenv(string(testenvs.ConfigureClientOnce), "") + t.Setenv(string(testenvs.EnableAllPreviewFeatures), "") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + Config: providerConfigWithDatasourcePreviewFeature(testprofiles.Default, "snowflake_current_account"), + ExpectError: regexp.MustCompile("snowflake_current_account_datasource is currently a preview feature, and must be enabled by adding snowflake_current_account_datasource to `preview_features_enabled` in Terraform configuration"), + }, }, }) } +func providerConfigWithDatasourcePreviewFeatureEnabled(profile, feature string) string { + return fmt.Sprintf(` +provider "snowflake" { + profile = "%[1]s" + preview_features_enabled = ["%[2]s_datasource"] +} +data %[2]s t {} +`, profile, feature) +} + +func providerConfigWithDatasourcePreviewFeature(profile, feature string) string { + return fmt.Sprintf(` +provider "snowflake" { + profile = "%[1]s" +} +data %[2]s t {} +`, profile, feature) +} + func providerConfigWithAuthenticator(profile string, authenticator sdk.AuthenticationType) string { return fmt.Sprintf(` provider "snowflake" { diff --git a/v1-preparations/ESSENTIAL_GA_OBJECTS.MD b/v1-preparations/ESSENTIAL_GA_OBJECTS.MD index 28c250976a..414f714ece 100644 --- a/v1-preparations/ESSENTIAL_GA_OBJECTS.MD +++ b/v1-preparations/ESSENTIAL_GA_OBJECTS.MD @@ -31,7 +31,7 @@ newer provider versions. We will address these while working on the given object | STAGE | ❌ | [#2995](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2995), [#2818](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2818), [#2679](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2679), [#2505](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2505), [#1911](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1911), [#1903](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1903), [#1795](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1795), [#1705](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1705), [#1544](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1544), [#1491](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1491), [#1087](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1087), [#265](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/265) | | STREAM | 🚀 | [#2975](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2975), [#2413](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2413), [#2201](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2201), [#1150](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1150) | | STREAMLIT | 🚀 | - | -| TABLE | 👨‍💻 | [#2997](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2997), [#2844](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2844), [#2839](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2839), [#2735](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2735), [#2733](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2733), [#2683](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2683), [#2676](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2676), [#2674](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2674), [#2629](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2629), [#2418](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2418), [#2415](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2415), [#2406](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2406), [#2236](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2236), [#2035](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2035), [#1823](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1823), [#1799](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1799), [#1764](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1764), [#1600](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1600), [#1387](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1387), [#1272](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1272), [#1271](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1271), [#1248](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1248), [#1241](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1241), [#1146](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1146), [#1032](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1032), [#420](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/420) | +| TABLE | 👨‍💻 | [#3221](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/3221), [#2997](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2997), [#2844](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2844), [#2839](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2839), [#2735](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2735), [#2733](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2733), [#2683](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2683), [#2676](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2676), [#2674](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2674), [#2629](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2629), [#2418](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2418), [#2415](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2415), [#2406](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2406), [#2236](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2236), [#2035](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2035), [#1823](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1823), [#1799](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1799), [#1764](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1764), [#1600](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1600), [#1387](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1387), [#1272](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1272), [#1271](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1271), [#1248](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1248), [#1241](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1241), [#1146](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1146), [#1032](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1032), [#420](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/420) | | TAG | 👨‍💻 | [#2943](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2902), [#2598](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2598), [#1910](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1910), [#1909](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1909), [#1862](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1862), [#1806](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1806), [#1657](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1657), [#1496](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1496), [#1443](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1443), [#1394](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1394), [#1372](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1372), [#1074](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1074) | | TASK | 👨‍💻 | [#1419](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1419), [#1250](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1250), [#1194](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1194), [#1088](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1088) | | VIEW | 🚀 | issues in the older versions: [resources](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues?q=label%3Aresource%3Aview+) and [datasources](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues?q=label%3Adata_source%3Aviews+) | diff --git a/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md b/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md index 1f51a82107..73e4e7f4c7 100644 --- a/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md +++ b/v1-preparations/LIST_OF_PREVIEW_FEATURES_FOR_V1.md @@ -27,6 +27,7 @@ * [snowflake_network_rule](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/network_rule) * [snowflake_email_notification_integration](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/email_notification_integration) * [snowflake_notification_integration](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/notification_integration) +* [snowflake_object_parameter](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/object_parameter) * [snowflake_password_policy](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/password_policy) * [snowflake_pipe](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/pipe) * [snowflake_pipes](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/data-sources/pipes) (datasource) @@ -49,3 +50,4 @@ * [snowflake_table_constraint](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/table_constraint) (undecided - may be deleted instead) * [snowflake_user_public_keys](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/user_public_keys) * [snowflake_user_password_policy_attachment](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/user_password_policy_attachment) +* [snowflake_user_authentication_policy_attachment](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/0.97.0/docs/resources/snowflake_user_authentication_policy_attachment)