From d6fb8d5e1e07830aab20410c752df7003a5557db Mon Sep 17 00:00:00 2001 From: Sai Kiran Burle Date: Sun, 5 Jan 2020 18:54:54 -0800 Subject: [PATCH 01/17] Add data capture config to Sagemaker endpoint configuration --- ...ce_aws_sagemaker_endpoint_configuration.go | 256 ++++++++++++++++++ ...s_sagemaker_endpoint_configuration_test.go | 70 +++++ aws/validators.go | 42 +++ ...maker_endpoint_configuration.html.markdown | 25 +- 4 files changed, 392 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index 20f9ceece3c..9e51b1467ef 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -92,6 +92,107 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { }, "tags": tagsSchema(), + + "data_capture_config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_capture": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "initial_sampling_percentage": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(0, 100), + }, + + "destination_url": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSagemakerDataCaptureDestinationUrl, + }, + + "kms_key_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + + "capture_options": { + Type: schema.TypeList, + Required: true, + MaxItems: 2, + MinItems: 1, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "capture_mode": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"Input", "Output"}, false), + }, + }, + }, + }, + + "capture_content_type_header": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "csv_content_types": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSagemakerCsvContentType, + }, + }, + }, + Optional: true, + ForceNew: true, + }, + + "json_content_types": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "content_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateSagemakerJsonContentType, + }, + }, + }, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, }, } } @@ -119,6 +220,10 @@ func resourceAwsSagemakerEndpointConfigurationCreate(d *schema.ResourceData, met createOpts.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SagemakerTags() } + if v, ok := d.GetOk("data_capture_config"); ok { + createOpts.DataCaptureConfig = expandSagemakerDataCaptureConfig(v.([]interface{})) + } + log.Printf("[DEBUG] SageMaker Endpoint Configuration create config: %#v", *createOpts) _, err := conn.CreateEndpointConfig(createOpts) if err != nil { @@ -159,6 +264,9 @@ func resourceAwsSagemakerEndpointConfigurationRead(d *schema.ResourceData, meta if err := d.Set("kms_key_arn", endpointConfig.KmsKeyId); err != nil { return err } + if err := d.Set("data_capture_config", flattenSagemakerDataCaptureConfig(endpointConfig.DataCaptureConfig)); err != nil { + return err + } tags, err := keyvaluetags.SagemakerListTags(conn, aws.StringValue(endpointConfig.EndpointConfigArn)) if err != nil { @@ -255,3 +363,151 @@ func flattenProductionVariants(list []*sagemaker.ProductionVariant) []map[string } return result } + +func expandSagemakerDataCaptureConfig(configured []interface{}) *sagemaker.DataCaptureConfig { + if len(configured) == 0 { + return nil + } + + m := configured[0].(map[string]interface{}) + + c := &sagemaker.DataCaptureConfig{ + InitialSamplingPercentage: aws.Int64(int64(m["initial_sampling_percentage"].(int))), + DestinationS3Uri: aws.String(m["destination_url"].(string)), + CaptureOptions: expandSagemakerCaptureOptions(m["capture_options"].([]interface{})), + } + + if v, ok := m["enable_capture"]; ok { + c.EnableCapture = aws.Bool(v.(bool)) + } + + if v, ok := m["kms_key_id"]; ok && v.(string) != "" { + c.KmsKeyId = aws.String(v.(string)) + } + + if v, ok := m["capture_content_type_header"]; ok && (len(v.([]interface{})) > 0) { + c.CaptureContentTypeHeader = expandSagemakerCaptureContentTypeHeader(v.([]interface{})[0].(map[string]interface{})) + } + + return c +} + +func flattenSagemakerDataCaptureConfig(dataCaptureConfig *sagemaker.DataCaptureConfig) []map[string]interface{} { + if dataCaptureConfig == nil { + return []map[string]interface{}{} + } + + cfg := map[string]interface{}{ + //"enable_capture": aws.BoolValue(dataCaptureConfig.EnableCapture), + "initial_sampling_percentage": aws.Int64Value(dataCaptureConfig.InitialSamplingPercentage), + "destination_url": aws.StringValue(dataCaptureConfig.DestinationS3Uri), + //"kms_key_id": aws.StringValue(dataCaptureConfig.KmsKeyId), + "capture_options": flattenSagemakerCaptureOptions(dataCaptureConfig.CaptureOptions), + //"capture_content_type_header": flattenSagemakerCaptureContentTypeHeader(dataCaptureConfig.CaptureContentTypeHeader), + } + + if dataCaptureConfig.EnableCapture != nil { + cfg["enable_capture"] = aws.BoolValue(dataCaptureConfig.EnableCapture) + } + + if dataCaptureConfig.KmsKeyId != nil { + cfg["kms_key_id"] = aws.StringValue(dataCaptureConfig.KmsKeyId) + } + + if dataCaptureConfig.CaptureContentTypeHeader != nil { + cfg["capture_content_type_header"] = flattenSagemakerCaptureContentTypeHeader(dataCaptureConfig.CaptureContentTypeHeader) + } + + return []map[string]interface{}{cfg} +} + +func expandSagemakerCaptureOptions(configured []interface{}) []*sagemaker.CaptureOption { + containers := make([]*sagemaker.CaptureOption, 0, len(configured)) + + for _, lRaw := range configured { + data := lRaw.(map[string]interface{}) + + l := &sagemaker.CaptureOption{ + CaptureMode: aws.String(data["capture_mode"].(string)), + } + containers = append(containers, l) + } + + return containers +} + +func flattenSagemakerCaptureOptions(list []*sagemaker.CaptureOption) []map[string]interface{} { + containers := make([]map[string]interface{}, 0, len(list)) + + for _, lRaw := range list { + captureOption := make(map[string]interface{}) + captureOption["capture_mode"] = aws.StringValue(lRaw.CaptureMode) + containers = append(containers, captureOption) + } + + return containers +} + +func expandSagemakerCaptureContentTypeHeader(m map[string]interface{}) *sagemaker.CaptureContentTypeHeader { + c := &sagemaker.CaptureContentTypeHeader{} + + if v, ok := m["csv_content_types"]; ok && len(v.([]interface{})) > 0 { + contentTypes := make([]*string, 0, len(v.([]interface{}))) + + for _, raw := range v.([]interface{}) { + sRaw := raw.(map[string]interface{})["content_type"] + contentTypes = append(contentTypes, aws.String(sRaw.(string))) + } + + c.CsvContentTypes = contentTypes + } + + if v, ok := m["json_content_types"]; ok && len(v.([]interface{})) > 0 { + contentTypes := make([]*string, 0, len(v.([]interface{}))) + + for _, raw := range v.([]interface{}) { + sRaw := raw.(map[string]interface{})["content_type"] + contentTypes = append(contentTypes, aws.String(sRaw.(string))) + } + + c.JsonContentTypes = contentTypes + } + + return c +} + +func flattenSagemakerCaptureContentTypeHeader(contentTypeHeader *sagemaker.CaptureContentTypeHeader) []map[string]interface{} { + if contentTypeHeader == nil { + return []map[string]interface{}{} + } + + l := make(map[string]interface{}) + + if contentTypeHeader.CsvContentTypes != nil { + csvTypes := make([]map[string]interface{}, 0, len(contentTypeHeader.CsvContentTypes)) + + for _, csvType := range contentTypeHeader.CsvContentTypes { + m := map[string]interface{}{ + "content_type": aws.StringValue(csvType), + } + csvTypes = append(csvTypes, m) + } + + l["csv_content_types"] = csvTypes + } + + if contentTypeHeader.JsonContentTypes != nil { + jsonTypes := make([]map[string]interface{}, 0, len(contentTypeHeader.JsonContentTypes)) + + for _, jsonType := range contentTypeHeader.JsonContentTypes { + m := map[string]interface{}{ + "content_type": aws.StringValue(jsonType), + } + jsonTypes = append(jsonTypes, m) + } + + l["json_content_types"] = jsonTypes + } + + return []map[string]interface{}{l} +} diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 853e30953e6..cf030abff98 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -198,6 +198,37 @@ func TestAccAWSSagemakerEndpointConfiguration_Tags(t *testing.T) { }) } +func TestAccAWSSagemakerEndpointConfiguration_DataCaptureConfig(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + dataDestinationBucketName := fmt.Sprintf("bucket-%s", rName) + resourceName := "aws_sagemaker_endpoint_configuration.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSagemakerEndpointConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSagemakerEndpointConfigurationConfig_DataCaptureConfig(rName, dataDestinationBucketName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSagemakerEndpointConfigurationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.enable_capture", "true"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.initial_sampling_percentage", "50"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_url", fmt.Sprintf("s3://%s/", dataDestinationBucketName)), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.0.capture_mode", "Input"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.1.capture_mode", "Output"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_content_type_header.0.json_content_types.0.content_type", "application/json"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckSagemakerEndpointConfigurationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).sagemakerconn @@ -397,3 +428,42 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { } `, rName) } + +func testAccSagemakerEndpointConfigurationConfig_DataCaptureConfig(rName string, bucket string) string { + return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` +resource "aws_s3_bucket" "foo" { + bucket = %q + acl = "private" + force_destroy = true +} + +resource "aws_sagemaker_endpoint_configuration" "foo" { + name = %q + + production_variants { + variant_name = "variant-1" + model_name = "${aws_sagemaker_model.foo.name}" + initial_instance_count = 2 + instance_type = "ml.t2.medium" + initial_variant_weight = 1 + } + + data_capture_config { + enable_capture = true + initial_sampling_percentage = 50 + destination_url = "s3://${aws_s3_bucket.foo.bucket}/" + capture_options { + capture_mode = "Input" + } + capture_options { + capture_mode = "Output" + } + capture_content_type_header { + json_content_types { + content_type = "application/json" + } + } + } +} +`, bucket, rName) +} diff --git a/aws/validators.go b/aws/validators.go index d877c5c39b3..4399fe5500f 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -508,6 +508,48 @@ func validateSagemakerModelDataUrl(v interface{}, k string) (ws []string, errors return } +func validateSagemakerDataCaptureDestinationUrl(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^(https|s3)://([^/]+)/?(.*)$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must be a valid path: %q", + k, value)) + } + if len(value) > 512 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 512 characters: %q", k, value)) + } + return +} + +func validateSagemakerCsvContentType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must be a valid content type: %q", + k, value)) + } + if len(value) < 1 { + errors = append(errors, fmt.Errorf( + "%q cannot be shorter than 1 character: %q", k, value)) + } + return +} + +func validateSagemakerJsonContentType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q must be a valid content type: %q", + k, value)) + } + if len(value) < 1 { + errors = append(errors, fmt.Errorf( + "%q cannot be shorter than 1 character: %q", k, value)) + } + return +} + func validateCloudWatchDashboardName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if len(value) > 255 { diff --git a/website/docs/r/sagemaker_endpoint_configuration.html.markdown b/website/docs/r/sagemaker_endpoint_configuration.html.markdown index b4db5a37d1e..9575a13e5f5 100644 --- a/website/docs/r/sagemaker_endpoint_configuration.html.markdown +++ b/website/docs/r/sagemaker_endpoint_configuration.html.markdown @@ -39,7 +39,8 @@ The following arguments are supported: * `production_variants` - (Required) Fields are documented below. * `kms_key_arn` - (Optional) Amazon Resource Name (ARN) of a AWS Key Management Service key that Amazon SageMaker uses to encrypt data on the storage volume attached to the ML compute instance that hosts the endpoint. * `name` - (Optional) The name of the endpoint configuration. If omitted, Terraform will assign a random, unique name. -* `tags` - (Optional) A map of tags to assign to the resource. +* `tags` - (Optional) A mapping of tags to assign to the resource. +* `data_capture_config` - (Optional) Specifies the parameters to capture input/output of Sagemaker models endpoints. Fields are documented below. The `production_variants` block supports: @@ -50,6 +51,28 @@ The `production_variants` block supports: * `model_name` - (Required) The name of the model to use. * `variant_name` - (Optional) The name of the variant. If omitted, Terraform will assign a random, unique name. +The `data_capture_config` block supports: + +* `initial_sampling_percentage` - (Required) Portion of data to capture. Should be between 0 and 100. +* `destination_url` - (Required) The URL for S3 location where the captured data is stored. +* `capture_options` - (Required) Specifies what data to capture. Fields are documented below. +* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of a AWS Key Management Service key that Amazon SageMaker uses to encrypt the captured data on Amazon S3. +* `enable_capture` - (Optional) Flag to enable data capture. Defaults to `false`. +* `capture_content_type_header` - (Optional) The content type headers to capture. Fields are documented below. + +The `capture_options` block supports: + +* `capture_mode` - (Required) Specifies the data to be captured. Should be one of `Input` or `Output`. + +The `capture_content_type_header` block supports: + +* `csv_content_types` - (Optional) The CSV content type headers to capture. Fields are documented below. +* `json_content_types` - (Optional) The JSON content type headers to capture. Fields are documented below. + +The `csv_content_types` and `json_content_types` block both support: + +* `content_type` - (Required) The value of the content type header. + ## Attributes Reference The following attributes are exported: From 1974c2521a0f63f643dbd9b41917166f399ac2a8 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:08:10 +0200 Subject: [PATCH 02/17] comments --- ...ce_aws_sagemaker_endpoint_configuration.go | 81 +++++-------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index 9e51b1467ef..afd23746d8c 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -113,7 +113,7 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { ValidateFunc: validation.IntBetween(0, 100), }, - "destination_url": { + "destination_s3_uri": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -139,7 +139,7 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"Input", "Output"}, false), + ValidateFunc: validation.StringInSlice(sagemaker.CaptureMode_Values(), false), }, }, }, @@ -153,36 +153,23 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "csv_content_types": { - Type: schema.TypeList, + Type: schema.TypeSet, MinItems: 1, MaxItems: 10, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "content_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateSagemakerCsvContentType, - }, - }, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSagemakerCsvContentType, }, Optional: true, ForceNew: true, }, - "json_content_types": { - Type: schema.TypeList, + Type: schema.TypeSet, MinItems: 1, MaxItems: 10, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "content_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateSagemakerJsonContentType, - }, - }, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSagemakerJsonContentType, }, Optional: true, ForceNew: true, @@ -373,7 +360,7 @@ func expandSagemakerDataCaptureConfig(configured []interface{}) *sagemaker.DataC c := &sagemaker.DataCaptureConfig{ InitialSamplingPercentage: aws.Int64(int64(m["initial_sampling_percentage"].(int))), - DestinationS3Uri: aws.String(m["destination_url"].(string)), + DestinationS3Uri: aws.String(m["destination_s3_uri"].(string)), CaptureOptions: expandSagemakerCaptureOptions(m["capture_options"].([]interface{})), } @@ -400,7 +387,7 @@ func flattenSagemakerDataCaptureConfig(dataCaptureConfig *sagemaker.DataCaptureC cfg := map[string]interface{}{ //"enable_capture": aws.BoolValue(dataCaptureConfig.EnableCapture), "initial_sampling_percentage": aws.Int64Value(dataCaptureConfig.InitialSamplingPercentage), - "destination_url": aws.StringValue(dataCaptureConfig.DestinationS3Uri), + "destination_s3_uri": aws.StringValue(dataCaptureConfig.DestinationS3Uri), //"kms_key_id": aws.StringValue(dataCaptureConfig.KmsKeyId), "capture_options": flattenSagemakerCaptureOptions(dataCaptureConfig.CaptureOptions), //"capture_content_type_header": flattenSagemakerCaptureContentTypeHeader(dataCaptureConfig.CaptureContentTypeHeader), @@ -451,26 +438,12 @@ func flattenSagemakerCaptureOptions(list []*sagemaker.CaptureOption) []map[strin func expandSagemakerCaptureContentTypeHeader(m map[string]interface{}) *sagemaker.CaptureContentTypeHeader { c := &sagemaker.CaptureContentTypeHeader{} - if v, ok := m["csv_content_types"]; ok && len(v.([]interface{})) > 0 { - contentTypes := make([]*string, 0, len(v.([]interface{}))) - - for _, raw := range v.([]interface{}) { - sRaw := raw.(map[string]interface{})["content_type"] - contentTypes = append(contentTypes, aws.String(sRaw.(string))) - } - - c.CsvContentTypes = contentTypes + if v, ok := m["csv_content_types"].(*schema.Set); ok && v.Len() > 0 { + c.CsvContentTypes = expandStringSet(v) } - if v, ok := m["json_content_types"]; ok && len(v.([]interface{})) > 0 { - contentTypes := make([]*string, 0, len(v.([]interface{}))) - - for _, raw := range v.([]interface{}) { - sRaw := raw.(map[string]interface{})["content_type"] - contentTypes = append(contentTypes, aws.String(sRaw.(string))) - } - - c.JsonContentTypes = contentTypes + if v, ok := m["json_content_types"].(*schema.Set); ok && v.Len() > 0 { + c.JsonContentTypes = expandStringSet(v) } return c @@ -484,29 +457,11 @@ func flattenSagemakerCaptureContentTypeHeader(contentTypeHeader *sagemaker.Captu l := make(map[string]interface{}) if contentTypeHeader.CsvContentTypes != nil { - csvTypes := make([]map[string]interface{}, 0, len(contentTypeHeader.CsvContentTypes)) - - for _, csvType := range contentTypeHeader.CsvContentTypes { - m := map[string]interface{}{ - "content_type": aws.StringValue(csvType), - } - csvTypes = append(csvTypes, m) - } - - l["csv_content_types"] = csvTypes + l["csv_content_types"] = flattenStringSet(contentTypeHeader.CsvContentTypes) } if contentTypeHeader.JsonContentTypes != nil { - jsonTypes := make([]map[string]interface{}, 0, len(contentTypeHeader.JsonContentTypes)) - - for _, jsonType := range contentTypeHeader.JsonContentTypes { - m := map[string]interface{}{ - "content_type": aws.StringValue(jsonType), - } - jsonTypes = append(jsonTypes, m) - } - - l["json_content_types"] = jsonTypes + l["json_content_types"] = flattenStringSet(contentTypeHeader.JsonContentTypes) } return []map[string]interface{}{l} From d5b7d65a769ed14d884952cfa6e756bdb3caa9aa Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:09:52 +0200 Subject: [PATCH 03/17] change docs --- .../r/sagemaker_endpoint_configuration.html.markdown | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/website/docs/r/sagemaker_endpoint_configuration.html.markdown b/website/docs/r/sagemaker_endpoint_configuration.html.markdown index 9575a13e5f5..0ae3c0633e2 100644 --- a/website/docs/r/sagemaker_endpoint_configuration.html.markdown +++ b/website/docs/r/sagemaker_endpoint_configuration.html.markdown @@ -54,7 +54,7 @@ The `production_variants` block supports: The `data_capture_config` block supports: * `initial_sampling_percentage` - (Required) Portion of data to capture. Should be between 0 and 100. -* `destination_url` - (Required) The URL for S3 location where the captured data is stored. +* `destination_s3_uri` - (Required) The URL for S3 location where the captured data is stored. * `capture_options` - (Required) Specifies what data to capture. Fields are documented below. * `kms_key_id` - (Optional) Amazon Resource Name (ARN) of a AWS Key Management Service key that Amazon SageMaker uses to encrypt the captured data on Amazon S3. * `enable_capture` - (Optional) Flag to enable data capture. Defaults to `false`. @@ -66,12 +66,8 @@ The `capture_options` block supports: The `capture_content_type_header` block supports: -* `csv_content_types` - (Optional) The CSV content type headers to capture. Fields are documented below. -* `json_content_types` - (Optional) The JSON content type headers to capture. Fields are documented below. - -The `csv_content_types` and `json_content_types` block both support: - -* `content_type` - (Required) The value of the content type header. +* `csv_content_types` - (Optional) The CSV content type headers to capture. +* `json_content_types` - (Optional) The JSON content type headers to capture. ## Attributes Reference From 63907a871622cbef28a7649cf0d5e10cdf916540 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:35:48 +0200 Subject: [PATCH 04/17] refactor tests --- ...s_sagemaker_endpoint_configuration_test.go | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index cf030abff98..9f086ab1fc6 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -5,12 +5,12 @@ import ( "log" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) func init() { @@ -200,7 +200,6 @@ func TestAccAWSSagemakerEndpointConfiguration_Tags(t *testing.T) { func TestAccAWSSagemakerEndpointConfiguration_DataCaptureConfig(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - dataDestinationBucketName := fmt.Sprintf("bucket-%s", rName) resourceName := "aws_sagemaker_endpoint_configuration.foo" resource.ParallelTest(t, resource.TestCase{ @@ -209,15 +208,16 @@ func TestAccAWSSagemakerEndpointConfiguration_DataCaptureConfig(t *testing.T) { CheckDestroy: testAccCheckSagemakerEndpointConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccSagemakerEndpointConfigurationConfig_DataCaptureConfig(rName, dataDestinationBucketName), + Config: testAccSagemakerEndpointConfigurationDataCaptureConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.enable_capture", "true"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.initial_sampling_percentage", "50"), - resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_url", fmt.Sprintf("s3://%s/", dataDestinationBucketName)), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_s3_uri", fmt.Sprintf("s3://%s/", dataDestinationBucketName)), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.0.capture_mode", "Input"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.1.capture_mode", "Output"), - resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_content_type_header.0.json_content_types.0.content_type", "application/json"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_content_type_header.0.json_content_types.#", "1"), + tfawsresource.TestCheckTypeSetElemAttr(resourceName, "data_capture_config.0.capture_content_type_header.0.json_content_types.*", "application/json"), ), }, { @@ -429,41 +429,42 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { `, rName) } -func testAccSagemakerEndpointConfigurationConfig_DataCaptureConfig(rName string, bucket string) string { +func testAccSagemakerEndpointConfigurationDataCaptureConfig(rName string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_s3_bucket" "foo" { - bucket = %q +resource "aws_s3_bucket" "test" { + bucket = %[1]q acl = "private" force_destroy = true } -resource "aws_sagemaker_endpoint_configuration" "foo" { - name = %q - - production_variants { - variant_name = "variant-1" - model_name = "${aws_sagemaker_model.foo.name}" - initial_instance_count = 2 - instance_type = "ml.t2.medium" - initial_variant_weight = 1 - } +resource "aws_sagemaker_endpoint_configuration" "test" { + name = %[1]q - data_capture_config { - enable_capture = true - initial_sampling_percentage = 50 - destination_url = "s3://${aws_s3_bucket.foo.bucket}/" - capture_options { - capture_mode = "Input" - } - capture_options { - capture_mode = "Output" - } - capture_content_type_header { - json_content_types { - content_type = "application/json" - } - } + production_variants { + variant_name = "variant-1" + model_name = aws_sagemaker_model.foo.name + initial_instance_count = 2 + instance_type = "ml.t2.medium" + initial_variant_weight = 1 + } + + data_capture_config { + enable_capture = true + initial_sampling_percentage = 50 + destination_s3_uri = "s3://${aws_s3_bucket.foo.bucket}/" + + capture_options { + capture_mode = "Input" + } + + capture_options { + capture_mode = "Output" + } + + capture_content_type_header { + json_content_types = ["application/json"] } + } } -`, bucket, rName) +`, rName) } From 905ab7e74e1de3c4e06c3d814ef66623eb424ac2 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:36:24 +0200 Subject: [PATCH 05/17] test refactor --- aws/resource_aws_sagemaker_endpoint_configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 9f086ab1fc6..d88eb80afb5 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -213,7 +213,7 @@ func TestAccAWSSagemakerEndpointConfiguration_DataCaptureConfig(t *testing.T) { testAccCheckSagemakerEndpointConfigurationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.enable_capture", "true"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.initial_sampling_percentage", "50"), - resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_s3_uri", fmt.Sprintf("s3://%s/", dataDestinationBucketName)), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_s3_uri", fmt.Sprintf("s3://%s/", rName)), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.0.capture_mode", "Input"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_options.1.capture_mode", "Output"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.capture_content_type_header.0.json_content_types.#", "1"), From 87f54b518b4309d3211218764cc4b845a92028bc Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:37:07 +0200 Subject: [PATCH 06/17] correct bucket reference --- aws/resource_aws_sagemaker_endpoint_configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index d88eb80afb5..53857c3cb67 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -451,7 +451,7 @@ resource "aws_sagemaker_endpoint_configuration" "test" { data_capture_config { enable_capture = true initial_sampling_percentage = 50 - destination_s3_uri = "s3://${aws_s3_bucket.foo.bucket}/" + destination_s3_uri = "s3://${aws_s3_bucket.test.bucket}/" capture_options { capture_mode = "Input" From 466576267fff7841c6aff94e87716f0ed55d7946 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:43:34 +0200 Subject: [PATCH 07/17] use shorthand validation logic --- ...ce_aws_sagemaker_endpoint_configuration.go | 27 ++++++++---- aws/validators.go | 42 ------------------- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index afd23746d8c..bc318482412 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "regexp" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sagemaker" @@ -114,11 +115,13 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { }, "destination_s3_uri": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateSagemakerDataCaptureDestinationUrl, - }, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(` ^(https|s3)://([^/])/?(.*)$`), ""), + validation.StringLenBetween(1, 512), + )}, "kms_key_id": { Type: schema.TypeString, @@ -157,8 +160,11 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { MinItems: 1, MaxItems: 10, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateSagemakerCsvContentType, + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`), ""), + validation.StringLenBetween(1, 256), + ), }, Optional: true, ForceNew: true, @@ -168,8 +174,11 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { MinItems: 1, MaxItems: 10, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateSagemakerJsonContentType, + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`), ""), + validation.StringLenBetween(1, 256), + ), }, Optional: true, ForceNew: true, diff --git a/aws/validators.go b/aws/validators.go index 4399fe5500f..d877c5c39b3 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -508,48 +508,6 @@ func validateSagemakerModelDataUrl(v interface{}, k string) (ws []string, errors return } -func validateSagemakerDataCaptureDestinationUrl(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^(https|s3)://([^/]+)/?(.*)$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q must be a valid path: %q", - k, value)) - } - if len(value) > 512 { - errors = append(errors, fmt.Errorf( - "%q cannot be longer than 512 characters: %q", k, value)) - } - return -} - -func validateSagemakerCsvContentType(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q must be a valid content type: %q", - k, value)) - } - if len(value) < 1 { - errors = append(errors, fmt.Errorf( - "%q cannot be shorter than 1 character: %q", k, value)) - } - return -} - -func validateSagemakerJsonContentType(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*\/[a-zA-Z0-9](-*[a-zA-Z0-9.])*`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q must be a valid content type: %q", - k, value)) - } - if len(value) < 1 { - errors = append(errors, fmt.Errorf( - "%q cannot be shorter than 1 character: %q", k, value)) - } - return -} - func validateCloudWatchDashboardName(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if len(value) > 255 { From bb26bfb15a124967028265c3ce0eecb0425ce10d Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:45:23 +0200 Subject: [PATCH 08/17] extraneous space --- aws/resource_aws_sagemaker_endpoint_configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index bc318482412..857cdf5304d 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -119,7 +119,7 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { Required: true, ForceNew: true, ValidateFunc: validation.All( - validation.StringMatch(regexp.MustCompile(` ^(https|s3)://([^/])/?(.*)$`), ""), + validation.StringMatch(regexp.MustCompile(`^(https|s3)://([^/])/?(.*)$`), ""), validation.StringLenBetween(1, 512), )}, From 5d8d6dc57a09cfb90b1e895030c39a4e835d7755 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:48:08 +0200 Subject: [PATCH 09/17] add plan time validation for `instance_type` and `accelerator_type` --- ...esource_aws_sagemaker_endpoint_configuration.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index 857cdf5304d..3e4f9006bd2 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -63,9 +63,10 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { }, "instance_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(sagemaker.ProductionVariantInstanceType_Values(), false), }, "initial_variant_weight": { @@ -77,9 +78,10 @@ func resourceAwsSagemakerEndpointConfiguration() *schema.Resource { }, "accelerator_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(sagemaker.ProductionVariantAcceleratorType_Values(), false), }, }, }, From 59440f2b3f5e166f147ff1d3ad25a9a44911398a Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:49:38 +0200 Subject: [PATCH 10/17] fix resource naming --- aws/resource_aws_sagemaker_endpoint_configuration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 53857c3cb67..8801152461f 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -198,9 +198,9 @@ func TestAccAWSSagemakerEndpointConfiguration_Tags(t *testing.T) { }) } -func TestAccAWSSagemakerEndpointConfiguration_DataCaptureConfig(t *testing.T) { +func TestAccAWSSagemakerEndpointConfiguration_dataCaptureConfig(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From d203dc352c2bf925f2d8382fe4ac40a81a9e4696 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 17:56:52 +0200 Subject: [PATCH 11/17] validate more specific error on read + remove commented out code + tests --- ...ce_aws_sagemaker_endpoint_configuration.go | 7 ++--- ...s_sagemaker_endpoint_configuration_test.go | 31 ++++++++++++++++--- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration.go b/aws/resource_aws_sagemaker_endpoint_configuration.go index 3e4f9006bd2..3f2cd84adb8 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration.go @@ -242,7 +242,7 @@ func resourceAwsSagemakerEndpointConfigurationRead(d *schema.ResourceData, meta endpointConfig, err := conn.DescribeEndpointConfig(request) if err != nil { - if isAWSErr(err, "ValidationException", "") { + if isAWSErr(err, "ValidationException", "Could not find endpoint configuration") { log.Printf("[INFO] unable to find the SageMaker Endpoint Configuration resource and therefore it is removed from the state: %s", d.Id()) d.SetId("") return nil @@ -396,12 +396,9 @@ func flattenSagemakerDataCaptureConfig(dataCaptureConfig *sagemaker.DataCaptureC } cfg := map[string]interface{}{ - //"enable_capture": aws.BoolValue(dataCaptureConfig.EnableCapture), "initial_sampling_percentage": aws.Int64Value(dataCaptureConfig.InitialSamplingPercentage), "destination_s3_uri": aws.StringValue(dataCaptureConfig.DestinationS3Uri), - //"kms_key_id": aws.StringValue(dataCaptureConfig.KmsKeyId), - "capture_options": flattenSagemakerCaptureOptions(dataCaptureConfig.CaptureOptions), - //"capture_content_type_header": flattenSagemakerCaptureContentTypeHeader(dataCaptureConfig.CaptureContentTypeHeader), + "capture_options": flattenSagemakerCaptureOptions(dataCaptureConfig.CaptureOptions), } if dataCaptureConfig.EnableCapture != nil { diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 8801152461f..d7b0107adbf 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -88,7 +88,7 @@ func TestAccAWSSagemakerEndpointConfiguration_basic(t *testing.T) { }) } -func TestAccAWSSagemakerEndpointConfiguration_ProductionVariants_InitialVariantWeight(t *testing.T) { +func TestAccAWSSagemakerEndpointConfiguration_productionVariants_InitialVariantWeight(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_sagemaker_endpoint_configuration.foo" @@ -114,7 +114,7 @@ func TestAccAWSSagemakerEndpointConfiguration_ProductionVariants_InitialVariantW }) } -func TestAccAWSSagemakerEndpointConfiguration_ProductionVariants_AcceleratorType(t *testing.T) { +func TestAccAWSSagemakerEndpointConfiguration_productionVariants_AcceleratorType(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_sagemaker_endpoint_configuration.foo" @@ -139,7 +139,7 @@ func TestAccAWSSagemakerEndpointConfiguration_ProductionVariants_AcceleratorType }) } -func TestAccAWSSagemakerEndpointConfiguration_KmsKeyId(t *testing.T) { +func TestAccAWSSagemakerEndpointConfiguration_kmsKeyId(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_sagemaker_endpoint_configuration.foo" @@ -152,7 +152,7 @@ func TestAccAWSSagemakerEndpointConfiguration_KmsKeyId(t *testing.T) { Config: testAccSagemakerEndpointConfiguration_Config_KmsKeyId(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "kms_key_arn"), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_arn", "aws_kms_key.foo", "arn"), ), }, { @@ -164,7 +164,7 @@ func TestAccAWSSagemakerEndpointConfiguration_KmsKeyId(t *testing.T) { }) } -func TestAccAWSSagemakerEndpointConfiguration_Tags(t *testing.T) { +func TestAccAWSSagemakerEndpointConfiguration_tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_sagemaker_endpoint_configuration.foo" @@ -229,6 +229,27 @@ func TestAccAWSSagemakerEndpointConfiguration_dataCaptureConfig(t *testing.T) { }) } +func TestAccAWSSagemakerEndpointConfiguration_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_endpoint_configuration.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSagemakerEndpointConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSagemakerEndpointConfigurationConfig_Basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSagemakerEndpointConfigurationExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSagemakerEndpointConfiguration(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckSagemakerEndpointConfigurationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).sagemakerconn From 99a6b3d3776bd9d182cafa742eeb5f75e2ac8b12 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 18:00:24 +0200 Subject: [PATCH 12/17] tags test --- ...s_sagemaker_endpoint_configuration_test.go | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index d7b0107adbf..61f99825257 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -174,25 +174,34 @@ func TestAccAWSSagemakerEndpointConfiguration_tags(t *testing.T) { CheckDestroy: testAccCheckSagemakerEndpointConfigurationDestroy, Steps: []resource.TestStep{ { - Config: testAccSagemakerEndpointConfigurationConfig_Tags(rName), + Config: testAccSagemakerEndpointConfigurationConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { - Config: testAccSagemakerEndpointConfigurationConfig_Tags_Update(rName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccSagemakerEndpointConfigurationConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.bar", "baz"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + Config: testAccSagemakerEndpointConfigurationConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSagemakerEndpointConfigurationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), }, }, }) @@ -410,10 +419,10 @@ resource "aws_kms_key" "foo" { `, rName, rName) } -func testAccSagemakerEndpointConfigurationConfig_Tags(rName string) string { +func testAccSagemakerEndpointConfigurationConfigTags1(rName, tagKey1, tagValue1 string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { - name = %q +resource "aws_sagemaker_endpoint_configuration" "test" { + name = %[1]q production_variants { variant_name = "variant-1" @@ -424,16 +433,16 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { } tags = { - foo = "bar" + %[2]q = %[3]q } } -`, rName) +`, rName, tagKey1, tagValue1) } -func testAccSagemakerEndpointConfigurationConfig_Tags_Update(rName string) string { +func testAccSagemakerEndpointConfigurationConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { - name = %q +resource "aws_sagemaker_endpoint_configuration" "test" { + name = %[1]q production_variants { variant_name = "variant-1" @@ -444,10 +453,11 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { } tags = { - bar = "baz" + %[2]q = %[3]q + %[4]q = %[5]q } } -`, rName) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) } func testAccSagemakerEndpointConfigurationDataCaptureConfig(rName string) string { From 780a6d740d4a13d2821f1ece37b69022973a1c00 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 18:04:31 +0200 Subject: [PATCH 13/17] use test instead of foo --- ...s_sagemaker_endpoint_configuration_test.go | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 61f99825257..935d374b24d 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -59,7 +59,7 @@ func testSweepSagemakerEndpointConfigurations(region string) error { func TestAccAWSSagemakerEndpointConfiguration_basic(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -90,7 +90,7 @@ func TestAccAWSSagemakerEndpointConfiguration_basic(t *testing.T) { func TestAccAWSSagemakerEndpointConfiguration_productionVariants_InitialVariantWeight(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -116,7 +116,7 @@ func TestAccAWSSagemakerEndpointConfiguration_productionVariants_InitialVariantW func TestAccAWSSagemakerEndpointConfiguration_productionVariants_AcceleratorType(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -141,7 +141,7 @@ func TestAccAWSSagemakerEndpointConfiguration_productionVariants_AcceleratorType func TestAccAWSSagemakerEndpointConfiguration_kmsKeyId(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -152,7 +152,7 @@ func TestAccAWSSagemakerEndpointConfiguration_kmsKeyId(t *testing.T) { Config: testAccSagemakerEndpointConfiguration_Config_KmsKeyId(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "kms_key_arn", "aws_kms_key.foo", "arn"), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_arn", "aws_kms_key.test", "arn"), ), }, { @@ -166,7 +166,7 @@ func TestAccAWSSagemakerEndpointConfiguration_kmsKeyId(t *testing.T) { func TestAccAWSSagemakerEndpointConfiguration_tags(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -240,7 +240,7 @@ func TestAccAWSSagemakerEndpointConfiguration_dataCaptureConfig(t *testing.T) { func TestAccAWSSagemakerEndpointConfiguration_disappears(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_sagemaker_endpoint_configuration.foo" + resourceName := "aws_sagemaker_endpoint_configuration.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -313,17 +313,17 @@ func testAccCheckSagemakerEndpointConfigurationExists(n string) resource.TestChe func testAccSagemakerEndpointConfigurationConfig_Base(rName string) string { return fmt.Sprintf(` -resource "aws_sagemaker_model" "foo" { - name = %q - execution_role_arn = aws_iam_role.foo.arn +resource "aws_sagemaker_model" "test" { + name = %[1]q + execution_role_arn = aws_iam_role.test.arn primary_container { image = "174872318107.dkr.ecr.us-west-2.amazonaws.com/kmeans:1" } } -resource "aws_iam_role" "foo" { - name = %q +resource "aws_iam_role" "test" { + name = %[1]q path = "/" assume_role_policy = data.aws_iam_policy_document.assume_role.json } @@ -338,17 +338,17 @@ data "aws_iam_policy_document" "assume_role" { } } } -`, rName, rName) +`, rName) } func testAccSagemakerEndpointConfigurationConfig_Basic(rName string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { +resource "aws_sagemaker_endpoint_configuration" "test" { name = %q production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 2 instance_type = "ml.t2.medium" initial_variant_weight = 1 @@ -359,19 +359,19 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { func testAccSagemakerEndpointConfigurationConfig_ProductionVariants_InitialVariantWeight(rName string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { +resource "aws_sagemaker_endpoint_configuration" "test" { name = %q production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 1 instance_type = "ml.t2.medium" } production_variants { variant_name = "variant-2" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 1 instance_type = "ml.t2.medium" initial_variant_weight = 0.5 @@ -382,12 +382,12 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { func testAccSagemakerEndpointConfigurationConfig_ProductionVariant_AcceleratorType(rName string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { +resource "aws_sagemaker_endpoint_configuration" "test" { name = %q production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 2 instance_type = "ml.t2.medium" accelerator_type = "ml.eia1.medium" @@ -399,20 +399,20 @@ resource "aws_sagemaker_endpoint_configuration" "foo" { func testAccSagemakerEndpointConfiguration_Config_KmsKeyId(rName string) string { return testAccSagemakerEndpointConfigurationConfig_Base(rName) + fmt.Sprintf(` -resource "aws_sagemaker_endpoint_configuration" "foo" { +resource "aws_sagemaker_endpoint_configuration" "test" { name = %q - kms_key_arn = aws_kms_key.foo.arn + kms_key_arn = aws_kms_key.test.arn production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 1 instance_type = "ml.t2.medium" initial_variant_weight = 1 } } -resource "aws_kms_key" "foo" { +resource "aws_kms_key" "test" { description = %q deletion_window_in_days = 10 } @@ -426,7 +426,7 @@ resource "aws_sagemaker_endpoint_configuration" "test" { production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 1 instance_type = "ml.t2.medium" initial_variant_weight = 1 @@ -446,7 +446,7 @@ resource "aws_sagemaker_endpoint_configuration" "test" { production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 1 instance_type = "ml.t2.medium" initial_variant_weight = 1 @@ -473,7 +473,7 @@ resource "aws_sagemaker_endpoint_configuration" "test" { production_variants { variant_name = "variant-1" - model_name = aws_sagemaker_model.foo.name + model_name = aws_sagemaker_model.test.name initial_instance_count = 2 instance_type = "ml.t2.medium" initial_variant_weight = 1 From 07e572467ec31cf87c443d5a2cd04d18ed34178b Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Wed, 28 Oct 2020 18:14:07 +0200 Subject: [PATCH 14/17] test fmt + check --- ...ws_sagemaker_endpoint_configuration_test.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 935d374b24d..058ce4069e7 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -77,6 +77,7 @@ func TestAccAWSSagemakerEndpointConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "production_variants.0.initial_instance_count", "2"), resource.TestCheckResourceAttr(resourceName, "production_variants.0.instance_type", "ml.t2.medium"), resource.TestCheckResourceAttr(resourceName, "production_variants.0.initial_variant_weight", "1"), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.#", "0"), ), }, { @@ -220,6 +221,7 @@ func TestAccAWSSagemakerEndpointConfiguration_dataCaptureConfig(t *testing.T) { Config: testAccSagemakerEndpointConfigurationDataCaptureConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckSagemakerEndpointConfigurationExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "data_capture_config.#", "1"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.enable_capture", "true"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.initial_sampling_percentage", "50"), resource.TestCheckResourceAttr(resourceName, "data_capture_config.0.destination_s3_uri", fmt.Sprintf("s3://%s/", rName)), @@ -453,8 +455,8 @@ resource "aws_sagemaker_endpoint_configuration" "test" { } tags = { - %[2]q = %[3]q - %[4]q = %[5]q + %[2]q = %[3]q + %[4]q = %[5]q } } `, rName, tagKey1, tagValue1, tagKey2, tagValue2) @@ -482,16 +484,16 @@ resource "aws_sagemaker_endpoint_configuration" "test" { data_capture_config { enable_capture = true initial_sampling_percentage = 50 - destination_s3_uri = "s3://${aws_s3_bucket.test.bucket}/" - + destination_s3_uri = "s3://${aws_s3_bucket.test.bucket}/" + capture_options { capture_mode = "Input" - } - + } + capture_options { capture_mode = "Output" - } - + } + capture_content_type_header { json_content_types = ["application/json"] } From e8ebae3a723c951ae7ddc5392835984db92a2dc6 Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Fri, 30 Oct 2020 08:43:25 +0200 Subject: [PATCH 15/17] Apply suggestions from code review Co-authored-by: Dirk Avery <31492422+YakDriver@users.noreply.github.com> --- ..._aws_sagemaker_endpoint_configuration_test.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 058ce4069e7..266b6d18867 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -314,13 +314,25 @@ func testAccCheckSagemakerEndpointConfigurationExists(n string) resource.TestChe } func testAccSagemakerEndpointConfigurationConfig_Base(rName string) string { + imageID := "" + switch testAccGetRegion() { + case "us-west-2": + imageID = "174872318107" + case "us-west-1": + imageID = "632365934929" + case "us-east-1": + imageID = "382416733822" + case "us-gov-west-1": + imageID = "226302683700" + } + registryPath = fmt.Sprintf("%s.dkr.ecr.%s.%s/kmeans:1", imageID, testAccGetRegion(), testAccGetPartitionDNSSuffix()) return fmt.Sprintf(` resource "aws_sagemaker_model" "test" { name = %[1]q execution_role_arn = aws_iam_role.test.arn primary_container { - image = "174872318107.dkr.ecr.us-west-2.amazonaws.com/kmeans:1" + image = %[2]q } } @@ -340,7 +352,7 @@ data "aws_iam_policy_document" "assume_role" { } } } -`, rName) +`, rName, registryPath) } func testAccSagemakerEndpointConfigurationConfig_Basic(rName string) string { From f3c7f50b259e3c35b26c0a58528f4af04c21b074 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 30 Oct 2020 08:50:56 +0200 Subject: [PATCH 16/17] variable assignment --- aws/resource_aws_sagemaker_endpoint_configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 266b6d18867..202a2e7a7c8 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -325,7 +325,7 @@ func testAccSagemakerEndpointConfigurationConfig_Base(rName string) string { case "us-gov-west-1": imageID = "226302683700" } - registryPath = fmt.Sprintf("%s.dkr.ecr.%s.%s/kmeans:1", imageID, testAccGetRegion(), testAccGetPartitionDNSSuffix()) + registryPath := fmt.Sprintf("%s.dkr.ecr.%s.%s/kmeans:1", imageID, testAccGetRegion(), testAccGetPartitionDNSSuffix()) return fmt.Sprintf(` resource "aws_sagemaker_model" "test" { name = %[1]q From 82105d6b8c6554a83cfbd42b1153c19dc68d8410 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Sat, 31 Oct 2020 18:49:22 +0200 Subject: [PATCH 17/17] use sagemaker datasource --- ...s_sagemaker_endpoint_configuration_test.go | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/aws/resource_aws_sagemaker_endpoint_configuration_test.go b/aws/resource_aws_sagemaker_endpoint_configuration_test.go index 202a2e7a7c8..6ab06c32859 100644 --- a/aws/resource_aws_sagemaker_endpoint_configuration_test.go +++ b/aws/resource_aws_sagemaker_endpoint_configuration_test.go @@ -314,25 +314,17 @@ func testAccCheckSagemakerEndpointConfigurationExists(n string) resource.TestChe } func testAccSagemakerEndpointConfigurationConfig_Base(rName string) string { - imageID := "" - switch testAccGetRegion() { - case "us-west-2": - imageID = "174872318107" - case "us-west-1": - imageID = "632365934929" - case "us-east-1": - imageID = "382416733822" - case "us-gov-west-1": - imageID = "226302683700" - } - registryPath := fmt.Sprintf("%s.dkr.ecr.%s.%s/kmeans:1", imageID, testAccGetRegion(), testAccGetPartitionDNSSuffix()) return fmt.Sprintf(` +data "aws_sagemaker_prebuilt_ecr_image" "test" { + repository_name = "kmeans" +} + resource "aws_sagemaker_model" "test" { name = %[1]q execution_role_arn = aws_iam_role.test.arn primary_container { - image = %[2]q + image = data.aws_sagemaker_prebuilt_ecr_image.test.registry_path } } @@ -352,7 +344,7 @@ data "aws_iam_policy_document" "assume_role" { } } } -`, rName, registryPath) +`, rName) } func testAccSagemakerEndpointConfigurationConfig_Basic(rName string) string {