Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/sagemaker_feature_group - support table format #30118

Merged
merged 10 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/30118.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_sagemaker_feature_group: Add `table_format` to the `offline_store_config` configuration block
```
89 changes: 50 additions & 39 deletions internal/service/sagemaker/feature_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,11 @@ func ResourceFeatureGroup() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"feature_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}`),
"Must start and end with an alphanumeric character and Can only contain alphanumeric character and hyphens. Spaces are not allowed."),
),
},
"record_identifier_feature_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]([-_]*[a-zA-Z0-9]){0,63}`),
"Must start and end with an alphanumeric character and Can only contains alphanumeric characters, hyphens, underscores. Spaces are not allowed."),
),
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(0, 128),
},
"event_time_feature_name": {
Type: schema.TypeString,
Expand All @@ -65,18 +51,6 @@ func ResourceFeatureGroup() *schema.Resource {
"Must start and end with an alphanumeric character and Can only contains alphanumeric characters, hyphens, underscores. Spaces are not allowed."),
),
},
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringLenBetween(0, 128),
},
"role_arn": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: verify.ValidARN,
},
"feature_definition": {
Type: schema.TypeList,
Required: true,
Expand All @@ -103,6 +77,16 @@ func ResourceFeatureGroup() *schema.Resource {
},
},
},
"feature_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,63}`),
"Must start and end with an alphanumeric character and Can only contain alphanumeric character and hyphens. Spaces are not allowed."),
),
},
"offline_store_config": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -136,6 +120,10 @@ func ResourceFeatureGroup() *schema.Resource {
},
},
},
"disable_glue_table_creation": {
Type: schema.TypeBool,
Optional: true,
},
"s3_storage_config": {
Type: schema.TypeList,
Required: true,
Expand All @@ -154,9 +142,11 @@ func ResourceFeatureGroup() *schema.Resource {
},
},
},
"disable_glue_table_creation": {
Type: schema.TypeBool,
Optional: true,
"table_format": {
Type: schema.TypeString,
Optional: true,
Default: sagemaker.TableFormatGlue,
ValidateFunc: validation.StringInSlice(sagemaker.TableFormat_Values(), false),
},
},
},
Expand All @@ -169,6 +159,11 @@ func ResourceFeatureGroup() *schema.Resource {
AtLeastOneOf: []string{"offline_store_config", "online_store_config"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enable_online_store": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"security_config": {
Type: schema.TypeList,
Optional: true,
Expand All @@ -183,14 +178,25 @@ func ResourceFeatureGroup() *schema.Resource {
},
},
},
"enable_online_store": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
},
},
"record_identifier_feature_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 64),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]([-_]*[a-zA-Z0-9]){0,63}`),
"Must start and end with an alphanumeric character and Can only contains alphanumeric characters, hyphens, underscores. Spaces are not allowed."),
),
},
"role_arn": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: verify.ValidARN,
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
},
Expand Down Expand Up @@ -472,6 +478,10 @@ func expandFeatureGroupOfflineStoreConfig(l []interface{}) *sagemaker.OfflineSto
config.DisableGlueTableCreation = aws.Bool(v)
}

if v, ok := m["table_format"].(string); ok {
config.TableFormat = aws.String(v)
}

return config
}

Expand All @@ -482,6 +492,7 @@ func flattenFeatureGroupOfflineStoreConfig(config *sagemaker.OfflineStoreConfig)

m := map[string]interface{}{
"disable_glue_table_creation": aws.BoolValue(config.DisableGlueTableCreation),
"table_format": aws.StringValue(config.TableFormat),
}

if config.DataCatalogConfig != nil {
Expand Down
99 changes: 80 additions & 19 deletions internal/service/sagemaker/feature_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestAccSageMakerFeatureGroup_serial(t *testing.T) {
"disappears": TestAccSageMakerFeatureGroup_disappears,
"multipleFeatures": testAccFeatureGroup_multipleFeatures,
"offlineConfig_basic": testAccFeatureGroup_offlineConfig_basic,
"offlineConfig_format": testAccFeatureGroup_offlineConfig_format,
"offlineConfig_createCatalog": testAccFeatureGroup_offlineConfig_createCatalog,
"offlineConfig_providedCatalog": TestAccSageMakerFeatureGroup_Offline_providedCatalog,
"onlineConfigSecurityConfig": testAccFeatureGroup_onlineConfigSecurityConfig,
Expand Down Expand Up @@ -236,6 +237,41 @@ func testAccFeatureGroup_offlineConfig_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.s3_storage_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.s3_storage_config.0.s3_uri", fmt.Sprintf("s3://%s/prefix/", rName)),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.data_catalog_config.#", "0"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.table_format", "Glue"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccFeatureGroup_offlineConfig_format(t *testing.T) {
ctx := acctest.Context(t)
var featureGroup sagemaker.DescribeFeatureGroupOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_sagemaker_feature_group.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, sagemaker.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckFeatureGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccFeatureGroupConfig_offlineTableFormat(rName, "Iceberg"),
Check: resource.ComposeTestCheckFunc(
testAccCheckFeatureGroupExists(ctx, resourceName, &featureGroup),
resource.TestCheckResourceAttr(resourceName, "feature_group_name", rName),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.disable_glue_table_creation", "false"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.s3_storage_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.s3_storage_config.0.s3_uri", fmt.Sprintf("s3://%s/prefix/", rName)),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.data_catalog_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "offline_store_config.0.table_format", "Iceberg"),
),
},
{
Expand Down Expand Up @@ -395,7 +431,7 @@ func testAccCheckFeatureGroupExists(ctx context.Context, n string, v *sagemaker.
}
}

func testAccFeatureGroupBaseConfig(rName string) string {
func testAccFeatureGroupConfig_base(rName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}

Expand All @@ -418,18 +454,13 @@ data "aws_iam_policy_document" "test" {
`, rName)
}

func testAccFeatureGroupOfflineBaseConfig(rName string) string {
func testAccFeatureGroupConfig_baseOffline(rName string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
bucket = %[1]q
force_destroy = true
}

resource "aws_s3_bucket_acl" "test" {
bucket = aws_s3_bucket.test.id
acl = "private"
}

resource "aws_iam_role_policy_attachment" "test" {
role = aws_iam_role.test.name
policy_arn = aws_iam_policy.test.arn
Expand All @@ -454,7 +485,7 @@ resource "aws_iam_policy" "test" {
}

func testAccFeatureGroupConfig_basic(rName string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
Expand All @@ -474,7 +505,7 @@ resource "aws_sagemaker_feature_group" "test" {
}

func testAccFeatureGroupConfig_description(rName string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
Expand All @@ -495,7 +526,7 @@ resource "aws_sagemaker_feature_group" "test" {
}

func testAccFeatureGroupConfig_multi(rName string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
Expand All @@ -520,7 +551,7 @@ resource "aws_sagemaker_feature_group" "test" {
}

func testAccFeatureGroupConfig_onlineSecurity(rName string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_kms_key" "test" {
description = %[1]q
deletion_window_in_days = 7
Expand Down Expand Up @@ -550,8 +581,8 @@ resource "aws_sagemaker_feature_group" "test" {

func testAccFeatureGroupConfig_offlineBasic(rName string) string {
return acctest.ConfigCompose(
testAccFeatureGroupBaseConfig(rName),
testAccFeatureGroupOfflineBaseConfig(rName),
testAccFeatureGroupConfig_base(rName),
testAccFeatureGroupConfig_baseOffline(rName),
fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
Expand All @@ -577,10 +608,40 @@ resource "aws_sagemaker_feature_group" "test" {
`, rName))
}

func testAccFeatureGroupConfig_offlineTableFormat(rName, format string) string {
return acctest.ConfigCompose(
testAccFeatureGroupConfig_base(rName),
testAccFeatureGroupConfig_baseOffline(rName),
fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
event_time_feature_name = %[1]q
role_arn = aws_iam_role.test.arn

feature_definition {
feature_name = %[1]q
feature_type = "String"
}

offline_store_config {
disable_glue_table_creation = false
table_format = %[2]q

s3_storage_config {
s3_uri = "s3://${aws_s3_bucket.test.bucket}/prefix/"
}
}

depends_on = [aws_iam_role_policy_attachment.test]
}
`, rName, format))
}

func testAccFeatureGroupConfig_offlineCreateGlueCatalog(rName string) string {
return acctest.ConfigCompose(
testAccFeatureGroupBaseConfig(rName),
testAccFeatureGroupOfflineBaseConfig(rName),
testAccFeatureGroupConfig_base(rName),
testAccFeatureGroupConfig_baseOffline(rName),
fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
Expand Down Expand Up @@ -608,8 +669,8 @@ resource "aws_sagemaker_feature_group" "test" {

func testAccFeatureGroupConfig_offlineCreateGlueCatalogProvidedCatalog(rName string) string {
return acctest.ConfigCompose(
testAccFeatureGroupBaseConfig(rName),
testAccFeatureGroupOfflineBaseConfig(rName),
testAccFeatureGroupConfig_base(rName),
testAccFeatureGroupConfig_baseOffline(rName),
fmt.Sprintf(`
resource "aws_glue_catalog_database" "test" {
name = %[1]q
Expand Down Expand Up @@ -651,7 +712,7 @@ resource "aws_sagemaker_feature_group" "test" {
}

func testAccFeatureGroupConfig_tags1(rName, tag1Key, tag1Value string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
Expand All @@ -675,7 +736,7 @@ resource "aws_sagemaker_feature_group" "test" {
}

func testAccFeatureGroupConfig_tags2(rName, tag1Key, tag1Value, tag2Key, tag2Value string) string {
return acctest.ConfigCompose(testAccFeatureGroupBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccFeatureGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_sagemaker_feature_group" "test" {
feature_group_name = %[1]q
record_identifier_feature_name = %[1]q
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/sagemaker_feature_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ The following arguments are supported:
* `enable_online_store` - (Optional) Set to `true` to disable the automatic creation of an AWS Glue table when configuring an OfflineStore.
* `s3_storage_config` - (Required) The Amazon Simple Storage (Amazon S3) location of OfflineStore. See [S3 Storage Config](#s3-storage-config) Below.
* `data_catalog_config` - (Optional) The meta data of the Glue table that is autogenerated when an OfflineStore is created. See [Data Catalog Config](#data-catalog-config) Below.
* `table_format` - (Optional) Format for the offline store table. Supported formats are `Glue` (Default) and Apache `Iceberg` (https://iceberg.apache.org/).

### Online Store Config

Expand Down