diff --git a/docs/resources/external_volume.md b/docs/resources/external_volume.md
new file mode 100644
index 0000000000..7e068ba7b5
--- /dev/null
+++ b/docs/resources/external_volume.md
@@ -0,0 +1,76 @@
+---
+page_title: "snowflake_external_volume Resource - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+ Resource used to manage external volume objects. For more information, check external volume documentation https://docs.snowflake.com/en/sql-reference/commands-data-loading#external-volume.
+---
+
+!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release.
+
+# snowflake_external_volume (Resource)
+
+Resource used to manage external volume objects. For more information, check [external volume documentation](https://docs.snowflake.com/en/sql-reference/commands-data-loading#external-volume).
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) Identifier for the external volume; must be unique for your account. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"`
+- `storage_location` (Block List, Min: 1) List of named cloud storage locations in different regions and, optionally, cloud platforms. Minimum 1 required. The order of the list is important as it impacts the active storage location, and updates will be triggered if it changes. Note that not all parameter combinations are valid as they depend on the given storage_provider. Consult [the docs](https://docs.snowflake.com/en/sql-reference/sql/create-external-volume#cloud-provider-parameters-cloudproviderparams) for more details on this. (see [below for nested schema](#nestedblock--storage_location))
+
+### Optional
+
+- `allow_writes` (String) Specifies whether write operations are allowed for the external volume; must be set to TRUE for Iceberg tables that use Snowflake as the catalog. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value.
+- `comment` (String) Specifies a comment for the external volume.
+
+### Read-Only
+
+- `describe_output` (List of Object) Outputs the result of `DESCRIBE EXTERNAL VOLUME` for the given external volume. (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.
+- `show_output` (List of Object) Outputs the result of `SHOW EXTERNAL VOLUMES` for the given external volume. (see [below for nested schema](#nestedatt--show_output))
+
+
+### Nested Schema for `storage_location`
+
+Required:
+
+- `storage_base_url` (String) Specifies the base URL for your cloud storage location.
+- `storage_location_name` (String) Name of the storage location. Must be unique for the external volume. Do not use the name `terraform_provider_sentinel_storage_location` - this is reserved for the provider for performing update operations. Due to technical limitations (read more [here](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/identifiers_rework_design_decisions.md#known-limitations-and-identifier-recommendations)), avoid using the following characters: `|`, `.`, `(`, `)`, `"`
+- `storage_provider` (String) Specifies the cloud storage provider that stores your data files. Valid values are (case-insensitive): `GCS` | `AZURE` | `S3` | `S3GOV`.
+
+Optional:
+
+- `azure_tenant_id` (String) Specifies the ID for your Office 365 tenant that the allowed and blocked storage accounts belong to.
+- `encryption_kms_key_id` (String) Specifies the ID for the KMS-managed key used to encrypt files.
+- `encryption_type` (String) Specifies the encryption type used.
+- `storage_aws_role_arn` (String) Specifies the case-sensitive Amazon Resource Name (ARN) of the AWS identity and access management (IAM) role that grants privileges on the S3 bucket containing your data files.
+
+Read-Only:
+
+- `storage_aws_external_id` (String) External ID that Snowflake uses to establish a trust relationship with AWS.
+
+
+
+### Nested Schema for `describe_output`
+
+Read-Only:
+
+- `default` (String)
+- `name` (String)
+- `parent` (String)
+- `type` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `show_output`
+
+Read-Only:
+
+- `allow_writes` (Boolean)
+- `comment` (String)
+- `name` (String)
diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go
index 395452fddc..3e03a38629 100644
--- a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go
+++ b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go
@@ -62,6 +62,11 @@ var allStructs = []SdkObjectDef{
ObjectType: sdk.ObjectTypeTask,
ObjectStruct: sdk.Task{},
},
+ {
+ IdType: "sdk.ExternalVolumeObjectIdentifier",
+ ObjectType: sdk.ObjectTypeExternalVolume,
+ ObjectStruct: sdk.ExternalVolume{},
+ },
{
IdType: "sdk.SchemaObjectIdentifier",
ObjectType: sdk.ObjectTypeSecret,
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_ext.go
new file mode 100644
index 0000000000..a0150a6cac
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_ext.go
@@ -0,0 +1,33 @@
+package resourceassert
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+)
+
+func (e *ExternalVolumeResourceAssert) HasStorageLocationLength(len int) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("storage_location.#", strconv.FormatInt(int64(len), 10)))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasStorageLocationAtIndex(
+ index int,
+ expectedName string,
+ expectedStorageProvider string,
+ expectedStorageBaseUrl string,
+ expectedStorageAwsRoleArn string,
+ expectedEncryptionType string,
+ expectedEncryptionKmsKeyId string,
+ expectedAzureTenantId string,
+) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.storage_location_name", strconv.Itoa(index)), expectedName))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.storage_provider", strconv.Itoa(index)), expectedStorageProvider))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.storage_base_url", strconv.Itoa(index)), expectedStorageBaseUrl))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.storage_aws_role_arn", strconv.Itoa(index)), expectedStorageAwsRoleArn))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.encryption_type", strconv.Itoa(index)), expectedEncryptionType))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.encryption_kms_key_id", strconv.Itoa(index)), expectedEncryptionKmsKeyId))
+ e.AddAssertion(assert.ValueSet(fmt.Sprintf("storage_location.%s.azure_tenant_id", strconv.Itoa(index)), expectedAzureTenantId))
+ return e
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_gen.go
new file mode 100644
index 0000000000..2a7a71d6ba
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/external_volume_resource_gen.go
@@ -0,0 +1,87 @@
+// Code generated by assertions generator; DO NOT EDIT.
+
+package resourceassert
+
+import (
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+)
+
+type ExternalVolumeResourceAssert struct {
+ *assert.ResourceAssert
+}
+
+func ExternalVolumeResource(t *testing.T, name string) *ExternalVolumeResourceAssert {
+ t.Helper()
+
+ return &ExternalVolumeResourceAssert{
+ ResourceAssert: assert.NewResourceAssert(name, "resource"),
+ }
+}
+
+func ImportedExternalVolumeResource(t *testing.T, id string) *ExternalVolumeResourceAssert {
+ t.Helper()
+
+ return &ExternalVolumeResourceAssert{
+ ResourceAssert: assert.NewImportedResourceAssert(id, "imported resource"),
+ }
+}
+
+///////////////////////////////////
+// Attribute value string checks //
+///////////////////////////////////
+
+func (e *ExternalVolumeResourceAssert) HasAllowWritesString(expected string) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("allow_writes", expected))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasCommentString(expected string) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("comment", expected))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasFullyQualifiedNameString(expected string) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("fully_qualified_name", expected))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasNameString(expected string) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("name", expected))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasStorageLocationString(expected string) *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueSet("storage_location", expected))
+ return e
+}
+
+////////////////////////////
+// Attribute empty checks //
+////////////////////////////
+
+func (e *ExternalVolumeResourceAssert) HasNoAllowWrites() *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueNotSet("allow_writes"))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasNoComment() *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueNotSet("comment"))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasNoFullyQualifiedName() *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueNotSet("fully_qualified_name"))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasNoName() *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueNotSet("name"))
+ return e
+}
+
+func (e *ExternalVolumeResourceAssert) HasNoStorageLocation() *ExternalVolumeResourceAssert {
+ e.AddAssertion(assert.ValueNotSet("storage_location"))
+ return e
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/external_volume_show_output_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/external_volume_show_output_gen.go
new file mode 100644
index 0000000000..fc46379035
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/external_volume_show_output_gen.go
@@ -0,0 +1,56 @@
+// Code generated by assertions generator; DO NOT EDIT.
+
+package resourceshowoutputassert
+
+import (
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+)
+
+// to ensure sdk package is used
+var _ = sdk.Object{}
+
+type ExternalVolumeShowOutputAssert struct {
+ *assert.ResourceAssert
+}
+
+func ExternalVolumeShowOutput(t *testing.T, name string) *ExternalVolumeShowOutputAssert {
+ t.Helper()
+
+ e := ExternalVolumeShowOutputAssert{
+ ResourceAssert: assert.NewResourceAssert(name, "show_output"),
+ }
+ e.AddAssertion(assert.ValueSet("show_output.#", "1"))
+ return &e
+}
+
+func ImportedExternalVolumeShowOutput(t *testing.T, id string) *ExternalVolumeShowOutputAssert {
+ t.Helper()
+
+ e := ExternalVolumeShowOutputAssert{
+ ResourceAssert: assert.NewImportedResourceAssert(id, "show_output"),
+ }
+ e.AddAssertion(assert.ValueSet("show_output.#", "1"))
+ return &e
+}
+
+////////////////////////////
+// Attribute value checks //
+////////////////////////////
+
+func (e *ExternalVolumeShowOutputAssert) HasName(expected string) *ExternalVolumeShowOutputAssert {
+ e.AddAssertion(assert.ResourceShowOutputValueSet("name", expected))
+ return e
+}
+
+func (e *ExternalVolumeShowOutputAssert) HasAllowWrites(expected string) *ExternalVolumeShowOutputAssert {
+ e.AddAssertion(assert.ResourceShowOutputValueSet("allow_writes", expected))
+ return e
+}
+
+func (e *ExternalVolumeShowOutputAssert) HasComment(expected string) *ExternalVolumeShowOutputAssert {
+ e.AddAssertion(assert.ResourceShowOutputValueSet("comment", expected))
+ return e
+}
diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go
index 0c87742296..cf1b0612c5 100644
--- a/pkg/acceptance/check_destroy.go
+++ b/pkg/acceptance/check_destroy.go
@@ -126,6 +126,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{
resources.ExternalTable: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error {
return runShowById(ctx, id, client.ExternalTables.ShowByID)
},
+ resources.ExternalVolume: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error {
+ return runShowById(ctx, id, client.ExternalVolumes.ShowByID)
+ },
resources.FailoverGroup: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error {
return runShowById(ctx, id, client.FailoverGroups.ShowByID)
},
diff --git a/pkg/acceptance/helpers/external_volume_client.go b/pkg/acceptance/helpers/external_volume_client.go
index 415f7bea0c..9bb00ceb4d 100644
--- a/pkg/acceptance/helpers/external_volume_client.go
+++ b/pkg/acceptance/helpers/external_volume_client.go
@@ -2,7 +2,6 @@ package helpers
import (
"context"
- "fmt"
"testing"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
@@ -21,39 +20,55 @@ func NewExternalVolumeClient(context *TestClientContext, idsGenerator *IdsGenera
}
}
-func (c *ExternalVolumeClient) exec(sql string) error {
- ctx := context.Background()
- _, err := c.context.client.ExecForTests(ctx, sql)
- return err
+func (c *ExternalVolumeClient) client() sdk.ExternalVolumes {
+ return c.context.client.ExternalVolumes
}
-// TODO(SNOW-999142): Use SDK implementation for External Volume once it's available
+// TODO(SNOW-999142): Switch to returning *sdk.ExternalVolume. Need to update existing acceptance tests for this.
func (c *ExternalVolumeClient) Create(t *testing.T) (sdk.AccountObjectIdentifier, func()) {
t.Helper()
+ ctx := context.Background()
+
id := c.ids.RandomAccountObjectIdentifier()
- err := c.exec(fmt.Sprintf(`
-create external volume %s
- storage_locations =
- (
- (
- name = 'my-s3-us-west-2'
- storage_provider = 's3'
- storage_base_url = 's3://my_example_bucket/'
- storage_aws_role_arn = 'arn:aws:iam::123456789012:role/myrole'
- encryption=(type='aws_sse_kms' kms_key_id='1234abcd-12ab-34cd-56ef-1234567890ab')
- )
- );
-`, id.FullyQualifiedName()))
+ kmsKeyId := "1234abcd-12ab-34cd-56ef-1234567890ab"
+ storageLocations := []sdk.ExternalVolumeStorageLocation{
+ {
+ S3StorageLocationParams: &sdk.S3StorageLocationParams{
+ Name: "my-s3-us-west-2",
+ StorageProvider: "S3",
+ StorageAwsRoleArn: "arn:aws:iam::123456789012:role/myrole",
+ StorageBaseUrl: "s3://my_example_bucket/",
+ Encryption: &sdk.ExternalVolumeS3Encryption{
+ Type: "AWS_SSE_KMS",
+ KmsKeyId: &kmsKeyId,
+ },
+ },
+ },
+ }
+
+ req := sdk.NewCreateExternalVolumeRequest(id, storageLocations)
+ err := c.client().Create(ctx, req)
require.NoError(t, err)
+ _, showErr := c.client().ShowByID(ctx, id)
+ require.NoError(t, showErr)
+
return id, c.DropFunc(t, id)
}
+func (c *ExternalVolumeClient) Alter(t *testing.T, req *sdk.AlterExternalVolumeRequest) {
+ t.Helper()
+ ctx := context.Background()
+ err := c.client().Alter(ctx, req)
+ require.NoError(t, err)
+}
+
func (c *ExternalVolumeClient) DropFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() {
t.Helper()
+ ctx := context.Background()
return func() {
- err := c.exec(fmt.Sprintf(`drop external volume if exists %s`, id.FullyQualifiedName()))
+ err := c.client().Drop(ctx, sdk.NewDropExternalVolumeRequest(id).WithIfExists(true))
require.NoError(t, err)
}
}
diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go
index 0c94f05585..bde76ff381 100644
--- a/pkg/helpers/helpers.go
+++ b/pkg/helpers/helpers.go
@@ -2,11 +2,13 @@ package helpers
import (
"encoding/csv"
+ "encoding/json"
"fmt"
"log"
"path"
"reflect"
"regexp"
+ "slices"
"strconv"
"strings"
@@ -163,6 +165,98 @@ func ConcatSlices[T any](slices ...[]T) []T {
return tmp
}
+type StorageLocation struct {
+ Name string `json:"NAME"`
+ StorageProvider string `json:"STORAGE_PROVIDER"`
+ StorageBaseUrl string `json:"STORAGE_BASE_URL"`
+ StorageAwsRoleArn string `json:"STORAGE_AWS_ROLE_ARN"`
+ StorageAwsExternalId string `json:"STORAGE_AWS_EXTERNAL_ID"`
+ EncryptionType string `json:"ENCRYPTION_TYPE"`
+ EncryptionKmsKeyId string `json:"ENCRYPTION_KMS_KEY_ID"`
+ AzureTenantId string `json:"AZURE_TENANT_ID"`
+}
+
+func validateParsedExternalVolumeDescribed(p ParsedExternalVolumeDescribed) error {
+ if len(p.StorageLocations) == 0 {
+ return fmt.Errorf("No storage locations could be parsed from the external volume.")
+ }
+ if len(p.AllowWrites) == 0 {
+ return fmt.Errorf("The external volume AllowWrites property could not be parsed.")
+ }
+
+ for _, s := range p.StorageLocations {
+ if len(s.Name) == 0 {
+ return fmt.Errorf("A storage location's Name in this volume could not be parsed.")
+ }
+ if !slices.Contains(sdk.AsStringList(sdk.AllStorageProviderValues), s.StorageProvider) {
+ return fmt.Errorf("invalid storage provider parsed: %s", s)
+ }
+ if len(s.StorageBaseUrl) == 0 {
+ return fmt.Errorf("A storage location's StorageBaseUrl in this volume could not be parsed.")
+ }
+
+ storageProvider, err := sdk.ToStorageProvider(s.StorageProvider)
+ if err != nil {
+ return err
+ }
+
+ switch storageProvider {
+ case sdk.StorageProviderS3, sdk.StorageProviderS3GOV:
+ if len(s.StorageAwsRoleArn) == 0 {
+ return fmt.Errorf("An S3 storage location's StorageAwsRoleArn in this volume could not be parsed.")
+ }
+ case sdk.StorageProviderAzure:
+ if len(s.AzureTenantId) == 0 {
+ return fmt.Errorf("An Azure storage location's AzureTenantId in this volume could not be parsed.")
+ }
+ }
+ }
+
+ return nil
+}
+
+type ParsedExternalVolumeDescribed struct {
+ StorageLocations []StorageLocation
+ Active string
+ Comment string
+ AllowWrites string
+}
+
+func ParseExternalVolumeDescribed(props []sdk.ExternalVolumeProperty) (ParsedExternalVolumeDescribed, error) {
+ parsedExternalVolumeDescribed := ParsedExternalVolumeDescribed{}
+ var storageLocations []StorageLocation
+ for _, p := range props {
+ switch {
+ case p.Name == "COMMENT":
+ parsedExternalVolumeDescribed.Comment = p.Value
+ case p.Name == "ACTIVE":
+ parsedExternalVolumeDescribed.Active = p.Value
+ case p.Name == "ALLOW_WRITES":
+ parsedExternalVolumeDescribed.AllowWrites = p.Value
+ case strings.Contains(p.Name, "STORAGE_LOCATION_"):
+ storageLocation := StorageLocation{}
+ err := json.Unmarshal([]byte(p.Value), &storageLocation)
+ if err != nil {
+ return ParsedExternalVolumeDescribed{}, err
+ }
+ storageLocations = append(
+ storageLocations,
+ storageLocation,
+ )
+ default:
+ return ParsedExternalVolumeDescribed{}, fmt.Errorf("Unrecognized external volume property: %s", p.Name)
+ }
+ }
+
+ parsedExternalVolumeDescribed.StorageLocations = storageLocations
+ err := validateParsedExternalVolumeDescribed(parsedExternalVolumeDescribed)
+ if err != nil {
+ return ParsedExternalVolumeDescribed{}, err
+ }
+
+ return parsedExternalVolumeDescribed, nil
+}
+
// TODO(SNOW-1569530): address during identifiers rework follow-up
func ParseRootLocation(location string) (sdk.SchemaObjectIdentifier, string, error) {
location = strings.TrimPrefix(location, "@")
diff --git a/pkg/helpers/helpers_test.go b/pkg/helpers/helpers_test.go
index fac9ae1858..42c3d92e4c 100644
--- a/pkg/helpers/helpers_test.go
+++ b/pkg/helpers/helpers_test.go
@@ -2,6 +2,8 @@ package helpers
import (
"fmt"
+ "reflect"
+ "strconv"
"testing"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
@@ -387,3 +389,680 @@ func Test_ContainsIdentifierIgnoreQuotes(t *testing.T) {
})
}
}
+
+// External volume helper tests
+
+// Generate input to the ParseExternalVolumeDescribedInput, useful for testing purposes
+func GenerateParseExternalVolumeDescribedInput(comment string, allowWrites string, storageLocations []string, active string) []sdk.ExternalVolumeProperty {
+ storageLocationProperties := make([]sdk.ExternalVolumeProperty, len(storageLocations))
+ allowWritesProperty := sdk.ExternalVolumeProperty{
+ Parent: "",
+ Name: "ALLOW_WRITES",
+ Type: "Boolean",
+ Value: allowWrites,
+ Default: "true",
+ }
+
+ commentProperty := sdk.ExternalVolumeProperty{
+ Parent: "",
+ Name: "COMMENT",
+ Type: "String",
+ Value: comment,
+ Default: "",
+ }
+
+ activeProperty := sdk.ExternalVolumeProperty{
+ Parent: "STORAGE_LOCATIONS",
+ Name: "ACTIVE",
+ Type: "String",
+ Value: active,
+ Default: "",
+ }
+
+ for i, property := range storageLocations {
+ storageLocationProperties[i] = sdk.ExternalVolumeProperty{
+ Parent: "STORAGE_LOCATIONS",
+ Name: fmt.Sprintf("STORAGE_LOCATION_%s", strconv.Itoa(i+1)),
+ Type: "String",
+ Value: property,
+ Default: "",
+ }
+ }
+
+ return append(append([]sdk.ExternalVolumeProperty{allowWritesProperty, commentProperty}, storageLocationProperties...), activeProperty)
+}
+
+func Test_GenerateParseExternalVolumeDescribedInput(t *testing.T) {
+ azureStorageLocationName := "azureTest"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+ azureStorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["azure://123456789.blob.core.windows.net/my_example_container"],"AZURE_TENANT_ID":"%s","AZURE_MULTI_TENANT_APP_NAME":"test12","AZURE_CONSENT_URL":"https://login.microsoftonline.com/123456789/oauth2/authorize?client_id=test&response_type=test","ENCRYPTION_TYPE":"NONE","ENCRYPTION_KMS_KEY_ID":""}`,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ azureTenantId,
+ )
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionTypeNone := "NONE"
+ gcsStorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["gcs://my_example_bucket/*"],"STORAGE_GCP_SERVICE_ACCOUNT":"test@test.iam.test.com","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":""}`,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ gcsEncryptionTypeNone,
+ )
+
+ s3StorageLocationName := "s3Test"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3StorageAwsExternalId := "123456789"
+ s3EncryptionTypeNone := "NONE"
+
+ s3StorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeNone,
+ )
+
+ allowWritesTrue := "true"
+ comment := "some comment"
+ cases := []struct {
+ TestName string
+ Comment string
+ AllowWrites string
+ StorageLocations []string
+ Active string
+ ExpectedOutput []sdk.ExternalVolumeProperty
+ }{
+ {
+ TestName: "Generate input",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ StorageLocations: []string{s3StorageLocationStandard},
+ Active: "",
+ ExpectedOutput: []sdk.ExternalVolumeProperty{
+ {
+ Parent: "",
+ Name: "ALLOW_WRITES",
+ Type: "Boolean",
+ Value: allowWritesTrue,
+ Default: "true",
+ },
+ {
+ Parent: "",
+ Name: "COMMENT",
+ Type: "String",
+ Value: comment,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_1",
+ Type: "String",
+ Value: s3StorageLocationStandard,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "ACTIVE",
+ Type: "String",
+ Value: "",
+ Default: "",
+ },
+ },
+ },
+ {
+ TestName: "Generate input - multiple locations and active set",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ StorageLocations: []string{s3StorageLocationStandard, azureStorageLocationStandard, gcsStorageLocationStandard},
+ Active: s3StorageLocationName,
+ ExpectedOutput: []sdk.ExternalVolumeProperty{
+ {
+ Parent: "",
+ Name: "ALLOW_WRITES",
+ Type: "Boolean",
+ Value: allowWritesTrue,
+ Default: "true",
+ },
+ {
+ Parent: "",
+ Name: "COMMENT",
+ Type: "String",
+ Value: comment,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_1",
+ Type: "String",
+ Value: s3StorageLocationStandard,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_2",
+ Type: "String",
+ Value: azureStorageLocationStandard,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_3",
+ Type: "String",
+ Value: gcsStorageLocationStandard,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "ACTIVE",
+ Type: "String",
+ Value: s3StorageLocationName,
+ Default: "",
+ },
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.TestName, func(t *testing.T) {
+ generatedInput := GenerateParseExternalVolumeDescribedInput(
+ tc.Comment,
+ tc.AllowWrites,
+ tc.StorageLocations,
+ tc.Active,
+ )
+
+ assert.Equal(t, len(tc.ExpectedOutput), len(generatedInput))
+ for i := range generatedInput {
+ assert.Equal(t, tc.ExpectedOutput[i].Parent, generatedInput[i].Parent)
+ assert.Equal(t, tc.ExpectedOutput[i].Name, generatedInput[i].Name)
+ assert.Equal(t, tc.ExpectedOutput[i].Type, generatedInput[i].Type)
+ assert.Equal(t, tc.ExpectedOutput[i].Value, generatedInput[i].Value)
+ assert.Equal(t, tc.ExpectedOutput[i].Default, generatedInput[i].Default)
+ }
+ })
+ }
+}
+
+func Test_ParseExternalVolumeDescribed(t *testing.T) {
+ azureStorageLocationName := "azureTest"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+ azureEncryptionTypeNone := "NONE"
+ azureStorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["azure://123456789.blob.core.windows.net/my_example_container"],"AZURE_TENANT_ID":"%s","AZURE_MULTI_TENANT_APP_NAME":"test12","AZURE_CONSENT_URL":"https://login.microsoftonline.com/123456789/oauth2/authorize?client_id=test&response_type=test","ENCRYPTION_TYPE":"NONE","ENCRYPTION_KMS_KEY_ID":""}`,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ azureTenantId,
+ )
+
+ azureStorageLocationWithExtraFields := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["azure://123456789.blob.core.windows.net/my_example_container"],"AZURE_TENANT_ID":"%s","AZURE_MULTI_TENANT_APP_NAME":"test12","AZURE_CONSENT_URL":"https://login.microsoftonline.com/123456789/oauth2/authorize?client_id=test&response_type=test","ENCRYPTION_TYPE":"NONE","ENCRYPTION_KMS_KEY_ID":"","EXTRA_FIELD_ONE":"testing","EXTRA_FIELD_TWO":"123456"}`,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ azureTenantId,
+ )
+
+ azureStorageLocationMissingTenantId := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["azure://123456789.blob.core.windows.net/my_example_container"],"AZURE_MULTI_TENANT_APP_NAME":"test12","AZURE_CONSENT_URL":"https://login.microsoftonline.com/123456789/oauth2/authorize?client_id=test&response_type=test","ENCRYPTION_TYPE":"NONE","ENCRYPTION_KMS_KEY_ID":""}`,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ )
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionTypeNone := "NONE"
+ gcsEncryptionTypeSseKms := "GCS_SSE_KMS"
+ gcsEncryptionKmsKeyId := "123456789"
+ gcsStorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["gcs://my_example_bucket/*"],"STORAGE_GCP_SERVICE_ACCOUNT":"test@test.iam.test.com","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":""}`,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ gcsEncryptionTypeNone,
+ )
+
+ gcsStorageLocationWithExtraFields := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["gcs://my_example_bucket/*"],"STORAGE_GCP_SERVICE_ACCOUNT":"test@test.iam.test.com","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"","EXTRA_FIELD_ONE":"testing","EXTRA_FIELD_TWO":"123456"}`,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ gcsEncryptionTypeNone,
+ )
+
+ gcsStorageLocationKmsEncryption := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["gcs://my_example_bucket/*"],"STORAGE_GCP_SERVICE_ACCOUNT":"test@test.iam.test.com","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s"}`,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId,
+ )
+
+ gcsStorageLocationMissingBaseUrl := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_ALLOWED_LOCATIONS":["gcs://my_example_bucket/*"],"STORAGE_GCP_SERVICE_ACCOUNT":"test@test.iam.test.com","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":""}`,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsEncryptionTypeNone,
+ )
+
+ s3StorageLocationName := "s3Test"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3StorageAwsExternalId := "123456789"
+ s3EncryptionTypeNone := "NONE"
+ s3EncryptionTypeSseS3 := "AWS_SSE_S3"
+ s3EncryptionTypeSseKms := "AWS_SSE_KMS"
+ s3EncryptionKmsKeyId := "123456789"
+
+ s3StorageLocationStandard := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeNone,
+ )
+
+ s3StorageLocationWithExtraFields := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s","EXTRA_FIELD_ONE":"testing","EXTRA_FIELD_TWO":"123456"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId,
+ )
+
+ s3StorageLocationSseS3Encryption := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeSseS3,
+ )
+
+ s3StorageLocationSseKmsEncryption := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s", "ENCRYPTION_KMS_KEY_ID":"%s"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId,
+ )
+
+ s3StorageLocationMissingRoleArn := fmt.Sprintf(
+ `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_ALLOWED_LOCATIONS":["s3://my_example_bucket/*"],"STORAGE_AWS_IAM_USER_ARN":"arn:aws:iam::123456789:user/a11b0000-s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsExternalId,
+ s3EncryptionTypeNone,
+ )
+ allowWritesTrue := "true"
+ allowWritesFalse := "false"
+ comment := "some comment"
+ validCases := []struct {
+ Name string
+ DescribeOutput []sdk.ExternalVolumeProperty
+ ParsedDescribeOutput ParsedExternalVolumeDescribed
+ }{
+ {
+ Name: "Volume with azure storage location",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesFalse, []string{azureStorageLocationStandard}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: azureStorageLocationName,
+ StorageProvider: azureStorageProvider,
+ StorageBaseUrl: azureStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: azureEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: azureTenantId,
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesFalse,
+ },
+ },
+ {
+ Name: "Volume with azure storage location, with extra fields",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesFalse, []string{azureStorageLocationWithExtraFields}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: azureStorageLocationName,
+ StorageProvider: azureStorageProvider,
+ StorageBaseUrl: azureStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: azureEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: azureTenantId,
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesFalse,
+ },
+ },
+ {
+ Name: "Volume with gcs storage location",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{gcsStorageLocationStandard}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: gcsStorageLocationName,
+ StorageProvider: gcsStorageProvider,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: gcsEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with gcs storage location, with extra fields",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{gcsStorageLocationWithExtraFields}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: gcsStorageLocationName,
+ StorageProvider: gcsStorageProvider,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: gcsEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with gcs storage location, sse kms encryption",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{gcsStorageLocationKmsEncryption}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: gcsStorageLocationName,
+ StorageProvider: gcsStorageProvider,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: gcsEncryptionTypeSseKms,
+ EncryptionKmsKeyId: gcsEncryptionKmsKeyId,
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with s3 storage location",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{s3StorageLocationStandard}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with s3 storage location, with extra fields",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{s3StorageLocationWithExtraFields}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeSseKms,
+ EncryptionKmsKeyId: s3EncryptionKmsKeyId,
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with s3 storage location, sse s3 encryption",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{s3StorageLocationSseS3Encryption}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeSseS3,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with s3 storage location, sse kms encryption",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{s3StorageLocationSseKmsEncryption}, ""),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeSseKms,
+ EncryptionKmsKeyId: s3EncryptionKmsKeyId,
+ AzureTenantId: "",
+ },
+ },
+ Active: "",
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with multiple storage locations and active set",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(
+ comment,
+ allowWritesTrue,
+ []string{s3StorageLocationStandard, gcsStorageLocationStandard, azureStorageLocationStandard},
+ s3StorageLocationName,
+ ),
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: gcsStorageLocationName,
+ StorageProvider: gcsStorageProvider,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: gcsEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: azureStorageLocationName,
+ StorageProvider: azureStorageProvider,
+ StorageBaseUrl: azureStorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: azureEncryptionTypeNone,
+ EncryptionKmsKeyId: "",
+ AzureTenantId: azureTenantId,
+ },
+ },
+ Active: s3StorageLocationName,
+ Comment: comment,
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ {
+ Name: "Volume with s3 storage location that has no comment set (in this case describe doesn't contain a comment property)",
+ DescribeOutput: []sdk.ExternalVolumeProperty{
+ {
+ Parent: "",
+ Name: "ALLOW_WRITES",
+ Type: "Boolean",
+ Value: allowWritesTrue,
+ Default: "true",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_1",
+ Type: "String",
+ Value: s3StorageLocationSseKmsEncryption,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "ACTIVE",
+ Type: "String",
+ Value: s3StorageLocationName,
+ Default: "",
+ },
+ },
+ ParsedDescribeOutput: ParsedExternalVolumeDescribed{
+ StorageLocations: []StorageLocation{
+ {
+ Name: s3StorageLocationName,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: s3StorageAwsExternalId,
+ EncryptionType: s3EncryptionTypeSseKms,
+ EncryptionKmsKeyId: s3EncryptionKmsKeyId,
+ AzureTenantId: "",
+ },
+ },
+ Active: s3StorageLocationName,
+ Comment: "",
+ AllowWrites: allowWritesTrue,
+ },
+ },
+ }
+
+ invalidCases := []struct {
+ Name string
+ DescribeOutput []sdk.ExternalVolumeProperty
+ }{
+ {
+ Name: "Volume with s3 storage location, missing STORAGE_AWS_ROLE_ARN",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{s3StorageLocationMissingRoleArn}, ""),
+ },
+ {
+ Name: "Volume with azure storage location, missing AZURE_TENANT_ID",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{azureStorageLocationMissingTenantId}, ""),
+ },
+ {
+ Name: "Volume with gcs storage location, missing STORAGE_BASE_URL",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{gcsStorageLocationMissingBaseUrl}, ""),
+ },
+ {
+ Name: "Volume with no storage locations",
+ DescribeOutput: GenerateParseExternalVolumeDescribedInput(comment, allowWritesTrue, []string{}, ""),
+ },
+ {
+ Name: "Volume with no allow writes",
+ DescribeOutput: []sdk.ExternalVolumeProperty{
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "STORAGE_LOCATION_1",
+ Type: "String",
+ Value: s3StorageLocationSseKmsEncryption,
+ Default: "",
+ },
+ {
+ Parent: "STORAGE_LOCATIONS",
+ Name: "ACTIVE",
+ Type: "String",
+ Value: s3StorageLocationName,
+ Default: "",
+ },
+ },
+ },
+ }
+
+ for _, tc := range validCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ parsed, err := ParseExternalVolumeDescribed(tc.DescribeOutput)
+ require.NoError(t, err)
+ assert.True(t, reflect.DeepEqual(tc.ParsedDescribeOutput, parsed))
+ })
+ }
+
+ for _, tc := range invalidCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ _, err := ParseExternalVolumeDescribed(tc.DescribeOutput)
+ require.Error(t, err)
+ })
+ }
+}
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go
index f372dcfc71..24f5d7f479 100644
--- a/pkg/provider/provider.go
+++ b/pkg/provider/provider.go
@@ -439,6 +439,7 @@ func getResources() map[string]*schema.Resource {
"snowflake_external_function": resources.ExternalFunction(),
"snowflake_external_oauth_integration": resources.ExternalOauthIntegration(),
"snowflake_external_table": resources.ExternalTable(),
+ "snowflake_external_volume": resources.ExternalVolume(),
"snowflake_failover_group": resources.FailoverGroup(),
"snowflake_file_format": resources.FileFormat(),
"snowflake_function": resources.Function(),
diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go
index 48f3550c96..45800bb5b0 100644
--- a/pkg/provider/resources/resources.go
+++ b/pkg/provider/resources/resources.go
@@ -19,6 +19,7 @@ const (
ExternalFunction resource = "snowflake_external_function"
ExternalTable resource = "snowflake_external_table"
ExternalOauthSecurityIntegration resource = "snowflake_external_oauth_security_integration"
+ ExternalVolume resource = "snowflake_external_volume"
FailoverGroup resource = "snowflake_failover_group"
FileFormat resource = "snowflake_file_format"
Function resource = "snowflake_function"
diff --git a/pkg/resources/external_volume.go b/pkg/resources/external_volume.go
new file mode 100644
index 0000000000..b2683849f6
--- /dev/null
+++ b/pkg/resources/external_volume.go
@@ -0,0 +1,695 @@
+package resources
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var externalVolumeSchema = map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ ForceNew: true,
+ Required: true,
+ Description: blocklistedCharactersFieldDescription("Identifier for the external volume; must be unique for your account."),
+ DiffSuppressFunc: suppressIdentifierQuoting,
+ },
+ // A list is used as the order of storage locations matter. Storage location position in the list is used to select
+ // the active storage location - https://docs.snowflake.com/en/user-guide/tables-iceberg-storage#active-storage-location
+ // This is also why it has been left as one list with optional cloud dependent parameters, rather than splitting into
+ // one list per cloud provider.
+ "storage_location": {
+ Type: schema.TypeList,
+ Required: true,
+ MinItems: 1,
+ Description: "List of named cloud storage locations in different regions and, optionally, cloud platforms. Minimum 1 required. The order of the list is important as it impacts the active storage location, and updates will be triggered if it changes. Note that not all parameter combinations are valid as they depend on the given storage_provider. Consult [the docs](https://docs.snowflake.com/en/sql-reference/sql/create-external-volume#cloud-provider-parameters-cloudproviderparams) for more details on this.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "storage_location_name": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: blocklistedCharactersFieldDescription("Name of the storage location. Must be unique for the external volume. Do not use the name `terraform_provider_sentinel_storage_location` - this is reserved for the provider for performing update operations."),
+ DiffSuppressFunc: suppressIdentifierQuoting,
+ },
+ "storage_provider": {
+ Type: schema.TypeString,
+ Required: true,
+ ValidateDiagFunc: sdkValidation(sdk.ToStorageProvider),
+ DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToStorageProvider)),
+ Description: fmt.Sprintf("Specifies the cloud storage provider that stores your data files. Valid values are (case-insensitive): %s.", possibleValuesListed(sdk.AllStorageProviderValues)),
+ },
+ "storage_base_url": {
+ Type: schema.TypeString,
+ Required: true,
+ Description: "Specifies the base URL for your cloud storage location.",
+ },
+ "storage_aws_role_arn": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies the case-sensitive Amazon Resource Name (ARN) of the AWS identity and access management (IAM) role that grants privileges on the S3 bucket containing your data files.",
+ },
+ "storage_aws_external_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: "External ID that Snowflake uses to establish a trust relationship with AWS.",
+ },
+ "encryption_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies the encryption type used.",
+ DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool {
+ return oldValue == "NONE" && newValue == ""
+ },
+ },
+ "encryption_kms_key_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies the ID for the KMS-managed key used to encrypt files.",
+ },
+ "azure_tenant_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies the ID for your Office 365 tenant that the allowed and blocked storage accounts belong to.",
+ },
+ },
+ },
+ },
+ "comment": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies a comment for the external volume.",
+ },
+ "allow_writes": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: BooleanDefault,
+ Description: booleanStringFieldDescription("Specifies whether write operations are allowed for the external volume; must be set to TRUE for Iceberg tables that use Snowflake as the catalog."),
+ },
+ ShowOutputAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Outputs the result of `SHOW EXTERNAL VOLUMES` for the given external volume.",
+ Elem: &schema.Resource{
+ Schema: schemas.ShowExternalVolumeSchema,
+ },
+ },
+ DescribeOutputAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Outputs the result of `DESCRIBE EXTERNAL VOLUME` for the given external volume.",
+ Elem: &schema.Resource{
+ Schema: schemas.DescribeExternalVolumeSchema,
+ },
+ },
+ FullyQualifiedNameAttributeName: schemas.FullyQualifiedNameSchema,
+}
+
+// ExternalVolume returns a pointer to the resource representing an external volume.
+func ExternalVolume() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: CreateContextExternalVolume,
+ UpdateContext: UpdateContextExternalVolume,
+ ReadContext: ReadContextExternalVolume(true),
+ DeleteContext: DeleteContextExternalVolume,
+ Description: "Resource used to manage external volume objects. For more information, check [external volume documentation](https://docs.snowflake.com/en/sql-reference/commands-data-loading#external-volume).",
+
+ Schema: externalVolumeSchema,
+ Importer: &schema.ResourceImporter{
+ StateContext: ImportExternalVolume,
+ },
+
+ CustomizeDiff: customdiff.All(
+ ComputedIfAnyAttributeChanged(externalVolumeSchema, ShowOutputAttributeName, "name", "allow_writes", "comment"),
+ ComputedIfAnyAttributeChanged(externalVolumeSchema, DescribeOutputAttributeName, "name", "allow_writes", "comment", "storage_location"),
+ ),
+ }
+}
+
+func ImportExternalVolume(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
+ logging.DebugLogger.Printf("[DEBUG] Starting external volume import")
+ client := meta.(*provider.Context).Client
+ id, err := sdk.ParseAccountObjectIdentifier(d.Id())
+ if err != nil {
+ return nil, err
+ }
+
+ if err := d.Set("name", id.Name()); err != nil {
+ return nil, err
+ }
+
+ externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = d.Set("allow_writes", booleanStringFromBool(externalVolume.AllowWrites)); err != nil {
+ return nil, err
+ }
+
+ externalVolumeDescribe, err := client.ExternalVolumes.Describe(ctx, id)
+ if err != nil {
+ return nil, err
+ }
+
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(externalVolumeDescribe)
+ if err != nil {
+ return nil, err
+ }
+
+ storageLocations := make([]map[string]any, len(parsedExternalVolumeDescribed.StorageLocations))
+ for i, storageLocation := range parsedExternalVolumeDescribed.StorageLocations {
+ storageLocations[i] = map[string]any{
+ "storage_location_name": storageLocation.Name,
+ "storage_provider": storageLocation.StorageProvider,
+ "storage_base_url": storageLocation.StorageBaseUrl,
+ "storage_aws_role_arn": storageLocation.StorageAwsRoleArn,
+ "storage_aws_external_id": storageLocation.StorageAwsExternalId,
+ "encryption_type": storageLocation.EncryptionType,
+ "encryption_kms_key_id": storageLocation.EncryptionKmsKeyId,
+ "azure_tenant_id": storageLocation.AzureTenantId,
+ }
+ }
+
+ if err = d.Set("storage_location", storageLocations); err != nil {
+ return nil, err
+ }
+
+ return []*schema.ResourceData{d}, nil
+}
+
+func CreateContextExternalVolume(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+
+ name := d.Get("name").(string)
+ id := sdk.NewAccountObjectIdentifier(name)
+
+ storageLocations, err := extractStorageLocations(d.Get("storage_location"))
+ if err != nil {
+ return diag.FromErr(fmt.Errorf("error creating external volume %v err = %w", id.Name(), err))
+ }
+
+ req := sdk.NewCreateExternalVolumeRequest(id, storageLocations)
+
+ if v, ok := d.GetOk("comment"); ok {
+ req.WithComment(v.(string))
+ }
+
+ if v := d.Get("allow_writes").(string); v != BooleanDefault {
+ parsed, err := booleanStringToBool(v)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ req.WithAllowWrites(parsed)
+ }
+
+ createErr := client.ExternalVolumes.Create(ctx, req)
+ if err != nil {
+ return diag.FromErr(fmt.Errorf("error creating external volume %v err = %w", id.Name(), createErr))
+ }
+
+ d.SetId(helpers.EncodeResourceIdentifier(id))
+ return ReadContextExternalVolume(false)(ctx, d, meta)
+}
+
+func ReadContextExternalVolume(withExternalChangesMarking bool) schema.ReadContextFunc {
+ return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+ id, err := sdk.ParseAccountObjectIdentifier(d.Id())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
+ if err != nil {
+ if errors.Is(err, sdk.ErrObjectNotFound) {
+ d.SetId("")
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Failed to query external volume. Marking the resource as removed.",
+ Detail: fmt.Sprintf("External Volume: %s, Err: %s", id.FullyQualifiedName(), err),
+ },
+ }
+ }
+
+ return diag.FromErr(err)
+ }
+
+ if err := d.Set(FullyQualifiedNameAttributeName, id.FullyQualifiedName()); err != nil {
+ return diag.FromErr(err)
+ }
+
+ if withExternalChangesMarking {
+ if err = handleExternalChangesToObjectInShow(d,
+ outputMapping{"allow_writes", "allow_writes", externalVolume.AllowWrites, booleanStringFromBool(externalVolume.AllowWrites), nil},
+ ); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+
+ if err = setStateToValuesFromConfig(d, externalVolumeSchema, []string{
+ "allow_writes",
+ }); err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err = d.Set("comment", externalVolume.Comment); err != nil {
+ return diag.FromErr(err)
+ }
+
+ externalVolumeDescribe, err := client.ExternalVolumes.Describe(ctx, id)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(externalVolumeDescribe)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ storageLocations := make([]map[string]any, len(parsedExternalVolumeDescribed.StorageLocations))
+ for i, storageLocation := range parsedExternalVolumeDescribed.StorageLocations {
+ storageLocations[i] = map[string]any{
+ "storage_location_name": storageLocation.Name,
+ "storage_provider": storageLocation.StorageProvider,
+ "storage_base_url": storageLocation.StorageBaseUrl,
+ "storage_aws_role_arn": storageLocation.StorageAwsRoleArn,
+ "storage_aws_external_id": storageLocation.StorageAwsExternalId,
+ "encryption_type": storageLocation.EncryptionType,
+ "encryption_kms_key_id": storageLocation.EncryptionKmsKeyId,
+ "azure_tenant_id": storageLocation.AzureTenantId,
+ }
+ }
+
+ if err = d.Set("storage_location", storageLocations); err != nil {
+ return diag.FromErr(err)
+ }
+ if err = d.Set(DescribeOutputAttributeName, schemas.ExternalVolumeDescriptionToSchema(externalVolumeDescribe)); err != nil {
+ return diag.FromErr(err)
+ }
+ if err = d.Set(ShowOutputAttributeName, []map[string]any{schemas.ExternalVolumeToSchema(externalVolume)}); err != nil {
+ return diag.FromErr(err)
+ }
+
+ return nil
+ }
+}
+
+func UpdateContextExternalVolume(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+ id, err := sdk.ParseAccountObjectIdentifier(d.Id())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ set := sdk.NewAlterExternalVolumeSetRequest()
+
+ if d.HasChange("comment") {
+ // not using d.GetOk as that doesn't let comments be reset to the empty string
+ set.WithComment(d.Get("comment").(string))
+ }
+
+ if d.HasChange("allow_writes") {
+ if v := d.Get("allow_writes").(string); v != BooleanDefault {
+ parsed, err := booleanStringToBool(v)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ set.WithAllowWrites(parsed)
+ } else {
+ // no way to unset allow writes - set to false as a default
+ set.WithAllowWrites(false)
+ }
+ }
+
+ if (*set != sdk.AlterExternalVolumeSetRequest{}) {
+ if err := client.ExternalVolumes.Alter(ctx, sdk.NewAlterExternalVolumeRequest(id).WithSet(*set)); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+
+ if d.HasChange("storage_location") {
+ old, new := d.GetChange("storage_location")
+ oldLocations, err := extractStorageLocations(old)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ newLocations, err := extractStorageLocations(new)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Storage locations can only be added to the tail of the list, but can be
+ // removed at any position. Given this limitation, to keep the configuration order
+ // matching that on Snowflake the list needs to be partially recreated. For example, if a location
+ // is added in the configuration at index 5 in the list, all existing storage locations from index 5
+ // need to be removed, then the new location can be added, and then the removed locations
+ // can be added back. The storage locations lower than index 5 don't need to be modified.
+ // The removal process could be done without the above recreation, but it handles this case
+ // too so it's used for both actions.
+ commonPrefixLastIndex, err := sdk.CommonPrefixLastIndex(newLocations, oldLocations)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ var removedLocations []sdk.ExternalVolumeStorageLocation
+ var addedLocations []sdk.ExternalVolumeStorageLocation
+ if commonPrefixLastIndex == -1 {
+ removedLocations = oldLocations
+ addedLocations = newLocations
+ } else {
+ // Could +1 on the prefix here as the lists until and including this index
+ // are identical, would need to add some more checks for list length to avoid
+ // an array index out of bounds error
+ removedLocations = oldLocations[commonPrefixLastIndex:]
+ addedLocations = newLocations[commonPrefixLastIndex:]
+ }
+
+ if len(removedLocations) == len(oldLocations) {
+ // Create a temporary storage location, which is a copy of a storage location currently existing
+ // except with a different name. This is done to avoid recreating the external volume, which
+ // would otherwise be necessary as a minimum of 1 storage location per external volume is required.
+ // The alternative solution of adding volumes before removing them isn't possible as
+ // name must be unique for storage locations
+ tempStorageLocation, err := sdk.CopySentinelStorageLocation(removedLocations[0])
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ addTempErr := addStorageLocation(tempStorageLocation, client, ctx, id)
+ if addTempErr != nil {
+ return diag.FromErr(addTempErr)
+ }
+
+ updateErr := updateStorageLocations(removedLocations, addedLocations, client, ctx, id)
+ // TODO use defer for the removal of the temp storage location
+ if updateErr != nil {
+ // Try to remove the temp location and then return with error
+ removeErr := removeStorageLocation(tempStorageLocation, client, ctx, id)
+ if removeErr != nil {
+ return diag.FromErr(errors.Join(updateErr, removeErr))
+ }
+
+ return diag.FromErr(updateErr)
+ }
+
+ removeErr := removeStorageLocation(tempStorageLocation, client, ctx, id)
+ if removeErr != nil {
+ return diag.FromErr(removeErr)
+ }
+ } else {
+ updateErr := updateStorageLocations(removedLocations, addedLocations, client, ctx, id)
+ if updateErr != nil {
+ return diag.FromErr(updateErr)
+ }
+ }
+
+ }
+
+ return ReadContextExternalVolume(false)(ctx, d, meta)
+}
+
+func DeleteContextExternalVolume(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+ id, idErr := sdk.ParseAccountObjectIdentifier(d.Id())
+ if idErr != nil {
+ return diag.FromErr(idErr)
+ }
+
+ err := client.ExternalVolumes.Drop(ctx, sdk.NewDropExternalVolumeRequest(id).WithIfExists(true))
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId("")
+ return nil
+}
+
+func extractStorageLocations(v any) ([]sdk.ExternalVolumeStorageLocation, error) {
+ _, ok := v.([]any)
+ if !ok {
+ return nil, fmt.Errorf("unable to extract storage locations, input is either nil or non expected type (%T): %v", v, v)
+ }
+
+ storageLocations := make([]sdk.ExternalVolumeStorageLocation, len(v.([]any)))
+ for i, storageLocationConfigRaw := range v.([]any) {
+ storageLocationConfig, ok := storageLocationConfigRaw.(map[string]any)
+ if !ok {
+ return nil, fmt.Errorf("unable to extract storage location, non expected type of %T: %v", storageLocationConfigRaw, storageLocationConfigRaw)
+ }
+
+ name, ok := storageLocationConfig["storage_location_name"].(string)
+ if !ok {
+ return nil, fmt.Errorf("unable to extract storage location, missing storage_location_name key in storage location")
+ }
+
+ storageProvider, ok := storageLocationConfig["storage_provider"].(string)
+ if !ok {
+ return nil, fmt.Errorf("unable to extract storage location, missing storage_provider key in storage location")
+ }
+
+ storageBaseUrl, ok := storageLocationConfig["storage_base_url"].(string)
+ if !ok {
+ return nil, fmt.Errorf("unable to extract storage location, missing storage_base_url key in storage location")
+ }
+
+ storageProviderParsed, err := sdk.ToStorageProvider(storageProvider)
+ if err != nil {
+ return nil, err
+ }
+
+ var storageLocation sdk.ExternalVolumeStorageLocation
+ switch storageProviderParsed {
+ case sdk.StorageProviderS3, sdk.StorageProviderS3GOV:
+ // Test that azure_tenant_id is not given
+ // If given non empty plans will be produced
+ azureTenantId, ok := storageLocationConfig["azure_tenant_id"].(string)
+ if ok && len(azureTenantId) > 0 {
+ return nil, fmt.Errorf("unable to extract storage location, azure_tenant_id provided for s3 storage location")
+ }
+
+ storageAwsRoleArn, ok := storageLocationConfig["storage_aws_role_arn"].(string)
+ if !ok || len(storageAwsRoleArn) == 0 {
+ return nil, fmt.Errorf("unable to extract storage location, missing storage_aws_role_arn key in an s3 storage location")
+ }
+
+ s3StorageProvider, err := sdk.ToS3StorageProvider(storageProvider)
+ if err != nil {
+ return nil, err
+ }
+
+ s3StorageLocation := &sdk.S3StorageLocationParams{
+ Name: name,
+ StorageProvider: s3StorageProvider,
+ StorageBaseUrl: storageBaseUrl,
+ StorageAwsRoleArn: storageAwsRoleArn,
+ }
+
+ encryptionType, ok := storageLocationConfig["encryption_type"].(string)
+ if ok && len(encryptionType) > 0 {
+ encryptionTypeParsed, err := sdk.ToS3EncryptionType(encryptionType)
+ if err != nil {
+ return nil, err
+ }
+
+ encryptionKmsKeyId, ok := storageLocationConfig["encryption_kms_key_id"].(string)
+ if ok && len(encryptionKmsKeyId) > 0 {
+ s3StorageLocation.Encryption = &sdk.ExternalVolumeS3Encryption{
+ Type: encryptionTypeParsed,
+ KmsKeyId: &encryptionKmsKeyId,
+ }
+ } else {
+ s3StorageLocation.Encryption = &sdk.ExternalVolumeS3Encryption{
+ Type: encryptionTypeParsed,
+ }
+ }
+ }
+
+ storageLocation = sdk.ExternalVolumeStorageLocation{
+ S3StorageLocationParams: s3StorageLocation,
+ }
+ case sdk.StorageProviderGCS:
+ // Test that azure_tenant_id and storage_aws_role_arn are not given
+ // If given non empty plans will be produced
+ azureTenantId, ok := storageLocationConfig["azure_tenant_id"].(string)
+ if ok && len(azureTenantId) > 0 {
+ return nil, fmt.Errorf("unable to extract storage location, azure_tenant_id provided for gcs storage location")
+ }
+
+ storageAwsRoleArn, ok := storageLocationConfig["storage_aws_role_arn"].(string)
+ if ok && len(storageAwsRoleArn) > 0 {
+ return nil, fmt.Errorf("unable to extract storage location, storage_aws_role_arn provided for gcs storage location")
+ }
+
+ gcsStorageLocation := &sdk.GCSStorageLocationParams{
+ Name: name,
+ StorageBaseUrl: storageBaseUrl,
+ }
+ encryptionType, ok := storageLocationConfig["encryption_type"].(string)
+ if ok && len(encryptionType) > 0 {
+ encryptionTypeParsed, err := sdk.ToGCSEncryptionType(encryptionType)
+ if err != nil {
+ return nil, err
+ }
+ encryptionKmsKeyId, ok := storageLocationConfig["encryption_kms_key_id"].(string)
+ if ok && len(encryptionKmsKeyId) > 0 {
+ gcsStorageLocation.Encryption = &sdk.ExternalVolumeGCSEncryption{
+ Type: encryptionTypeParsed,
+ KmsKeyId: &encryptionKmsKeyId,
+ }
+ } else {
+ gcsStorageLocation.Encryption = &sdk.ExternalVolumeGCSEncryption{
+ Type: encryptionTypeParsed,
+ }
+ }
+ }
+
+ storageLocation = sdk.ExternalVolumeStorageLocation{
+ GCSStorageLocationParams: gcsStorageLocation,
+ }
+ case sdk.StorageProviderAzure:
+ // Test that storage_aws_role_arn and encryption_kms_key_id is not given
+ // If given non empty plans will be produced
+ storageAwsRolArn, ok := storageLocationConfig["storage_aws_role_arn"].(string)
+ if ok && len(storageAwsRolArn) > 0 {
+ return nil, fmt.Errorf("unable to extract storage location, storage_aws_role_arn provided for azure storage location")
+ }
+
+ encryptionKmsKeyId, ok := storageLocationConfig["encryption_kms_key_id"].(string)
+ if ok && len(encryptionKmsKeyId) > 0 {
+ return nil, fmt.Errorf("unable to extract storage location, encryption_kms_key_id provided for azure storage location")
+ }
+
+ // TODO add check that encryption_type is not set in the config for azure storage locations
+ // This may be more difficult as NONE is returned as the encyption type from Snowflake for azure
+ // storage locations, although it's not documented as a parameter
+
+ azureTenantId, ok := storageLocationConfig["azure_tenant_id"].(string)
+ if !ok || len(azureTenantId) == 0 {
+ return nil, fmt.Errorf("unable to extract storage location, missing azure_tenant_id provider key in an azure storage location")
+ }
+
+ storageLocation = sdk.ExternalVolumeStorageLocation{
+ AzureStorageLocationParams: &sdk.AzureStorageLocationParams{
+ Name: name,
+ AzureTenantId: azureTenantId,
+ StorageBaseUrl: storageBaseUrl,
+ },
+ }
+ }
+ storageLocations[i] = storageLocation
+ }
+ return storageLocations, nil
+}
+
+func addStorageLocation(
+ addedLocation sdk.ExternalVolumeStorageLocation,
+ client *sdk.Client,
+ ctx context.Context,
+ id sdk.AccountObjectIdentifier,
+) error {
+ storageProvider, err := sdk.GetStorageLocationStorageProvider(addedLocation)
+ if err != nil {
+ return err
+ }
+
+ var newStorageLocationreq *sdk.ExternalVolumeStorageLocationRequest
+ switch storageProvider {
+ case sdk.StorageProviderS3, sdk.StorageProviderS3GOV:
+ addedLocation := addedLocation.S3StorageLocationParams
+ s3ParamsRequest := sdk.NewS3StorageLocationParamsRequest(
+ addedLocation.Name,
+ addedLocation.StorageProvider,
+ addedLocation.StorageAwsRoleArn,
+ addedLocation.StorageBaseUrl,
+ )
+ if addedLocation.Encryption != nil {
+ encryptionRequest := sdk.NewExternalVolumeS3EncryptionRequest(addedLocation.Encryption.Type)
+ if addedLocation.Encryption.KmsKeyId != nil {
+ encryptionRequest = encryptionRequest.WithKmsKeyId(*addedLocation.Encryption.KmsKeyId)
+ }
+
+ s3ParamsRequest = s3ParamsRequest.WithEncryption(*encryptionRequest)
+ }
+
+ newStorageLocationreq = sdk.NewExternalVolumeStorageLocationRequest().WithS3StorageLocationParams(*s3ParamsRequest)
+ case sdk.StorageProviderGCS:
+ addedLocation := addedLocation.GCSStorageLocationParams
+ gcsParamsRequest := sdk.NewGCSStorageLocationParamsRequest(
+ addedLocation.Name,
+ addedLocation.StorageBaseUrl,
+ )
+
+ if addedLocation.Encryption != nil {
+ encryptionRequest := sdk.NewExternalVolumeGCSEncryptionRequest(addedLocation.Encryption.Type)
+ if addedLocation.Encryption.KmsKeyId != nil {
+ encryptionRequest = encryptionRequest.WithKmsKeyId(*addedLocation.Encryption.KmsKeyId)
+ }
+
+ gcsParamsRequest = gcsParamsRequest.WithEncryption(*encryptionRequest)
+ }
+
+ newStorageLocationreq = sdk.NewExternalVolumeStorageLocationRequest().WithGCSStorageLocationParams(*gcsParamsRequest)
+ case sdk.StorageProviderAzure:
+ addedLocation := addedLocation.AzureStorageLocationParams
+ azureParamsRequest := sdk.NewAzureStorageLocationParamsRequest(
+ addedLocation.Name,
+ addedLocation.AzureTenantId,
+ addedLocation.StorageBaseUrl,
+ )
+ newStorageLocationreq = sdk.NewExternalVolumeStorageLocationRequest().WithAzureStorageLocationParams(*azureParamsRequest)
+ }
+
+ return client.ExternalVolumes.Alter(ctx, sdk.NewAlterExternalVolumeRequest(id).WithAddStorageLocation(*newStorageLocationreq))
+}
+
+func removeStorageLocation(
+ removedLocation sdk.ExternalVolumeStorageLocation,
+ client *sdk.Client,
+ ctx context.Context,
+ id sdk.AccountObjectIdentifier,
+) error {
+ removedName, err := sdk.GetStorageLocationName(removedLocation)
+ if err != nil {
+ return err
+ }
+
+ return client.ExternalVolumes.Alter(ctx, sdk.NewAlterExternalVolumeRequest(id).WithRemoveStorageLocation(removedName))
+}
+
+// Process the removal / addition storage location requests.
+// to avoid creating storage locations with duplicate names.
+// len(removedLocations) should be less than the total number
+// of storage locations the external volume has, else this function will fail.
+func updateStorageLocations(
+ removedLocations []sdk.ExternalVolumeStorageLocation,
+ addedLocations []sdk.ExternalVolumeStorageLocation,
+ client *sdk.Client,
+ ctx context.Context,
+ id sdk.AccountObjectIdentifier,
+) error {
+ for _, removedLocation := range removedLocations {
+ err := removeStorageLocation(removedLocation, client, ctx, id)
+ if err != nil {
+ return err
+ }
+ }
+ for _, addedLocation := range addedLocations {
+ err := addStorageLocation(addedLocation, client, ctx, id)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/resources/external_volume_acceptance_test.go b/pkg/resources/external_volume_acceptance_test.go
new file mode 100644
index 0000000000..0efb047ab3
--- /dev/null
+++ b/pkg/resources/external_volume_acceptance_test.go
@@ -0,0 +1,2558 @@
+package resources_test
+
+// TODO Add test that includes Iceberg table creation, as this impacts the describe output (updates ACTIVE)
+// TODO Add S3Gov tests
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
+
+ acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources"
+ r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-testing/config"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+)
+
+// Note that generators currently don't handle lists of objects, which is required for storage locations
+// Using the old approach of files for this reason
+
+func getS3StorageLocation(
+ locName string,
+ provider string,
+ baseUrl string,
+ roleArn string,
+ encryptionType string,
+ s3EncryptionKmsKeyId string,
+) config.Variable {
+ if encryptionType == "AWS_SSE_KMS" {
+ return config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(locName),
+ "storage_provider": config.StringVariable(provider),
+ "storage_base_url": config.StringVariable(baseUrl),
+ "storage_aws_role_arn": config.StringVariable(roleArn),
+ "encryption_type": config.StringVariable(encryptionType),
+ "encryption_kms_key_id": config.StringVariable(s3EncryptionKmsKeyId),
+ })
+ } else {
+ return config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(locName),
+ "storage_provider": config.StringVariable(provider),
+ "storage_base_url": config.StringVariable(baseUrl),
+ "storage_aws_role_arn": config.StringVariable(roleArn),
+ "encryption_type": config.StringVariable(encryptionType),
+ })
+ }
+}
+
+func getGcsStorageLocation(
+ locName string,
+ baseUrl string,
+ encryptionType string,
+ gcsEncryptionKmsKeyId string,
+) config.Variable {
+ gcsStorageProvider := "GCS"
+ if encryptionType == "GCS_SSE_KMS" {
+ return config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(locName),
+ "storage_provider": config.StringVariable(gcsStorageProvider),
+ "storage_base_url": config.StringVariable(baseUrl),
+ "encryption_type": config.StringVariable(encryptionType),
+ "encryption_kms_key_id": config.StringVariable(gcsEncryptionKmsKeyId),
+ })
+ } else {
+ return config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(locName),
+ "storage_provider": config.StringVariable(gcsStorageProvider),
+ "storage_base_url": config.StringVariable(baseUrl),
+ "encryption_type": config.StringVariable(encryptionType),
+ })
+ }
+}
+
+func getAzureStorageLocation(
+ locName string,
+ baseUrl string,
+ azureTenantId string,
+) config.Variable {
+ azureStorageProvider := "AZURE"
+ return config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(locName),
+ "storage_provider": config.StringVariable(azureStorageProvider),
+ "storage_base_url": config.StringVariable(baseUrl),
+ "azure_tenant_id": config.StringVariable(azureTenantId),
+ })
+}
+
+func externalVolume(storageLocations config.Variable, name string, comment string, allowWrites string) config.Variables {
+ return config.Variables{
+ "name": config.StringVariable(name),
+ "comment": config.StringVariable(comment),
+ "allow_writes": config.StringVariable(allowWrites),
+ "storage_location": storageLocations,
+ }
+}
+
+func externalVolumeMultiple(s3StorageLocations config.Variable, gcsStorageLocations config.Variable, azureStorageLocations config.Variable, name string, comment string, allowWrites string) config.Variables {
+ return config.Variables{
+ "name": config.StringVariable(name),
+ "comment": config.StringVariable(comment),
+ "allow_writes": config.StringVariable(allowWrites),
+ "s3_storage_locations": s3StorageLocations,
+ "gcs_storage_locations": gcsStorageLocations,
+ "azure_storage_locations": azureStorageLocations,
+ }
+}
+
+// Test volume with s3 storage locations
+func TestAcc_External_Volume_S3(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ resourceId := helpers.EncodeResourceIdentifier(id)
+ comment := random.Comment()
+ comment2 := random.Comment()
+ allowWritesTrue := "true"
+ allowWritesFalse := "false"
+ s3StorageLocationName := "s3Test"
+ s3StorageLocationName2 := "s3Test2"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionTypeNone := "NONE"
+ s3EncryptionTypeSseS3 := "AWS_SSE_S3"
+ s3EncryptionTypeSseKms := "AWS_SSE_KMS"
+ s3EncryptionKmsKeyId := "123456789"
+ s3EncryptionKmsKeyId2 := "987654321"
+ s3StorageLocation := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationUpdatedName := getS3StorageLocation(s3StorageLocationName2, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationSseEncryption := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeSseS3, "")
+ s3StorageLocationKmsEncryption := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeSseKms, s3EncryptionKmsKeyId)
+ s3StorageLocationKmsEncryptionUpdatedName := getS3StorageLocation(s3StorageLocationName2, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeSseKms, s3EncryptionKmsKeyId)
+ s3StorageLocationKmsEncryptionUpdatedKey := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeSseKms, s3EncryptionKmsKeyId2)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ // without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ // import - without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, "", ""),
+ ResourceName: "snowflake_external_volume.test",
+ ImportStateCheck: assert.AssertThatImport(t,
+ resourceassert.ImportedExternalVolumeResource(t, resourceId).
+ HasNameString(externalVolumeName).
+ HasStorageLocationLength(1),
+ ),
+ },
+ // set external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // import - with external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // add second storage location without s3 optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // import - 2 storage locations without all s3 optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // update comment and change back to 1 storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment2, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // update allowWrites
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // add none encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // add AWS_SSE_S3 encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationSseEncryption), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseS3,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // add AWS_SSE_KMS encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationKmsEncryption), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // update kms key
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationKmsEncryptionUpdatedKey), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId2,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // import - all optional s3 storage location optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationKmsEncryption), externalVolumeName, comment2, allowWritesFalse),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // add second storage location with all s3 optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationKmsEncryptionUpdatedKey, s3StorageLocationKmsEncryptionUpdatedName), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId2,
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // import - 2 s3 storage locations, with all s3 optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationKmsEncryptionUpdatedKey, s3StorageLocationKmsEncryptionUpdatedName), externalVolumeName, comment2, allowWritesFalse),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // change back to AWS_SSE_S3 encryption with 1 s3 storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationSseEncryption), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseS3,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // change back to none encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // change back to no encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // remove allow writes and comment from config
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ },
+ })
+}
+
+// Test volume with gcs storage locations
+func TestAcc_External_Volume_GCS(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ resourceId := helpers.EncodeResourceIdentifier(id)
+ comment := random.Comment()
+ comment2 := random.Comment()
+ allowWritesTrue := "true"
+ allowWritesFalse := "false"
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageLocationName2 := "gcsTest2"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionTypeNone := "NONE"
+ gcsEncryptionTypeSseKms := "GCS_SSE_KMS"
+ gcsEncryptionKmsKeyId := "123456789"
+ gcsEncryptionKmsKeyId2 := "987654321"
+ gcsStorageLocation := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl, gcsEncryptionTypeNone, "")
+ gcsStorageLocationUpdatedName := getGcsStorageLocation(gcsStorageLocationName2, gcsStorageBaseUrl, gcsEncryptionTypeNone, "")
+ gcsStorageLocationKmsEncryption := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl, gcsEncryptionTypeSseKms, gcsEncryptionKmsKeyId)
+ gcsStorageLocationKmsEncryptionUpdatedName := getGcsStorageLocation(gcsStorageLocationName2, gcsStorageBaseUrl, gcsEncryptionTypeSseKms, gcsEncryptionKmsKeyId)
+ gcsStorageLocationKmsEncryptionUpdatedKey := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl, gcsEncryptionTypeSseKms, gcsEncryptionKmsKeyId2)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ // without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ // import - without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, "", ""),
+ ResourceName: "snowflake_external_volume.test",
+ ImportStateCheck: assert.AssertThatImport(t,
+ resourceassert.ImportedExternalVolumeResource(t, resourceId).
+ HasNameString(externalVolumeName).
+ HasStorageLocationLength(1),
+ ),
+ },
+ // set external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // import - with external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // add second storage location without gcs optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation, gcsStorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName2,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // import - 2 storage locations without all gcs optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation, gcsStorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // update comment and change back to 1 storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment2, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // update allowWrites
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // add none encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // add GCS_SSE_KMS encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationKmsEncryption), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // update kms key
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationKmsEncryptionUpdatedKey), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId2,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // import - all gcs storage location optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationKmsEncryption), externalVolumeName, comment2, allowWritesFalse),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // add second storage location with all gcs optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationKmsEncryptionUpdatedKey, gcsStorageLocationKmsEncryptionUpdatedName), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId2,
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName2,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId,
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // import - 2 gcs storage locations, with all gcs optionals set
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationKmsEncryptionUpdatedKey, gcsStorageLocationKmsEncryptionUpdatedName), externalVolumeName, comment2, allowWritesFalse),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // change back to none encryption with 1 gcs storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // change back to no encryption
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // remove allow writes and comment from config
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ },
+ })
+}
+
+// Test volume with azure storage locations
+func TestAcc_External_Volume_Azure(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ resourceId := helpers.EncodeResourceIdentifier(id)
+ comment := random.Comment()
+ comment2 := random.Comment()
+ allowWritesTrue := "true"
+ allowWritesFalse := "false"
+ azureStorageLocationName := "azureTest"
+ azureStorageLocationName2 := "azureTest2"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+ azureEncryptionTypeNone := "NONE"
+ azureStorageLocation := getAzureStorageLocation(azureStorageLocationName, azureStorageBaseUrl, azureTenantId)
+ azureStorageLocationUpdatedName := getAzureStorageLocation(azureStorageLocationName2, azureStorageBaseUrl, azureTenantId)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ // without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ // import - without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, "", ""),
+ ResourceName: "snowflake_external_volume.test",
+ ImportStateCheck: assert.AssertThatImport(t,
+ resourceassert.ImportedExternalVolumeResource(t, resourceId).
+ HasNameString(externalVolumeName).
+ HasStorageLocationLength(1),
+ ),
+ },
+ // set external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // import - with external volume optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // add second storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation, azureStorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ azureStorageLocationName2,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // import - 2 storage locations
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // update comment and change back to 1 storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, comment2, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // update allowWrites
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, comment2, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment2).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment2).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "4")),
+ ),
+ },
+ // remove allow writes and comment from config
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocation), externalVolumeName, "", ""),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString("").
+ HasAllowWritesString(r.BooleanDefault).
+ HasStorageLocationLength(1).
+ HasStorageLocationAtIndex(
+ 0,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment("").
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "3")),
+ ),
+ },
+ },
+ })
+}
+
+// Test apply works when setting all optionals from the start
+// Other tests start without setting all optionals
+func TestAcc_External_Volume_All_Options(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ comment := random.Comment()
+ allowWritesFalse := "false"
+
+ s3StorageLocationName := "s3Test"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionTypeSseKms := "AWS_SSE_KMS"
+ s3EncryptionKmsKeyId := "123456789"
+ s3StorageLocationKmsEncryption := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeSseKms, s3EncryptionKmsKeyId)
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionTypeSseKms := "GCS_SSE_KMS"
+ gcsEncryptionKmsKeyId := "123456789"
+ gcsStorageLocationKmsEncryption := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl, gcsEncryptionTypeSseKms, gcsEncryptionKmsKeyId)
+
+ azureStorageLocationName := "azureTest"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+ azureEncryptionTypeNone := "NONE"
+ azureStorageLocation := getAzureStorageLocation(azureStorageLocationName, azureStorageBaseUrl, azureTenantId)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocationKmsEncryption), config.ListVariable(gcsStorageLocationKmsEncryption), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeSseKms,
+ s3EncryptionKmsKeyId,
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeSseKms,
+ gcsEncryptionKmsKeyId,
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocationKmsEncryption), config.ListVariable(gcsStorageLocationKmsEncryption), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesFalse),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+// Test volume with multiple storage locations that span multiple providers
+// Test adding/removing storage locations at different positions in the storage_location list
+func TestAcc_External_Volume_Multiple(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ comment := random.Comment()
+ allowWritesTrue := "true"
+ s3StorageLocationName := "s3Test"
+ s3StorageLocationName2 := "s3Test2"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageBaseUrl2 := "s3://my_example_bucket2"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionTypeNone := "NONE"
+ s3StorageLocation := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationUpdatedBaseUrl := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl2, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationUpdatedName := getS3StorageLocation(s3StorageLocationName2, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageLocationName2 := "gcsTest2"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsStorageBaseUrl2 := "gcs://my_example_bucket2"
+ gcsEncryptionTypeNone := "NONE"
+ gcsStorageLocation := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl, gcsEncryptionTypeNone, "")
+ gcsStorageLocationUpdatedName := getGcsStorageLocation(gcsStorageLocationName2, gcsStorageBaseUrl, gcsEncryptionTypeNone, "")
+ gcsStorageLocationUpdatedBaseUrl := getGcsStorageLocation(gcsStorageLocationName, gcsStorageBaseUrl2, gcsEncryptionTypeNone, "")
+
+ azureStorageLocationName := "azureTest"
+ azureStorageLocationName2 := "azureTest2"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+ azureTenantId2 := "987654321"
+ azureEncryptionTypeNone := "NONE"
+ azureStorageLocation := getAzureStorageLocation(azureStorageLocationName, azureStorageBaseUrl, azureTenantId)
+ azureStorageLocationUpdatedTenantId := getAzureStorageLocation(azureStorageLocationName, azureStorageBaseUrl, azureTenantId2)
+ azureStorageLocationUpdatedName := getAzureStorageLocation(azureStorageLocationName2, azureStorageBaseUrl, azureTenantId)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ // one location of each provider
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // import
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ ResourceName: "snowflake_external_volume.complete",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ // change the s3 base url at position 0
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocationUpdatedBaseUrl), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl2,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // change back the s3 base url at position 0
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // add new s3 storage location to position 0
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocationUpdatedName, s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(4).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 3,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "7")),
+ ),
+ },
+ // remove s3 storage location at position 0
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // change the base url of the gcs storage location at position 1
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocationUpdatedBaseUrl), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl2,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // change back the encryption type of the gcs storage location at position 1
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // add new s3 storage location to position 1
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(4).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 3,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "7")),
+ ),
+ },
+ // remove s3 storage location at position 1
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // change the tenant id of the azure storage location at position 2
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocationUpdatedTenantId), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId2,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // change back the tenant id of the azure storage location at position 2
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // add new gcs storage location to position 2
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation, gcsStorageLocationUpdatedName), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(4).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ gcsStorageLocationName2,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 3,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "7")),
+ ),
+ },
+ // remove gcs storage location at position 2
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ // add new azure storage location to position 3
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation, azureStorageLocationUpdatedName), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(4).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ).
+ HasStorageLocationAtIndex(
+ 3,
+ azureStorageLocationName2,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "7")),
+ ),
+ },
+ // remove azure storage location from position 3
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/multiple/complete"),
+ ConfigVariables: externalVolumeMultiple(config.ListVariable(s3StorageLocation), config.ListVariable(gcsStorageLocation), config.ListVariable(azureStorageLocation), externalVolumeName, comment, allowWritesTrue),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesTrue).
+ HasStorageLocationLength(3).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ gcsStorageLocationName,
+ gcsStorageProvider,
+ gcsStorageBaseUrl,
+ "",
+ gcsEncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 2,
+ azureStorageLocationName,
+ azureStorageProvider,
+ azureStorageBaseUrl,
+ "",
+ azureEncryptionTypeNone,
+ "",
+ azureTenantId,
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesTrue),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "6")),
+ ),
+ },
+ },
+ })
+}
+
+// Test that drifts are detected and fixed
+func TestAcc_External_Volume_External_Changes(t *testing.T) {
+ _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ externalVolumeName := id.Name()
+ comment := random.Comment()
+ comment2 := random.Comment()
+ allowWritesFalse := "false"
+ s3StorageLocationName := "s3Test"
+ s3StorageLocationName2 := "s3Test2"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionTypeNone := "NONE"
+ s3StorageLocation := getS3StorageLocation(s3StorageLocationName, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationUpdatedName := getS3StorageLocation(s3StorageLocationName2, s3StorageProvider, s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.ExternalVolume),
+ Steps: []resource.TestStep{
+ // create volume with 2 s3 storage locations
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // externally remove storage location
+ {
+ PreConfig: func() {
+ acc.TestClient().ExternalVolume.Alter(t, sdk.NewAlterExternalVolumeRequest(id).WithRemoveStorageLocation(s3StorageLocationName))
+ },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // externally add storage location
+ {
+ PreConfig: func() {
+ acc.TestClient().ExternalVolume.Alter(
+ t,
+ sdk.NewAlterExternalVolumeRequest(id).WithAddStorageLocation(
+ *sdk.NewExternalVolumeStorageLocationRequest().WithS3StorageLocationParams(
+ *sdk.NewS3StorageLocationParamsRequest(
+ "externally-added-s3-storage-location",
+ "s3",
+ "arn:aws:iam::123456789012:role/externally-added-role",
+ "s3://externally-added-bucket",
+ ),
+ ),
+ ),
+ )
+ },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // externally drop external volume
+ {
+ PreConfig: func() {
+ acc.TestClient().ExternalVolume.DropFunc(t, id)
+ },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // externally update comment
+ {
+ PreConfig: func() {
+ acc.TestClient().ExternalVolume.Alter(t, sdk.NewAlterExternalVolumeRequest(id).WithSet(
+ *sdk.NewAlterExternalVolumeSetRequest().WithComment(comment2),
+ ))
+ },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ // externally update allow writes
+ {
+ PreConfig: func() {
+ acc.TestClient().ExternalVolume.Alter(t, sdk.NewAlterExternalVolumeRequest(id).WithSet(
+ *sdk.NewAlterExternalVolumeSetRequest().WithAllowWrites(true),
+ ))
+ },
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/complete"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocation, s3StorageLocationUpdatedName), externalVolumeName, comment, allowWritesFalse),
+ Check: assert.AssertThat(t,
+ resourceassert.ExternalVolumeResource(t, "snowflake_external_volume.complete").
+ HasNameString(externalVolumeName).
+ HasCommentString(comment).
+ HasAllowWritesString(allowWritesFalse).
+ HasStorageLocationLength(2).
+ HasStorageLocationAtIndex(
+ 0,
+ s3StorageLocationName,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ).
+ HasStorageLocationAtIndex(
+ 1,
+ s3StorageLocationName2,
+ s3StorageProvider,
+ s3StorageBaseUrl,
+ s3StorageAwsRoleArn,
+ s3EncryptionTypeNone,
+ "",
+ "",
+ ),
+ resourceshowoutputassert.ExternalVolumeShowOutput(t, "snowflake_external_volume.complete").
+ HasName(externalVolumeName).
+ HasComment(comment).
+ HasAllowWrites(allowWritesFalse),
+ assert.Check(resource.TestCheckResourceAttr("snowflake_external_volume.complete", "describe_output.#", "5")),
+ ),
+ },
+ },
+ })
+}
+
+// Test invalid parameter combinations throw errors
+func TestAcc_External_Volume_Invalid_Cases(t *testing.T) {
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ s3StorageLocationName := "s3Test"
+ s3StorageProvider := "S3"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionTypeNone := "NONE"
+ s3EncryptionKmsKeyId := "123456789"
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageProvider := "GCS"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+
+ azureStorageLocationName := "azureTest"
+ azureStorageProvider := "AZURE"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+
+ externalVolumeName := id.Name()
+ s3StorageLocationInvalidStorageProvider := getS3StorageLocation(s3StorageLocationName, "invalid-storage-provider", s3StorageBaseUrl, s3StorageAwsRoleArn, s3EncryptionTypeNone, "")
+ s3StorageLocationNoRoleArn := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(s3StorageLocationName),
+ "storage_provider": config.StringVariable(s3StorageProvider),
+ "storage_base_url": config.StringVariable(s3StorageBaseUrl),
+ })
+ s3StorageLocationWithTenantId := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(s3StorageLocationName),
+ "storage_provider": config.StringVariable(s3StorageProvider),
+ "storage_base_url": config.StringVariable(s3StorageBaseUrl),
+ "azure_tenant_id": config.StringVariable(azureTenantId),
+ })
+ gcsStorageLocationWithAwsRoleArn := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(gcsStorageLocationName),
+ "storage_provider": config.StringVariable(gcsStorageProvider),
+ "storage_base_url": config.StringVariable(gcsStorageBaseUrl),
+ "storage_aws_role_arn": config.StringVariable(s3StorageAwsRoleArn),
+ })
+ gcsStorageLocationWithTenantId := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(gcsStorageLocationName),
+ "storage_provider": config.StringVariable(gcsStorageProvider),
+ "storage_base_url": config.StringVariable(gcsStorageBaseUrl),
+ "azure_tenant_id": config.StringVariable(azureTenantId),
+ })
+ azureStorageLocationNoTenantId := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(azureStorageLocationName),
+ "storage_provider": config.StringVariable(azureStorageProvider),
+ "storage_base_url": config.StringVariable(azureStorageBaseUrl),
+ })
+ azureStorageLocationWithKmsKeyId := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(azureStorageLocationName),
+ "storage_provider": config.StringVariable(azureStorageProvider),
+ "storage_base_url": config.StringVariable(azureStorageBaseUrl),
+ "azure_tenant_id": config.StringVariable(azureTenantId),
+ "encryption_kms_key_id": config.StringVariable(s3EncryptionKmsKeyId),
+ })
+ azureStorageLocationWithAwsRoleArn := config.MapVariable(map[string]config.Variable{
+ "storage_location_name": config.StringVariable(azureStorageLocationName),
+ "storage_provider": config.StringVariable(azureStorageProvider),
+ "storage_base_url": config.StringVariable(azureStorageBaseUrl),
+ "storage_aws_role_arn": config.StringVariable(s3StorageAwsRoleArn),
+ "azure_tenant_id": config.StringVariable(azureTenantId),
+ })
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.Warehouse),
+ Steps: []resource.TestStep{
+ // invalid storage provider test
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationInvalidStorageProvider), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("invalid storage provider: invalid-storage-provider"),
+ },
+ // no storage locations test
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("At least 1 \"storage_location\" blocks are required"),
+ },
+ // aws storage location doesn't specify storage_aws_role_arn
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationNoRoleArn), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, missing storage_aws_role_arn key in an s3 storage location"),
+ },
+ // azure storage location doesn't specify azure_tenant_id
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocationNoTenantId), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, missing azure_tenant_id provider key in an azure storage location"),
+ },
+ // azure_tenant_id specified for s3 storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(s3StorageLocationWithTenantId), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, azure_tenant_id provided for s3 storage location"),
+ },
+ // storage_aws_role_arn specified for gcs storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationWithAwsRoleArn), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, storage_aws_role_arn provided for gcs storage location"),
+ },
+ // azure_tenant_id specified for gcs storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(gcsStorageLocationWithTenantId), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, azure_tenant_id provided for gcs storage location"),
+ },
+ // storage_aws_role_arn specified for azure storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocationWithAwsRoleArn), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, storage_aws_role_arn provided for azure storage location"),
+ },
+ // encryption_kms_key_id specified for azure storage location
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalVolume/single/basic"),
+ ConfigVariables: externalVolume(config.ListVariable(azureStorageLocationWithKmsKeyId), externalVolumeName, "", ""),
+ ExpectError: regexp.MustCompile("unable to extract storage location, encryption_kms_key_id provided for azure storage location"),
+ },
+ // TODO add test for encryption_type specified for azure storage location
+ },
+ })
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/test.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/test.tf
new file mode 100644
index 0000000000..a78b12d106
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/test.tf
@@ -0,0 +1,35 @@
+resource "snowflake_external_volume" "complete" {
+ name = var.name
+ comment = var.comment
+ allow_writes = var.allow_writes
+ dynamic "storage_location" {
+ for_each = var.s3_storage_locations
+ content {
+ storage_location_name = storage_location.value["storage_location_name"]
+ storage_provider = storage_location.value["storage_provider"]
+ storage_base_url = storage_location.value["storage_base_url"]
+ storage_aws_role_arn = storage_location.value["storage_aws_role_arn"]
+ encryption_type = try(storage_location.value["encryption_type"], null)
+ encryption_kms_key_id = try(storage_location.value["encryption_kms_key_id"], null)
+ }
+ }
+ dynamic "storage_location" {
+ for_each = var.gcs_storage_locations
+ content {
+ storage_location_name = storage_location.value["storage_location_name"]
+ storage_provider = storage_location.value["storage_provider"]
+ storage_base_url = storage_location.value["storage_base_url"]
+ encryption_type = try(storage_location.value["encryption_type"], null)
+ encryption_kms_key_id = try(storage_location.value["encryption_kms_key_id"], null)
+ }
+ }
+ dynamic "storage_location" {
+ for_each = var.azure_storage_locations
+ content {
+ storage_location_name = storage_location.value["storage_location_name"]
+ storage_provider = storage_location.value["storage_provider"]
+ storage_base_url = storage_location.value["storage_base_url"]
+ azure_tenant_id = storage_location.value["azure_tenant_id"]
+ }
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/variables.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/variables.tf
new file mode 100644
index 0000000000..de24426a29
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/multiple/complete/variables.tf
@@ -0,0 +1,18 @@
+variable "name" {
+ type = string
+}
+variable "comment" {
+ type = string
+}
+variable "allow_writes" {
+ type = string
+}
+variable "s3_storage_locations" {
+ type = list(map(string))
+}
+variable "gcs_storage_locations" {
+ type = list(map(string))
+}
+variable "azure_storage_locations" {
+ type = list(map(string))
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/test.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/test.tf
new file mode 100644
index 0000000000..06731ac0f6
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/test.tf
@@ -0,0 +1,15 @@
+resource "snowflake_external_volume" "complete" {
+ name = var.name
+ dynamic "storage_location" {
+ for_each = var.storage_location
+ content {
+ storage_location_name = storage_location.value["storage_location_name"]
+ storage_provider = storage_location.value["storage_provider"]
+ storage_base_url = storage_location.value["storage_base_url"]
+ storage_aws_role_arn = try(storage_location.value["storage_aws_role_arn"], null)
+ encryption_type = try(storage_location.value["encryption_type"], null)
+ encryption_kms_key_id = try(storage_location.value["encryption_kms_key_id"], null)
+ azure_tenant_id = try(storage_location.value["azure_tenant_id"], null)
+ }
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/variables.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/variables.tf
new file mode 100644
index 0000000000..a704fdd13a
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/single/basic/variables.tf
@@ -0,0 +1,6 @@
+variable "name" {
+ type = string
+}
+variable "storage_location" {
+ type = list(map(string))
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/test.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/test.tf
new file mode 100644
index 0000000000..37a168197b
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/test.tf
@@ -0,0 +1,17 @@
+resource "snowflake_external_volume" "complete" {
+ name = var.name
+ comment = var.comment
+ allow_writes = var.allow_writes
+ dynamic "storage_location" {
+ for_each = var.storage_location
+ content {
+ storage_location_name = storage_location.value["storage_location_name"]
+ storage_provider = storage_location.value["storage_provider"]
+ storage_base_url = storage_location.value["storage_base_url"]
+ storage_aws_role_arn = try(storage_location.value["storage_aws_role_arn"], null)
+ encryption_type = try(storage_location.value["encryption_type"], null)
+ encryption_kms_key_id = try(storage_location.value["encryption_kms_key_id"], null)
+ azure_tenant_id = try(storage_location.value["azure_tenant_id"], null)
+ }
+ }
+}
diff --git a/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/variables.tf b/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/variables.tf
new file mode 100644
index 0000000000..e3491d338e
--- /dev/null
+++ b/pkg/resources/testdata/TestAcc_ExternalVolume/single/complete/variables.tf
@@ -0,0 +1,12 @@
+variable "name" {
+ type = string
+}
+variable "comment" {
+ type = string
+}
+variable "allow_writes" {
+ type = string
+}
+variable "storage_location" {
+ type = list(map(string))
+}
diff --git a/pkg/schemas/external_volume.go b/pkg/schemas/external_volume.go
new file mode 100644
index 0000000000..0a6d9184b1
--- /dev/null
+++ b/pkg/schemas/external_volume.go
@@ -0,0 +1,47 @@
+package schemas
+
+import (
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var DescribeExternalVolumeSchema = map[string]*schema.Schema{
+ "parent": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "type": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "value": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "default": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+}
+
+var _ = ShowExternalVolumeSchema
+
+func ExternalVolumeDescriptionToSchema(description []sdk.ExternalVolumeProperty) []map[string]any {
+ result := make([]map[string]any, len(description))
+ for i, row := range description {
+ result[i] = map[string]any{
+ "parent": row.Parent,
+ "name": row.Name,
+ "type": row.Type,
+ "value": row.Value,
+ "default": row.Default,
+ }
+ }
+ return result
+}
+
+var _ = ExternalVolumeDescriptionToSchema
diff --git a/pkg/schemas/external_volume_gen.go b/pkg/schemas/external_volume_gen.go
new file mode 100644
index 0000000000..1a9f112980
--- /dev/null
+++ b/pkg/schemas/external_volume_gen.go
@@ -0,0 +1,36 @@
+// Code generated by sdk-to-schema generator; DO NOT EDIT.
+
+package schemas
+
+import (
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+// ShowExternalVolumeSchema represents output of SHOW query for the single ExternalVolume.
+var ShowExternalVolumeSchema = map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "allow_writes": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "comment": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+}
+
+var _ = ShowExternalVolumeSchema
+
+func ExternalVolumeToSchema(externalVolume *sdk.ExternalVolume) map[string]any {
+ externalVolumeSchema := make(map[string]any)
+ externalVolumeSchema["name"] = externalVolume.Name
+ externalVolumeSchema["allow_writes"] = externalVolume.AllowWrites
+ externalVolumeSchema["comment"] = externalVolume.Comment
+ return externalVolumeSchema
+}
+
+var _ = ExternalVolumeToSchema
diff --git a/pkg/schemas/gen/sdk_show_result_structs.go b/pkg/schemas/gen/sdk_show_result_structs.go
index 337367a112..52aaeaa36d 100644
--- a/pkg/schemas/gen/sdk_show_result_structs.go
+++ b/pkg/schemas/gen/sdk_show_result_structs.go
@@ -15,6 +15,7 @@ var SdkShowResultStructs = []any{
sdk.EventTable{},
sdk.ExternalFunction{},
sdk.ExternalTable{},
+ sdk.ExternalVolume{},
sdk.FailoverGroup{},
sdk.FileFormat{},
sdk.Function{},
diff --git a/pkg/sdk/external_volumes_def.go b/pkg/sdk/external_volumes_def.go
index fb4f610be9..ac6fb0124f 100644
--- a/pkg/sdk/external_volumes_def.go
+++ b/pkg/sdk/external_volumes_def.go
@@ -1,12 +1,18 @@
package sdk
-import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator"
+import (
+ "fmt"
+ "strings"
+
+ g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator"
+)
//go:generate go run ./poc/main.go
type (
- S3EncryptionType string
+ StorageProvider string
S3StorageProvider string
+ S3EncryptionType string
GCSEncryptionType string
)
@@ -18,8 +24,69 @@ var (
GCSEncryptionTypeNone GCSEncryptionType = "NONE"
S3StorageProviderS3 S3StorageProvider = "S3"
S3StorageProviderS3GOV S3StorageProvider = "S3GOV"
+ StorageProviderGCS StorageProvider = "GCS"
+ StorageProviderAzure StorageProvider = "AZURE"
+ StorageProviderS3 StorageProvider = "S3"
+ StorageProviderS3GOV StorageProvider = "S3GOV"
)
+var AllStorageProviderValues = []StorageProvider{
+ StorageProviderGCS,
+ StorageProviderAzure,
+ StorageProviderS3,
+ StorageProviderS3GOV,
+}
+
+func ToS3EncryptionType(s string) (S3EncryptionType, error) {
+ switch strings.ToUpper(s) {
+ case string(S3EncryptionTypeSseS3):
+ return S3EncryptionTypeSseS3, nil
+ case string(S3EncryptionTypeSseKms):
+ return S3EncryptionTypeSseKms, nil
+ case string(S3EncryptionNone):
+ return S3EncryptionNone, nil
+ default:
+ return "", fmt.Errorf("invalid s3 encryption type: %s", s)
+ }
+}
+
+func ToGCSEncryptionType(s string) (GCSEncryptionType, error) {
+ switch strings.ToUpper(s) {
+ case string(GCSEncryptionTypeSseKms):
+ return GCSEncryptionTypeSseKms, nil
+ case string(GCSEncryptionTypeNone):
+ return GCSEncryptionTypeNone, nil
+ default:
+ return "", fmt.Errorf("invalid gcs encryption type: %s", s)
+ }
+}
+
+func ToStorageProvider(s string) (StorageProvider, error) {
+ switch strings.ToUpper(s) {
+ case string(StorageProviderGCS):
+ return StorageProviderGCS, nil
+ case string(StorageProviderAzure):
+ return StorageProviderAzure, nil
+ case string(StorageProviderS3):
+ return StorageProviderS3, nil
+ case string(StorageProviderS3GOV):
+ return StorageProviderS3GOV, nil
+ default:
+ return "", fmt.Errorf("invalid storage provider: %s", s)
+ }
+}
+
+func ToS3StorageProvider(s string) (S3StorageProvider, error) {
+ switch strings.ToUpper(s) {
+ case string(S3StorageProviderS3):
+ return S3StorageProviderS3, nil
+ case string(S3StorageProviderS3GOV):
+ return S3StorageProviderS3GOV, nil
+ default:
+ return "", fmt.Errorf("invalid s3 storage provider: %s", s)
+ }
+}
+
var externalS3StorageLocationDef = g.NewQueryStruct("S3StorageLocationParams").
TextAssignment("NAME", g.ParameterOptions().SingleQuotes().Required()).
Assignment("STORAGE_PROVIDER", g.KindOfT[S3StorageProvider](), g.ParameterOptions().SingleQuotes().Required()).
@@ -36,7 +103,7 @@ var externalS3StorageLocationDef = g.NewQueryStruct("S3StorageLocationParams").
var externalGCSStorageLocationDef = g.NewQueryStruct("GCSStorageLocationParams").
TextAssignment("NAME", g.ParameterOptions().SingleQuotes().Required()).
- PredefinedQueryStructField("StorageProviderGcs", "string", g.StaticOptions().SQL("STORAGE_PROVIDER = 'GCS'")).
+ PredefinedQueryStructField("StorageProviderGcs", "string", g.StaticOptions().SQL(fmt.Sprintf("STORAGE_PROVIDER = '%s'", StorageProviderGCS))).
TextAssignment("STORAGE_BASE_URL", g.ParameterOptions().SingleQuotes().Required()).
OptionalQueryStructField(
"Encryption",
@@ -48,7 +115,7 @@ var externalGCSStorageLocationDef = g.NewQueryStruct("GCSStorageLocationParams")
var externalAzureStorageLocationDef = g.NewQueryStruct("AzureStorageLocationParams").
TextAssignment("NAME", g.ParameterOptions().SingleQuotes().Required()).
- PredefinedQueryStructField("StorageProviderAzure", "string", g.StaticOptions().SQL("STORAGE_PROVIDER = 'AZURE'")).
+ PredefinedQueryStructField("StorageProviderAzure", "string", g.StaticOptions().SQL(fmt.Sprintf("STORAGE_PROVIDER = '%s'", StorageProviderAzure))).
TextAssignment("AZURE_TENANT_ID", g.ParameterOptions().SingleQuotes().Required()).
TextAssignment("STORAGE_BASE_URL", g.ParameterOptions().SingleQuotes().Required())
@@ -148,11 +215,11 @@ var ExternalVolumesDef = g.NewInterface(
"https://docs.snowflake.com/en/sql-reference/sql/show-external-volumes",
g.DbStruct("externalVolumeShowRow").
Text("name").
- Text("allow_writes").
- Text("comment"),
+ Bool("allow_writes").
+ OptionalText("comment"),
g.PlainStruct("ExternalVolume").
Text("Name").
- Text("AllowWrites").
+ Bool("AllowWrites").
Text("Comment"),
g.NewQueryStruct("ShowExternalVolumes").
Show().
diff --git a/pkg/sdk/external_volumes_gen.go b/pkg/sdk/external_volumes_gen.go
index 726e3ab819..6be0c19f03 100644
--- a/pkg/sdk/external_volumes_gen.go
+++ b/pkg/sdk/external_volumes_gen.go
@@ -1,6 +1,11 @@
package sdk
-import "context"
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "reflect"
+)
type ExternalVolumes interface {
Create(ctx context.Context, request *CreateExternalVolumeRequest) error
@@ -107,12 +112,117 @@ type ShowExternalVolumeOptions struct {
Like *Like `ddl:"keyword" sql:"LIKE"`
}
type externalVolumeShowRow struct {
- Name string `db:"name"`
- AllowWrites string `db:"allow_writes"`
- Comment string `db:"comment"`
+ Name string `db:"name"`
+ AllowWrites bool `db:"allow_writes"`
+ Comment sql.NullString `db:"comment"`
}
type ExternalVolume struct {
Name string
- AllowWrites string
+ AllowWrites bool
Comment string
}
+
+// Returns a copy of the given storage location with a set name
+func CopySentinelStorageLocation(
+ storageLocation ExternalVolumeStorageLocation,
+) (ExternalVolumeStorageLocation, error) {
+ storageProvider, err := GetStorageLocationStorageProvider(storageLocation)
+ if err != nil {
+ return ExternalVolumeStorageLocation{}, err
+ }
+
+ newName := "terraform_provider_sentinel_storage_location"
+ var tempNameStorageLocation ExternalVolumeStorageLocation
+ switch storageProvider {
+ case StorageProviderS3, StorageProviderS3GOV:
+ tempNameStorageLocation = ExternalVolumeStorageLocation{
+ S3StorageLocationParams: &S3StorageLocationParams{
+ Name: newName,
+ StorageProvider: storageLocation.S3StorageLocationParams.StorageProvider,
+ StorageBaseUrl: storageLocation.S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: storageLocation.S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: storageLocation.S3StorageLocationParams.StorageAwsExternalId,
+ Encryption: storageLocation.S3StorageLocationParams.Encryption,
+ },
+ }
+ case StorageProviderGCS:
+ tempNameStorageLocation = ExternalVolumeStorageLocation{
+ GCSStorageLocationParams: &GCSStorageLocationParams{
+ Name: newName,
+ StorageBaseUrl: storageLocation.GCSStorageLocationParams.StorageBaseUrl,
+ Encryption: storageLocation.GCSStorageLocationParams.Encryption,
+ },
+ }
+ case StorageProviderAzure:
+ tempNameStorageLocation = ExternalVolumeStorageLocation{
+ AzureStorageLocationParams: &AzureStorageLocationParams{
+ Name: newName,
+ StorageBaseUrl: storageLocation.AzureStorageLocationParams.StorageBaseUrl,
+ AzureTenantId: storageLocation.AzureStorageLocationParams.AzureTenantId,
+ },
+ }
+ }
+
+ return tempNameStorageLocation, nil
+}
+
+func GetStorageLocationName(s ExternalVolumeStorageLocation) (string, error) {
+ if s.S3StorageLocationParams != nil && (*s.S3StorageLocationParams != S3StorageLocationParams{}) {
+ if len(s.S3StorageLocationParams.Name) == 0 {
+ return "", fmt.Errorf("Invalid S3 storage location - no name set")
+ }
+
+ return s.S3StorageLocationParams.Name, nil
+ } else if s.GCSStorageLocationParams != nil && (*s.GCSStorageLocationParams != GCSStorageLocationParams{}) {
+ if len(s.GCSStorageLocationParams.Name) == 0 {
+ return "", fmt.Errorf("Invalid GCS storage location - no name set")
+ }
+
+ return s.GCSStorageLocationParams.Name, nil
+ } else if s.AzureStorageLocationParams != nil && (*s.AzureStorageLocationParams != AzureStorageLocationParams{}) {
+ if len(s.AzureStorageLocationParams.Name) == 0 {
+ return "", fmt.Errorf("Invalid Azure storage location - no name set")
+ }
+
+ return s.AzureStorageLocationParams.Name, nil
+ } else {
+ return "", fmt.Errorf("Invalid storage location")
+ }
+}
+
+func GetStorageLocationStorageProvider(s ExternalVolumeStorageLocation) (StorageProvider, error) {
+ if s.S3StorageLocationParams != nil && (*s.S3StorageLocationParams != S3StorageLocationParams{}) {
+ return ToStorageProvider(string(s.S3StorageLocationParams.StorageProvider))
+ } else if s.GCSStorageLocationParams != nil && (*s.GCSStorageLocationParams != GCSStorageLocationParams{}) {
+ return StorageProviderGCS, nil
+ } else if s.AzureStorageLocationParams != nil && (*s.AzureStorageLocationParams != AzureStorageLocationParams{}) {
+ return StorageProviderAzure, nil
+ } else {
+ return "", fmt.Errorf("Invalid storage location")
+ }
+}
+
+// Returns the index of the last matching elements in the list
+// e.g. [1,2,3] [1,3,2] -> 0, [1,2,3] [1,2,4] -> 1
+// -1 is returned if there are no common prefixes in the list
+func CommonPrefixLastIndex(a []ExternalVolumeStorageLocation, b []ExternalVolumeStorageLocation) (int, error) {
+ commonPrefixLastIndex := 0
+
+ if len(a) == 0 || len(b) == 0 {
+ return -1, nil
+ }
+
+ if !reflect.DeepEqual(a[0], b[0]) {
+ return -1, nil
+ }
+
+ for i := 1; i < min(len(a), len(b)); i++ {
+ if !reflect.DeepEqual(a[i], b[i]) {
+ break
+ }
+
+ commonPrefixLastIndex = i
+ }
+
+ return commonPrefixLastIndex, nil
+}
diff --git a/pkg/sdk/external_volumes_gen_test.go b/pkg/sdk/external_volumes_gen_test.go
index 4278e47db6..96d52c02ac 100644
--- a/pkg/sdk/external_volumes_gen_test.go
+++ b/pkg/sdk/external_volumes_gen_test.go
@@ -1,6 +1,11 @@
package sdk
-import "testing"
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
// Storage location structs for testing
var s3StorageLocationParams = &S3StorageLocationParams{
@@ -439,3 +444,736 @@ func TestExternalVolumes_Show(t *testing.T) {
assertOptsValidAndSQLEquals(t, opts, "SHOW EXTERNAL VOLUMES LIKE '%s'", id.Name())
})
}
+
+func Test_ExternalVolumes_ToS3EncryptionType(t *testing.T) {
+ type test struct {
+ input string
+ want S3EncryptionType
+ }
+
+ valid := []test{
+ {input: "aws_sse_s3", want: S3EncryptionTypeSseS3},
+ {input: "AWS_SSE_S3", want: S3EncryptionTypeSseS3},
+ {input: "AWS_SSE_KMS", want: S3EncryptionTypeSseKms},
+ {input: "NONE", want: S3EncryptionNone},
+ }
+
+ invalid := []test{
+ {input: ""},
+ {input: "foo"},
+ }
+
+ for _, tc := range valid {
+ t.Run(tc.input, func(t *testing.T) {
+ got, err := ToS3EncryptionType(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 := ToS3EncryptionType(tc.input)
+ require.Error(t, err)
+ })
+ }
+}
+
+func Test_ExternalVolumes_ToStorageProvider(t *testing.T) {
+ type test struct {
+ input string
+ want StorageProvider
+ }
+
+ valid := []test{
+ {input: "s3", want: StorageProviderS3},
+ {input: "S3", want: StorageProviderS3},
+ {input: "s3gov", want: StorageProviderS3GOV},
+ {input: "S3GOV", want: StorageProviderS3GOV},
+ {input: "gcs", want: StorageProviderGCS},
+ {input: "GCS", want: StorageProviderGCS},
+ {input: "azure", want: StorageProviderAzure},
+ {input: "AZURE", want: StorageProviderAzure},
+ }
+
+ invalid := []test{
+ {input: ""},
+ {input: "foo"},
+ }
+
+ for _, tc := range valid {
+ t.Run(tc.input, func(t *testing.T) {
+ got, err := ToStorageProvider(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 := ToStorageProvider(tc.input)
+ require.Error(t, err)
+ })
+ }
+}
+
+func Test_ExternalVolumes_ToS3StorageProvider(t *testing.T) {
+ type test struct {
+ input string
+ want S3StorageProvider
+ }
+
+ valid := []test{
+ {input: "s3", want: S3StorageProviderS3},
+ {input: "S3", want: S3StorageProviderS3},
+ {input: "s3gov", want: S3StorageProviderS3GOV},
+ {input: "S3GOV", want: S3StorageProviderS3GOV},
+ }
+
+ invalid := []test{
+ {input: ""},
+ {input: "foo"},
+ }
+
+ for _, tc := range valid {
+ t.Run(tc.input, func(t *testing.T) {
+ got, err := ToS3StorageProvider(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 := ToS3StorageProvider(tc.input)
+ require.Error(t, err)
+ })
+ }
+}
+
+func Test_ExternalVolumes_ToGCSEncryptionType(t *testing.T) {
+ type test struct {
+ input string
+ want GCSEncryptionType
+ }
+
+ valid := []test{
+ {input: "gcs_sse_kms", want: GCSEncryptionTypeSseKms},
+ {input: "GCS_SSE_KMS", want: GCSEncryptionTypeSseKms},
+ {input: "NONE", want: GCSEncryptionTypeNone},
+ {input: "none", want: GCSEncryptionTypeNone},
+ }
+
+ invalid := []test{
+ {input: ""},
+ {input: "foo"},
+ }
+
+ for _, tc := range valid {
+ t.Run(tc.input, func(t *testing.T) {
+ got, err := ToGCSEncryptionType(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 := ToGCSEncryptionType(tc.input)
+ require.Error(t, err)
+ })
+ }
+}
+
+// External volume helper tests
+
+func Test_GetStorageLocationName(t *testing.T) {
+ s3StorageLocationName := "s3Test"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionKmsKeyId := "123456789"
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionKmsKeyId := "123456789"
+
+ azureStorageLocationName := "azureTest"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+
+ s3StorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: &s3StorageAwsExternalId,
+ Encryption: &ExternalVolumeS3Encryption{
+ Type: S3EncryptionTypeSseKms,
+ KmsKeyId: &s3EncryptionKmsKeyId,
+ },
+ }
+
+ azureStorageLocationA := AzureStorageLocationParams{
+ Name: azureStorageLocationName,
+ StorageBaseUrl: azureStorageBaseUrl,
+ AzureTenantId: azureTenantId,
+ }
+
+ gcsStorageLocationA := GCSStorageLocationParams{
+ Name: gcsStorageLocationName,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ s3GovStorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3GOV,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ }
+
+ testCases := []struct {
+ Name string
+ StorageLocation ExternalVolumeStorageLocation
+ ExpectedName string
+ }{
+ {
+ Name: "S3 storage location name succesfully read",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &s3StorageLocationA},
+ ExpectedName: s3StorageLocationA.Name,
+ },
+ {
+ Name: "S3GOV storage location name succesfully read",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &s3GovStorageLocationA},
+ ExpectedName: s3GovStorageLocationA.Name,
+ },
+ {
+ Name: "GCS storage location name succesfully read",
+ StorageLocation: ExternalVolumeStorageLocation{GCSStorageLocationParams: &gcsStorageLocationA},
+ ExpectedName: gcsStorageLocationA.Name,
+ },
+ {
+ Name: "Azure storage location name succesfully read",
+ StorageLocation: ExternalVolumeStorageLocation{AzureStorageLocationParams: &azureStorageLocationA},
+ ExpectedName: azureStorageLocationA.Name,
+ },
+ }
+
+ invalidTestCases := []struct {
+ Name string
+ StorageLocation ExternalVolumeStorageLocation
+ }{
+ {
+ Name: "Empty S3 storage location",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &S3StorageLocationParams{}},
+ },
+ {
+ Name: "Empty GCS storage location",
+ StorageLocation: ExternalVolumeStorageLocation{GCSStorageLocationParams: &GCSStorageLocationParams{}},
+ },
+ {
+ Name: "Empty Azure storage location",
+ StorageLocation: ExternalVolumeStorageLocation{AzureStorageLocationParams: &AzureStorageLocationParams{}},
+ },
+ {
+ Name: "Empty storage location",
+ StorageLocation: ExternalVolumeStorageLocation{},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ name, err := GetStorageLocationName(tc.StorageLocation)
+ require.NoError(t, err)
+ assert.Equal(t, tc.ExpectedName, name)
+ })
+ }
+ for _, tc := range invalidTestCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ _, err := GetStorageLocationName(tc.StorageLocation)
+ require.Error(t, err)
+ })
+ }
+}
+
+func Test_GetStorageLocationStorageProvider(t *testing.T) {
+ s3StorageLocationName := "s3Test"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionKmsKeyId := "123456789"
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionKmsKeyId := "123456789"
+
+ azureStorageLocationName := "azureTest"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+
+ s3StorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: &s3StorageAwsExternalId,
+ Encryption: &ExternalVolumeS3Encryption{
+ Type: S3EncryptionTypeSseKms,
+ KmsKeyId: &s3EncryptionKmsKeyId,
+ },
+ }
+
+ azureStorageLocationA := AzureStorageLocationParams{
+ Name: azureStorageLocationName,
+ StorageBaseUrl: azureStorageBaseUrl,
+ AzureTenantId: azureTenantId,
+ }
+
+ gcsStorageLocationA := GCSStorageLocationParams{
+ Name: gcsStorageLocationName,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ s3GovStorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3GOV,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ }
+ testCases := []struct {
+ Name string
+ StorageLocation ExternalVolumeStorageLocation
+ ExpectedStorageProvider StorageProvider
+ }{
+ {
+ Name: "S3 storage provider",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &s3StorageLocationA},
+ ExpectedStorageProvider: StorageProviderS3,
+ },
+ {
+ Name: "S3GOV storage provider",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &s3GovStorageLocationA},
+ ExpectedStorageProvider: StorageProviderS3GOV,
+ },
+ {
+ Name: "GCS storage provider",
+ StorageLocation: ExternalVolumeStorageLocation{GCSStorageLocationParams: &gcsStorageLocationA},
+ ExpectedStorageProvider: StorageProviderGCS,
+ },
+ {
+ Name: "Azure storage provider",
+ StorageLocation: ExternalVolumeStorageLocation{AzureStorageLocationParams: &azureStorageLocationA},
+ ExpectedStorageProvider: StorageProviderAzure,
+ },
+ }
+
+ invalidTestCases := []struct {
+ Name string
+ StorageLocation ExternalVolumeStorageLocation
+ }{
+ {
+ Name: "Empty S3 storage location",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &S3StorageLocationParams{}},
+ },
+ {
+ Name: "Empty GCS storage location",
+ StorageLocation: ExternalVolumeStorageLocation{GCSStorageLocationParams: &GCSStorageLocationParams{}},
+ },
+ {
+ Name: "Empty Azure storage location",
+ StorageLocation: ExternalVolumeStorageLocation{AzureStorageLocationParams: &AzureStorageLocationParams{}},
+ },
+ {
+ Name: "Empty storage location",
+ StorageLocation: ExternalVolumeStorageLocation{},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ storageProvider, err := GetStorageLocationStorageProvider(tc.StorageLocation)
+ require.NoError(t, err)
+ assert.Equal(t, tc.ExpectedStorageProvider, storageProvider)
+ })
+ }
+ for _, tc := range invalidTestCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ _, err := GetStorageLocationName(tc.StorageLocation)
+ require.Error(t, err)
+ })
+ }
+}
+
+var s3StorageAwsExternalId = "1234567890"
+
+func Test_CopySentinelStorageLocation(t *testing.T) {
+ tempStorageLocationName := "terraform_provider_sentinel_storage_location"
+ s3StorageLocationName := "s3Test"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionKmsKeyId := "123456789"
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionKmsKeyId := "123456789"
+
+ azureStorageLocationName := "azureTest"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+
+ s3StorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: &s3StorageAwsExternalId,
+ Encryption: &ExternalVolumeS3Encryption{
+ Type: S3EncryptionTypeSseKms,
+ KmsKeyId: &s3EncryptionKmsKeyId,
+ },
+ }
+
+ azureStorageLocationA := AzureStorageLocationParams{
+ Name: azureStorageLocationName,
+ StorageBaseUrl: azureStorageBaseUrl,
+ AzureTenantId: azureTenantId,
+ }
+
+ gcsStorageLocationA := GCSStorageLocationParams{
+ Name: gcsStorageLocationName,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ t.Run("S3 storage location", func(t *testing.T) {
+ storageLocationInput := ExternalVolumeStorageLocation{S3StorageLocationParams: &s3StorageLocationA}
+ copiedStorageLocation, err := CopySentinelStorageLocation(storageLocationInput)
+ require.NoError(t, err)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.Name, tempStorageLocationName)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.StorageProvider, s3StorageLocationA.StorageProvider)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.StorageBaseUrl, s3StorageLocationA.StorageBaseUrl)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.StorageAwsRoleArn, s3StorageLocationA.StorageAwsRoleArn)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.StorageAwsExternalId, s3StorageLocationA.StorageAwsExternalId)
+ assert.Equal(t, copiedStorageLocation.S3StorageLocationParams.Encryption.Type, s3StorageLocationA.Encryption.Type)
+ assert.Equal(t, *copiedStorageLocation.S3StorageLocationParams.Encryption.KmsKeyId, *s3StorageLocationA.Encryption.KmsKeyId)
+ })
+
+ t.Run("GCS storage location", func(t *testing.T) {
+ storageLocationInput := ExternalVolumeStorageLocation{GCSStorageLocationParams: &gcsStorageLocationA}
+ copiedStorageLocation, err := CopySentinelStorageLocation(storageLocationInput)
+ require.NoError(t, err)
+ assert.Equal(t, copiedStorageLocation.GCSStorageLocationParams.Name, tempStorageLocationName)
+ assert.Equal(t, copiedStorageLocation.GCSStorageLocationParams.StorageBaseUrl, gcsStorageLocationA.StorageBaseUrl)
+ assert.Equal(t, copiedStorageLocation.GCSStorageLocationParams.Encryption.Type, gcsStorageLocationA.Encryption.Type)
+ assert.Equal(t, *copiedStorageLocation.GCSStorageLocationParams.Encryption.KmsKeyId, *gcsStorageLocationA.Encryption.KmsKeyId)
+ })
+
+ t.Run("Azure storage location", func(t *testing.T) {
+ storageLocationInput := ExternalVolumeStorageLocation{AzureStorageLocationParams: &azureStorageLocationA}
+ copiedStorageLocation, err := CopySentinelStorageLocation(storageLocationInput)
+ require.NoError(t, err)
+ assert.Equal(t, copiedStorageLocation.AzureStorageLocationParams.Name, tempStorageLocationName)
+ assert.Equal(t, copiedStorageLocation.AzureStorageLocationParams.StorageBaseUrl, azureStorageLocationA.StorageBaseUrl)
+ assert.Equal(t, copiedStorageLocation.AzureStorageLocationParams.AzureTenantId, azureStorageLocationA.AzureTenantId)
+ })
+
+ invalidTestCases := []struct {
+ Name string
+ StorageLocation ExternalVolumeStorageLocation
+ }{
+ {
+ Name: "Empty S3 storage location",
+ StorageLocation: ExternalVolumeStorageLocation{S3StorageLocationParams: &S3StorageLocationParams{}},
+ },
+ {
+ Name: "Empty GCS storage location",
+ StorageLocation: ExternalVolumeStorageLocation{GCSStorageLocationParams: &GCSStorageLocationParams{}},
+ },
+ {
+ Name: "Empty Azure storage location",
+ StorageLocation: ExternalVolumeStorageLocation{AzureStorageLocationParams: &AzureStorageLocationParams{}},
+ },
+ {
+ Name: "Empty storage location",
+ StorageLocation: ExternalVolumeStorageLocation{},
+ },
+ }
+
+ for _, tc := range invalidTestCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ _, err := CopySentinelStorageLocation(tc.StorageLocation)
+ require.Error(t, err)
+ })
+ }
+}
+
+func Test_CommonPrefixLastIndex(t *testing.T) {
+ s3StorageLocationName := "s3Test"
+ s3StorageLocationName2 := "s3Test2"
+ s3StorageBaseUrl := "s3://my_example_bucket"
+ s3StorageAwsRoleArn := "arn:aws:iam::123456789012:role/myrole"
+ s3EncryptionKmsKeyId := "123456789"
+
+ gcsStorageLocationName := "gcsTest"
+ gcsStorageLocationName2 := "gcsTest2"
+ gcsStorageBaseUrl := "gcs://my_example_bucket"
+ gcsEncryptionKmsKeyId := "123456789"
+
+ azureStorageLocationName := "azureTest"
+ azureStorageLocationName2 := "azureTest2"
+ azureStorageBaseUrl := "azure://123456789.blob.core.windows.net/my_example_container"
+ azureTenantId := "123456789"
+
+ s3StorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: &s3StorageAwsExternalId,
+ Encryption: &ExternalVolumeS3Encryption{
+ Type: S3EncryptionTypeSseKms,
+ KmsKeyId: &s3EncryptionKmsKeyId,
+ },
+ }
+
+ s3StorageLocationB := S3StorageLocationParams{
+ Name: s3StorageLocationName2,
+ StorageProvider: S3StorageProviderS3,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ StorageAwsExternalId: &s3StorageAwsExternalId,
+ Encryption: &ExternalVolumeS3Encryption{
+ Type: S3EncryptionTypeSseKms,
+ KmsKeyId: &s3EncryptionKmsKeyId,
+ },
+ }
+
+ azureStorageLocationA := AzureStorageLocationParams{
+ Name: azureStorageLocationName,
+ StorageBaseUrl: azureStorageBaseUrl,
+ AzureTenantId: azureTenantId,
+ }
+
+ azureStorageLocationB := AzureStorageLocationParams{
+ Name: azureStorageLocationName2,
+ StorageBaseUrl: azureStorageBaseUrl,
+ AzureTenantId: azureTenantId,
+ }
+
+ gcsStorageLocationA := GCSStorageLocationParams{
+ Name: gcsStorageLocationName,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ gcsStorageLocationB := GCSStorageLocationParams{
+ Name: gcsStorageLocationName2,
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ gcsStorageLocationC := GCSStorageLocationParams{
+ Name: "test",
+ StorageBaseUrl: gcsStorageBaseUrl,
+ Encryption: &ExternalVolumeGCSEncryption{
+ Type: GCSEncryptionTypeSseKms,
+ KmsKeyId: &gcsEncryptionKmsKeyId,
+ },
+ }
+
+ s3GovStorageLocationA := S3StorageLocationParams{
+ Name: s3StorageLocationName,
+ StorageProvider: S3StorageProviderS3GOV,
+ StorageBaseUrl: s3StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageAwsRoleArn,
+ }
+
+ testCases := []struct {
+ Name string
+ ListA []ExternalVolumeStorageLocation
+ ListB []ExternalVolumeStorageLocation
+ ExpectedOutput int
+ }{
+ {
+ Name: "Two empty lists",
+ ListA: []ExternalVolumeStorageLocation{},
+ ListB: []ExternalVolumeStorageLocation{},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "First list empty",
+ ListA: []ExternalVolumeStorageLocation{},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Second list empty",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Lists with no common prefix - length 1",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationB}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Lists with no common prefix - length 2",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}, {AzureStorageLocationParams: &azureStorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationB}, {AzureStorageLocationParams: &azureStorageLocationB}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Identical lists - length 1",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ExpectedOutput: 0,
+ },
+ {
+ Name: "Identical lists - length 2",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}, {AzureStorageLocationParams: &azureStorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}, {AzureStorageLocationParams: &azureStorageLocationA}},
+ ExpectedOutput: 1,
+ },
+ {
+ Name: "Identical lists - length 3",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {S3StorageLocationParams: &s3GovStorageLocationA},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {S3StorageLocationParams: &s3GovStorageLocationA},
+ },
+ ExpectedOutput: 2,
+ },
+ {
+ Name: "Lists with a common prefix - length 3, matching up to and including index 1",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationB},
+ },
+ ExpectedOutput: 1,
+ },
+ {
+ Name: "Lists with a common prefix - length 4, matching up to and including index 2",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationB},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationC},
+ },
+ ExpectedOutput: 2,
+ },
+ {
+ Name: "Lists with a common prefix - length 4, matching up to and including index 1",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationC},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationB},
+ {GCSStorageLocationParams: &gcsStorageLocationC},
+ },
+ ExpectedOutput: 1,
+ },
+ {
+ Name: "Lists with a common prefix - different lengths, matching up to and including index 1 (last index of shorter list)",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ },
+ ExpectedOutput: 1,
+ },
+ {
+ Name: "Lists with a common prefix - different lengths, matching up to and including index 2",
+ ListA: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {S3StorageLocationParams: &s3StorageLocationB},
+ {GCSStorageLocationParams: &gcsStorageLocationA},
+ {GCSStorageLocationParams: &gcsStorageLocationB},
+ {AzureStorageLocationParams: &azureStorageLocationB},
+ },
+ ListB: []ExternalVolumeStorageLocation{
+ {S3StorageLocationParams: &s3StorageLocationA},
+ {AzureStorageLocationParams: &azureStorageLocationA},
+ {S3StorageLocationParams: &s3StorageLocationB},
+ {GCSStorageLocationParams: &gcsStorageLocationB},
+ {AzureStorageLocationParams: &azureStorageLocationB},
+ },
+ ExpectedOutput: 2,
+ },
+ {
+ Name: "Empty S3 storage location",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &S3StorageLocationParams{}}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Empty GCS storage location",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{GCSStorageLocationParams: &GCSStorageLocationParams{}}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Empty Azure storage location",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{AzureStorageLocationParams: &AzureStorageLocationParams{}}},
+ ExpectedOutput: -1,
+ },
+ {
+ Name: "Empty storage location",
+ ListA: []ExternalVolumeStorageLocation{{S3StorageLocationParams: &s3StorageLocationA}},
+ ListB: []ExternalVolumeStorageLocation{{}},
+ ExpectedOutput: -1,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ commonPrefixLastIndex, err := CommonPrefixLastIndex(tc.ListA, tc.ListB)
+ require.NoError(t, err)
+ assert.Equal(t, tc.ExpectedOutput, commonPrefixLastIndex)
+ })
+ }
+}
diff --git a/pkg/sdk/external_volumes_impl_gen.go b/pkg/sdk/external_volumes_impl_gen.go
index 5b7de50826..7e6a8567fa 100644
--- a/pkg/sdk/external_volumes_impl_gen.go
+++ b/pkg/sdk/external_volumes_impl_gen.go
@@ -160,9 +160,14 @@ func (r *ShowExternalVolumeRequest) toOpts() *ShowExternalVolumeOptions {
}
func (r externalVolumeShowRow) convert() *ExternalVolume {
- return &ExternalVolume{
+ externalVolume := ExternalVolume{
Name: r.Name,
AllowWrites: r.AllowWrites,
- Comment: r.Comment,
}
+
+ if r.Comment.Valid {
+ externalVolume.Comment = r.Comment.String
+ }
+
+ return &externalVolume
}
diff --git a/pkg/sdk/testint/external_volumes_gen_integration_test.go b/pkg/sdk/testint/external_volumes_gen_integration_test.go
index 2c1c14056b..38fa11985f 100644
--- a/pkg/sdk/testint/external_volumes_gen_integration_test.go
+++ b/pkg/sdk/testint/external_volumes_gen_integration_test.go
@@ -1,13 +1,11 @@
package testint
import (
- "encoding/json"
- "fmt"
"strconv"
- "strings"
"testing"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -31,144 +29,8 @@ func TestInt_ExternalVolumes(t *testing.T) {
assertExternalVolumeShowResult := func(t *testing.T, s *sdk.ExternalVolume, name sdk.AccountObjectIdentifier, allowWrites bool, comment string) {
t.Helper()
assert.Equal(t, name.Name(), s.Name)
- assert.Equal(t, strconv.FormatBool(allowWrites), s.AllowWrites)
- assert.Equal(t, comment, s.Comment)
- }
-
- // Structs for trimProperties function
- type ExternalVolumePropNameValue struct {
- Name string
- Value string
- }
-
- type S3StorageLocation struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- StorageAllowedLocations []string `json:"STORAGE_ALLOWED_LOCATIONS"`
- StorageAwsRoleArn string `json:"STORAGE_AWS_ROLE_ARN"`
- StroageAwsIamUserArn string `json:"STORAGE_AWS_IAM_USER_ARN"`
- StorageAwsExternalId string `json:"STORAGE_AWS_EXTERNAL_ID"`
- EncryptionType string `json:"ENCRYPTION_TYPE"`
- EncryptionKmsId string `json:"ENCRYPTION_KMS_KEY_ID"`
- }
-
- type S3StorageLocationTrimmed struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- StorageAwsRoleArn string `json:"STORAGE_AWS_ROLE_ARN"`
- StorageAwsExternalId string `json:"STORAGE_AWS_EXTERNAL_ID"`
- EncryptionType string `json:"ENCRYPTION_TYPE,omitempty"`
- EncryptionKmsId string `json:"ENCRYPTION_KMS_KEY_ID,omitempty"`
- }
-
- type GCSStorageLocation struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- StorageAllowedLocations []string `json:"STORAGE_ALLOWED_LOCATIONS"`
- StorageGcpServiceAccount string `json:"STORAGE_GCP_SERVICE_ACCOUNT"`
- EncryptionType string `json:"ENCRYPTION_TYPE"`
- EncryptionKmsId string `json:"ENCRYPTION_KMS_KEY_ID"`
- }
-
- type GCSStorageLocationTrimmed struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- EncryptionType string `json:"ENCRYPTION_TYPE,omitempty"`
- EncryptionKmsId string `json:"ENCRYPTION_KMS_KEY_ID,omitempty"`
- }
-
- type AzureStorageLocation struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- StorageAllowedLocations []string `json:"STORAGE_ALLOWED_LOCATIONS"`
- AzureTenantId string `json:"AZURE_TENANT_ID"`
- AzureMultiTenantAppName string `json:"AZURE_MULTI_TENANT_APP_NAME"`
- AzureConsentUrl string `json:"AZURE_CONSENT_URL"`
- EncryptionType string `json:"ENCRYPTION_TYPE"`
- EncryptionKmsId string `json:"ENCRYPTION_KMS_KEY_ID"`
- }
-
- type AzureStorageLocationTrimmed struct {
- Name string `json:"NAME"`
- StorageProvider string `json:"STORAGE_PROVIDER"`
- StorageBaseUrl string `json:"STORAGE_BASE_URL"`
- AzureTenantId string `json:"AZURE_TENANT_ID"`
- }
-
- // Enforce only property names and values in tests, not parent_property, type and property_default
- // In addition the storage location properties are trimmed to only contain values that we set
- trimProperties := func(t *testing.T, props []sdk.ExternalVolumeProperty) []ExternalVolumePropNameValue {
- t.Helper()
- var externalVolumePropNameValue []ExternalVolumePropNameValue
- for _, p := range props {
- if strings.Contains(p.Name, "STORAGE_LOCATION_") {
- switch {
- case strings.Contains(p.Value, `"STORAGE_PROVIDER":"S3"`):
- s3StorageLocation := S3StorageLocation{}
- err := json.Unmarshal([]byte(p.Value), &s3StorageLocation)
- require.NoError(t, err)
- s3StorageLocationTrimmed := S3StorageLocationTrimmed{
- Name: s3StorageLocation.Name,
- StorageProvider: s3StorageLocation.StorageProvider,
- StorageBaseUrl: s3StorageLocation.StorageBaseUrl,
- StorageAwsRoleArn: s3StorageLocation.StorageAwsRoleArn,
- StorageAwsExternalId: s3StorageLocation.StorageAwsExternalId,
- EncryptionType: s3StorageLocation.EncryptionType,
- EncryptionKmsId: s3StorageLocation.EncryptionKmsId,
- }
- s3StorageLocationTrimmedMarshaled, err := json.Marshal(s3StorageLocationTrimmed)
- require.NoError(t, err)
- externalVolumePropNameValue = append(
- externalVolumePropNameValue,
- ExternalVolumePropNameValue{Name: p.Name, Value: string(s3StorageLocationTrimmedMarshaled)},
- )
- case strings.Contains(p.Value, `"STORAGE_PROVIDER":"GCS"`):
- gcsStorageLocation := GCSStorageLocation{}
- err := json.Unmarshal([]byte(p.Value), &gcsStorageLocation)
- require.NoError(t, err)
- gcsStorageLocationTrimmed := GCSStorageLocationTrimmed{
- Name: gcsStorageLocation.Name,
- StorageProvider: gcsStorageLocation.StorageProvider,
- StorageBaseUrl: gcsStorageLocation.StorageBaseUrl,
- EncryptionType: gcsStorageLocation.EncryptionType,
- EncryptionKmsId: gcsStorageLocation.EncryptionKmsId,
- }
- gcsStorageLocationTrimmedMarshaled, err := json.Marshal(gcsStorageLocationTrimmed)
- require.NoError(t, err)
- externalVolumePropNameValue = append(
- externalVolumePropNameValue,
- ExternalVolumePropNameValue{Name: p.Name, Value: string(gcsStorageLocationTrimmedMarshaled)},
- )
- case strings.Contains(p.Value, `"STORAGE_PROVIDER":"AZURE"`):
- azureStorageLocation := AzureStorageLocation{}
- err := json.Unmarshal([]byte(p.Value), &azureStorageLocation)
- require.NoError(t, err)
- azureStorageLocationTrimmed := AzureStorageLocationTrimmed{
- Name: azureStorageLocation.Name,
- StorageProvider: azureStorageLocation.StorageProvider,
- StorageBaseUrl: azureStorageLocation.StorageBaseUrl,
- AzureTenantId: azureStorageLocation.AzureTenantId,
- }
- azureStorageLocationTrimmedMarshaled, err := json.Marshal(azureStorageLocationTrimmed)
- require.NoError(t, err)
- externalVolumePropNameValue = append(
- externalVolumePropNameValue,
- ExternalVolumePropNameValue{Name: p.Name, Value: string(azureStorageLocationTrimmedMarshaled)},
- )
- default:
- panic("Unrecognized storage provider in storage location property")
- }
- } else {
- externalVolumePropNameValue = append(externalVolumePropNameValue, ExternalVolumePropNameValue{Name: p.Name, Value: p.Value})
- }
- }
-
- return externalVolumePropNameValue
+ assert.Equal(t, allowWrites, s.AllowWrites)
+ assert.Equal(t, s.Comment, comment)
}
// Storage location structs for testing
@@ -261,14 +123,17 @@ func TestInt_ExternalVolumes(t *testing.T) {
},
}
- createExternalVolume := func(t *testing.T, storageLocations []sdk.ExternalVolumeStorageLocation, allowWrites bool, comment string) sdk.AccountObjectIdentifier {
+ createExternalVolume := func(t *testing.T, storageLocations []sdk.ExternalVolumeStorageLocation, allowWrites bool, comment *string) sdk.AccountObjectIdentifier {
t.Helper()
id := testClientHelper().Ids.RandomAccountObjectIdentifier()
req := sdk.NewCreateExternalVolumeRequest(id, storageLocations).
WithIfNotExists(true).
- WithAllowWrites(allowWrites).
- WithComment(comment)
+ WithAllowWrites(allowWrites)
+
+ if comment != nil {
+ req = req.WithComment(*comment)
+ }
err := client.ExternalVolumes.Create(ctx, req)
require.NoError(t, err)
@@ -284,7 +149,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - S3 Storage Location", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, s3StorageLocations, allowWrites, comment)
+ id := createExternalVolume(t, s3StorageLocations, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -292,10 +157,31 @@ func TestInt_ExternalVolumes(t *testing.T) {
assertExternalVolumeShowResult(t, externalVolume, id, allowWrites, comment)
})
+ t.Run("Create - S3 Storage Location empty Comment", func(t *testing.T) {
+ allowWrites := true
+ emptyComment := ""
+ id := createExternalVolume(t, s3StorageLocations, allowWrites, &emptyComment)
+
+ externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
+ require.NoError(t, err)
+
+ assertExternalVolumeShowResult(t, externalVolume, id, allowWrites, emptyComment)
+ })
+
+ t.Run("Create - S3 Storage Location No Comment", func(t *testing.T) {
+ allowWrites := true
+ id := createExternalVolume(t, s3StorageLocations, allowWrites, nil)
+
+ externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
+ require.NoError(t, err)
+
+ assertExternalVolumeShowResult(t, externalVolume, id, allowWrites, "")
+ })
+
t.Run("Create - S3 Storage Location None Encryption", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, s3StorageLocationsNoneEncryption, allowWrites, comment)
+ id := createExternalVolume(t, s3StorageLocationsNoneEncryption, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -306,7 +192,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - S3 Storage Location No Encryption", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, s3StorageLocationsNoEncryption, allowWrites, comment)
+ id := createExternalVolume(t, s3StorageLocationsNoEncryption, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -317,7 +203,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - GCS Storage Location", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, gcsStorageLocations, allowWrites, comment)
+ id := createExternalVolume(t, gcsStorageLocations, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -328,7 +214,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - GCS Storage Location None Encryption", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, gcsStorageLocationsNoneEncryption, allowWrites, comment)
+ id := createExternalVolume(t, gcsStorageLocationsNoneEncryption, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -339,7 +225,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - GCS Storage Location No Encryption", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, gcsStorageLocationsNoEncryption, allowWrites, comment)
+ id := createExternalVolume(t, gcsStorageLocationsNoEncryption, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -350,7 +236,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - Azure Storage Location", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, azureStorageLocations, allowWrites, comment)
+ id := createExternalVolume(t, azureStorageLocations, allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -361,7 +247,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Create - Multiple Storage Locations", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, append(append(s3StorageLocations, gcsStorageLocationsNoneEncryption...), azureStorageLocations...), allowWrites, comment)
+ id := createExternalVolume(t, append(append(s3StorageLocations, gcsStorageLocationsNoneEncryption...), azureStorageLocations...), allowWrites, &comment)
externalVolume, err := client.ExternalVolumes.ShowByID(ctx, id)
require.NoError(t, err)
@@ -372,7 +258,7 @@ func TestInt_ExternalVolumes(t *testing.T) {
t.Run("Alter - remove storage location", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, append(s3StorageLocationsNoneEncryption, gcsStorageLocationsNoneEncryption...), allowWrites, comment)
+ id := createExternalVolume(t, append(s3StorageLocationsNoneEncryption, gcsStorageLocationsNoneEncryption...), allowWrites, &comment)
req := sdk.NewAlterExternalVolumeRequest(id).WithRemoveStorageLocation(gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Name)
@@ -382,36 +268,37 @@ func TestInt_ExternalVolumes(t *testing.T) {
props, err := client.ExternalVolumes.Describe(ctx, id)
require.NoError(t, err)
- trimmedProperties := trimProperties(t, props)
- assert.Equal(t, 4, len(trimmedProperties))
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "COMMENT", Value: comment})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ALLOW_WRITES", Value: strconv.FormatBool(allowWrites)})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ACTIVE", Value: ""})
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_1",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type,
- ),
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(props)
+ require.NoError(t, err)
+ expectedParsedExternalVolumeDescribed := helpers.ParsedExternalVolumeDescribed{
+ StorageLocations: []helpers.StorageLocation{
+ {
+ Name: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
},
- )
+ Active: "",
+ Comment: comment,
+ AllowWrites: strconv.FormatBool(allowWrites),
+ }
+
+ assert.True(t, helpers.ParsedExternalVolumesDescribedEqual(parsedExternalVolumeDescribed, expectedParsedExternalVolumeDescribed))
})
t.Run("Alter - set comment", func(t *testing.T) {
allowWrites := true
- comment := ""
- id := createExternalVolume(t, s3StorageLocationsNoneEncryption, allowWrites, "some comment")
+ comment1 := "some comment"
+ comment2 := ""
+ id := createExternalVolume(t, s3StorageLocationsNoneEncryption, allowWrites, &comment1)
req := sdk.NewAlterExternalVolumeRequest(id).WithSet(
- *sdk.NewAlterExternalVolumeSetRequest().WithComment(comment),
+ *sdk.NewAlterExternalVolumeSetRequest().WithComment(comment2),
)
err := client.ExternalVolumes.Alter(ctx, req)
@@ -420,32 +307,33 @@ func TestInt_ExternalVolumes(t *testing.T) {
props, err := client.ExternalVolumes.Describe(ctx, id)
require.NoError(t, err)
- trimmedProperties := trimProperties(t, props)
- assert.Equal(t, 3, len(trimmedProperties))
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ALLOW_WRITES", Value: strconv.FormatBool(allowWrites)})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ACTIVE", Value: ""})
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_1",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type,
- ),
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(props)
+ require.NoError(t, err)
+ expectedParsedExternalVolumeDescribed := helpers.ParsedExternalVolumeDescribed{
+ StorageLocations: []helpers.StorageLocation{
+ {
+ Name: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
},
- )
+ Active: "",
+ Comment: comment2,
+ AllowWrites: strconv.FormatBool(allowWrites),
+ }
+
+ assert.True(t, helpers.ParsedExternalVolumesDescribedEqual(parsedExternalVolumeDescribed, expectedParsedExternalVolumeDescribed))
})
t.Run("Alter - set allow writes", func(t *testing.T) {
allowWrites := false
comment := "some comment"
- id := createExternalVolume(t, s3StorageLocations, true, comment)
+ id := createExternalVolume(t, s3StorageLocations, true, &comment)
req := sdk.NewAlterExternalVolumeRequest(id).WithSet(
*sdk.NewAlterExternalVolumeSetRequest().WithAllowWrites(allowWrites),
@@ -457,34 +345,33 @@ func TestInt_ExternalVolumes(t *testing.T) {
props, err := client.ExternalVolumes.Describe(ctx, id)
require.NoError(t, err)
- trimmedProperties := trimProperties(t, props)
- assert.Equal(t, 4, len(trimmedProperties))
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "COMMENT", Value: comment})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ALLOW_WRITES", Value: strconv.FormatBool(allowWrites)})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ACTIVE", Value: ""})
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_1",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s"}`,
- s3StorageLocations[0].S3StorageLocationParams.Name,
- s3StorageLocations[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocations[0].S3StorageLocationParams.Encryption.Type,
- *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
- ),
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(props)
+ require.NoError(t, err)
+ expectedParsedExternalVolumeDescribed := helpers.ParsedExternalVolumeDescribed{
+ StorageLocations: []helpers.StorageLocation{
+ {
+ Name: s3StorageLocations[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocations[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocations[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
+ AzureTenantId: "",
+ },
},
- )
+ Active: "",
+ Comment: comment,
+ AllowWrites: strconv.FormatBool(allowWrites),
+ }
+
+ assert.True(t, helpers.ParsedExternalVolumesDescribedEqual(parsedExternalVolumeDescribed, expectedParsedExternalVolumeDescribed))
})
t.Run("Alter - add s3 storage location to external volume", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, gcsStorageLocationsNoneEncryption, allowWrites, comment)
+ id := createExternalVolume(t, gcsStorageLocationsNoneEncryption, allowWrites, &comment)
req := sdk.NewAlterExternalVolumeRequest(id).WithAddStorageLocation(
*sdk.NewExternalVolumeStorageLocationRequest().WithS3StorageLocationParams(
@@ -507,41 +394,37 @@ func TestInt_ExternalVolumes(t *testing.T) {
props, err := client.ExternalVolumes.Describe(ctx, id)
require.NoError(t, err)
- trimmedProperties := trimProperties(t, props)
- assert.Equal(t, 5, len(trimmedProperties))
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "COMMENT", Value: comment})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ALLOW_WRITES", Value: strconv.FormatBool(allowWrites)})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ACTIVE", Value: ""})
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_1",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"GCS","STORAGE_BASE_URL":"%s","ENCRYPTION_TYPE":"%s"}`,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Name,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Encryption.Type,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_2",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s"}`,
- s3StorageLocations[0].S3StorageLocationParams.Name,
- s3StorageLocations[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocations[0].S3StorageLocationParams.Encryption.Type,
- *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
- ),
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(props)
+ require.NoError(t, err)
+ expectedParsedExternalVolumeDescribed := helpers.ParsedExternalVolumeDescribed{
+ StorageLocations: []helpers.StorageLocation{
+ {
+ Name: gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Name,
+ StorageProvider: string(sdk.StorageProviderGCS),
+ StorageBaseUrl: gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: string(gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: s3StorageLocations[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocations[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocations[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
+ AzureTenantId: "",
+ },
},
- )
+ Active: "",
+ Comment: comment,
+ AllowWrites: strconv.FormatBool(allowWrites),
+ }
+
+ assert.True(t, helpers.ParsedExternalVolumesDescribedEqual(parsedExternalVolumeDescribed, expectedParsedExternalVolumeDescribed))
})
t.Run("Describe", func(t *testing.T) {
@@ -551,123 +434,99 @@ func TestInt_ExternalVolumes(t *testing.T) {
t,
append(append(append(append(append(append(s3StorageLocations, gcsStorageLocationsNoneEncryption...), azureStorageLocations...), s3StorageLocationsNoneEncryption...), gcsStorageLocations...), s3StorageLocationsNoEncryption...), gcsStorageLocationsNoEncryption...),
allowWrites,
- comment,
+ &comment,
)
props, err := client.ExternalVolumes.Describe(ctx, id)
require.NoError(t, err)
- trimmedProperties := trimProperties(t, props)
- assert.Equal(t, 10, len(trimmedProperties))
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "COMMENT", Value: comment})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ALLOW_WRITES", Value: strconv.FormatBool(allowWrites)})
- assert.Contains(t, trimmedProperties, ExternalVolumePropNameValue{Name: "ACTIVE", Value: ""})
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_1",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s"}`,
- s3StorageLocations[0].S3StorageLocationParams.Name,
- s3StorageLocations[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocations[0].S3StorageLocationParams.Encryption.Type,
- *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_2",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"GCS","STORAGE_BASE_URL":"%s","ENCRYPTION_TYPE":"%s"}`,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Name,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
- gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Encryption.Type,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_3",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"AZURE","STORAGE_BASE_URL":"%s","AZURE_TENANT_ID":"%s"}`,
- azureStorageLocations[0].AzureStorageLocationParams.Name,
- azureStorageLocations[0].AzureStorageLocationParams.StorageBaseUrl,
- azureStorageLocations[0].AzureStorageLocationParams.AzureTenantId,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_4",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"%s"}`,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
- s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_5",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"GCS","STORAGE_BASE_URL":"%s","ENCRYPTION_TYPE":"%s","ENCRYPTION_KMS_KEY_ID":"%s"}`,
- gcsStorageLocations[0].GCSStorageLocationParams.Name,
- gcsStorageLocations[0].GCSStorageLocationParams.StorageBaseUrl,
- gcsStorageLocations[0].GCSStorageLocationParams.Encryption.Type,
- *gcsStorageLocations[0].GCSStorageLocationParams.Encryption.KmsKeyId,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_6",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"%s","STORAGE_BASE_URL":"%s","STORAGE_AWS_ROLE_ARN":"%s","STORAGE_AWS_EXTERNAL_ID":"%s","ENCRYPTION_TYPE":"NONE"}`,
- s3StorageLocationsNoEncryption[0].S3StorageLocationParams.Name,
- s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageProvider,
- s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageBaseUrl,
- s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
- *s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
- ),
- },
- )
- assert.Contains(
- t,
- trimmedProperties,
- ExternalVolumePropNameValue{
- Name: "STORAGE_LOCATION_7",
- Value: fmt.Sprintf(
- `{"NAME":"%s","STORAGE_PROVIDER":"GCS","STORAGE_BASE_URL":"%s","ENCRYPTION_TYPE":"NONE"}`,
- gcsStorageLocationsNoEncryption[0].GCSStorageLocationParams.Name,
- gcsStorageLocationsNoEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
- ),
+ parsedExternalVolumeDescribed, err := helpers.ParseExternalVolumeDescribed(props)
+ require.NoError(t, err)
+ expectedParsedExternalVolumeDescribed := helpers.ParsedExternalVolumeDescribed{
+ StorageLocations: []helpers.StorageLocation{
+ {
+ Name: s3StorageLocations[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocations[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocations[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocations[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocations[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocations[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: *s3StorageLocations[0].S3StorageLocationParams.Encryption.KmsKeyId,
+ AzureTenantId: "",
+ },
+ {
+ Name: gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Name,
+ StorageProvider: string(sdk.StorageProviderGCS),
+ StorageBaseUrl: gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: string(gcsStorageLocationsNoneEncryption[0].GCSStorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: azureStorageLocations[0].AzureStorageLocationParams.Name,
+ StorageProvider: string(sdk.StorageProviderAzure),
+ StorageBaseUrl: azureStorageLocations[0].AzureStorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: "",
+ EncryptionKmsKeyId: "",
+ AzureTenantId: azureStorageLocations[0].AzureStorageLocationParams.AzureTenantId,
+ },
+ {
+ Name: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: string(s3StorageLocationsNoneEncryption[0].S3StorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: gcsStorageLocations[0].GCSStorageLocationParams.Name,
+ StorageProvider: string(sdk.StorageProviderGCS),
+ StorageBaseUrl: gcsStorageLocations[0].GCSStorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: string(gcsStorageLocations[0].GCSStorageLocationParams.Encryption.Type),
+ EncryptionKmsKeyId: *gcsStorageLocations[0].GCSStorageLocationParams.Encryption.KmsKeyId,
+ AzureTenantId: "",
+ },
+ {
+ Name: s3StorageLocationsNoEncryption[0].S3StorageLocationParams.Name,
+ StorageProvider: string(s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageProvider),
+ StorageBaseUrl: s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageAwsRoleArn,
+ StorageAwsExternalId: *s3StorageLocationsNoEncryption[0].S3StorageLocationParams.StorageAwsExternalId,
+ EncryptionType: "NONE",
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
+ {
+ Name: gcsStorageLocationsNoEncryption[0].GCSStorageLocationParams.Name,
+ StorageProvider: string(sdk.StorageProviderGCS),
+ StorageBaseUrl: gcsStorageLocationsNoEncryption[0].GCSStorageLocationParams.StorageBaseUrl,
+ StorageAwsRoleArn: "",
+ StorageAwsExternalId: "",
+ EncryptionType: "NONE",
+ EncryptionKmsKeyId: "",
+ AzureTenantId: "",
+ },
},
- )
+ Active: "",
+ Comment: comment,
+ AllowWrites: strconv.FormatBool(allowWrites),
+ }
+
+ assert.True(t, helpers.ParsedExternalVolumesDescribedEqual(parsedExternalVolumeDescribed, expectedParsedExternalVolumeDescribed))
})
t.Run("Show with like", func(t *testing.T) {
allowWrites := true
comment := "some comment"
- id := createExternalVolume(t, s3StorageLocations, allowWrites, comment)
+ id := createExternalVolume(t, s3StorageLocations, allowWrites, &comment)
name := id.Name()
req := sdk.NewShowExternalVolumeRequest().WithLike(sdk.Like{Pattern: &name})
diff --git a/templates/resources/external_volume.md.tmpl b/templates/resources/external_volume.md.tmpl
new file mode 100644
index 0000000000..fb6aff121b
--- /dev/null
+++ b/templates/resources/external_volume.md.tmpl
@@ -0,0 +1,35 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ if gt (len (split .Description "")) 1 -}}
+{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }}
+{{- else -}}
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+{{- end }}
+---
+
+!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release.
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+{{ if .HasExample -}}
+## Example Usage
+
+{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}}
+-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources).
+
+
+{{- end }}
+
+{{ .SchemaMarkdown | trimspace }}
+{{- if .HasImport }}
+
+## Import
+
+Import is supported using the following syntax:
+
+{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}}
+{{- end }}