From 49e22fb6a8043038ba4a7d699effb749ea9bbdf2 Mon Sep 17 00:00:00 2001 From: Tyler Horvath Date: Wed, 22 Feb 2023 21:41:42 -0700 Subject: [PATCH 1/5] feat: adding appsync opensearch config capabilities --- .changelog/29578.txt | 3 + internal/service/appsync/datasource.go | 73 ++++++++- internal/service/appsync/datasource_test.go | 165 ++++++++++++++++++++ 3 files changed, 236 insertions(+), 5 deletions(-) create mode 100644 .changelog/29578.txt diff --git a/.changelog/29578.txt b/.changelog/29578.txt new file mode 100644 index 00000000000..f72e45f4a6f --- /dev/null +++ b/.changelog/29578.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_appsync_datasource: Add `opensearchservice_config` configuration block for OpenSearch Service data sources +``` \ No newline at end of file diff --git a/internal/service/appsync/datasource.go b/internal/service/appsync/datasource.go index 701fe88f892..a627a7772df 100644 --- a/internal/service/appsync/datasource.go +++ b/internal/service/appsync/datasource.go @@ -96,7 +96,7 @@ func ResourceDataSource() *schema.Resource { }, }, }, - ConflictsWith: []string{"elasticsearch_config", "http_config", "lambda_config", "relational_database_config"}, + ConflictsWith: []string{"elasticsearch_config", "http_config", "lambda_config", "relational_database_config", "opensearchservice_config"}, }, "elasticsearch_config": { Type: schema.TypeList, @@ -115,7 +115,26 @@ func ResourceDataSource() *schema.Resource { }, }, }, - ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config"}, + ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config", "opensearchservice_config"}, + }, + "opensearchservice_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "endpoint": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config", "elasticsearch_config"}, }, "http_config": { Type: schema.TypeList, @@ -161,7 +180,7 @@ func ResourceDataSource() *schema.Resource { }, }, }, - ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "lambda_config", "relational_database_config"}, + ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "lambda_config", "relational_database_config"}, }, "lambda_config": { Type: schema.TypeList, @@ -176,7 +195,7 @@ func ResourceDataSource() *schema.Resource { }, }, }, - ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "http_config", "relational_database_config"}, + ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "http_config", "relational_database_config"}, }, "relational_database_config": { Type: schema.TypeList, @@ -223,7 +242,7 @@ func ResourceDataSource() *schema.Resource { }, }, }, - ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "http_config", "lambda_config"}, + ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "http_config", "lambda_config"}, }, "service_role_arn": { Type: schema.TypeString, @@ -261,6 +280,10 @@ func resourceDataSourceCreate(ctx context.Context, d *schema.ResourceData, meta input.ElasticsearchConfig = expandElasticsearchDataSourceConfig(v.([]interface{}), region) } + if v, ok := d.GetOk("opensearchservice_config"); ok { + input.OpenSearchServiceConfig = expandOpenSearchServiceDataSourceConfig(v.([]interface{}), region) + } + if v, ok := d.GetOk("http_config"); ok { input.HttpConfig = expandHTTPDataSourceConfig(v.([]interface{})) } @@ -326,6 +349,10 @@ func resourceDataSourceRead(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "setting elasticsearch_config: %s", err) } + if err := d.Set("opensearchservice_config", flattenOpenSearchServiceDataSourceConfig(dataSource.OpenSearchServiceConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting opensearchservice_config: %s", err) + } + if err := d.Set("http_config", flattenHTTPDataSourceConfig(dataSource.HttpConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting http_config: %s", err) } @@ -374,6 +401,10 @@ func resourceDataSourceUpdate(ctx context.Context, d *schema.ResourceData, meta input.ElasticsearchConfig = expandElasticsearchDataSourceConfig(v.([]interface{}), region) } + if v, ok := d.GetOk("opensearchservice_config"); ok { + input.OpenSearchServiceConfig = expandOpenSearchServiceDataSourceConfig(v.([]interface{}), region) + } + if v, ok := d.GetOk("http_config"); ok { input.HttpConfig = expandHTTPDataSourceConfig(v.([]interface{})) } @@ -553,6 +584,25 @@ func expandElasticsearchDataSourceConfig(l []interface{}, currentRegion string) return result } +func expandOpenSearchServiceDataSourceConfig(l []interface{}, currentRegion string) *appsync.OpenSearchServiceDataSourceConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + configured := l[0].(map[string]interface{}) + + result := &appsync.OpenSearchServiceDataSourceConfig{ + AwsRegion: aws.String(currentRegion), + Endpoint: aws.String(configured["endpoint"].(string)), + } + + if v, ok := configured["region"]; ok && v.(string) != "" { + result.AwsRegion = aws.String(v.(string)) + } + + return result +} + func flattenElasticsearchDataSourceConfig(config *appsync.ElasticsearchDataSourceConfig) []map[string]interface{} { if config == nil { return nil @@ -566,6 +616,19 @@ func flattenElasticsearchDataSourceConfig(config *appsync.ElasticsearchDataSourc return []map[string]interface{}{result} } +func flattenOpenSearchServiceDataSourceConfig(config *appsync.OpenSearchServiceDataSourceConfig) []map[string]interface{} { + if config == nil { + return nil + } + + result := map[string]interface{}{ + "endpoint": aws.StringValue(config.Endpoint), + "region": aws.StringValue(config.AwsRegion), + } + + return []map[string]interface{}{result} +} + func expandHTTPDataSourceConfig(l []interface{}) *appsync.HttpDataSourceConfig { if len(l) == 0 || l[0] == nil { return nil diff --git a/internal/service/appsync/datasource_test.go b/internal/service/appsync/datasource_test.go index d351d567ec2..547eaa36f77 100644 --- a/internal/service/appsync/datasource_test.go +++ b/internal/service/appsync/datasource_test.go @@ -36,6 +36,7 @@ func testAccDataSource_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "dynamodb_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "elasticsearch_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "http_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "lambda_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "relational_database_config.#", "0"), @@ -195,6 +196,43 @@ func TestAccAppSyncDataSource_Elasticsearch_region(t *testing.T) { }) } +func TestAccAppSyncDataSource_OpenSearchService_region(t *testing.T) { + ctx := acctest.Context(t) + rName := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) + resourceName := "aws_appsync_datasource.test" + + // Keep this test Parallel as it takes considerably longer to run than any non-OpenSearchService tests. + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(appsync.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDataSourceDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceConfig_openSearchServiceRegion(rName, acctest.Region()), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistsDataSource(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.0.region", acctest.Region()), + ), + }, + { + Config: testAccDataSourceConfig_typeOpenSearchService(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistsDataSource(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.0.region", acctest.Region()), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccDataSource_HTTP_endpoint(t *testing.T) { ctx := acctest.Context(t) rName := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) @@ -328,6 +366,39 @@ func TestAccAppSyncDataSource_Type_elasticSearch(t *testing.T) { }) } +func TestAccAppSyncDataSource_Type_openSearchService(t *testing.T) { + ctx := acctest.Context(t) + rName := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_appsync_datasource.test" + + // Keep this test Parallel as it takes considerably longer to run than any non-OpenSearchService tests. + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(appsync.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDataSourceDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceConfig_typeOpenSearchService(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistsDataSource(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "opensearchservice_config.0.endpoint"), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.0.region", acctest.Region()), + resource.TestCheckResourceAttrPair(resourceName, "service_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "type", "AMAZON_OPENSEARCH_SERVICE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccDataSource_Type_http(t *testing.T) { ctx := acctest.Context(t) rName := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) @@ -679,6 +750,59 @@ EOF `, rName, rName) } +func testAccDataSourceConfig_Base_openSearchService(rName string) string { + return fmt.Sprintf(` +resource "aws_opensearch_domain" "test" { + domain_name = %q + + ebs_options { + ebs_enabled = true + volume_size = 10 + } +} + +resource "aws_iam_role" "test" { + name = %q + + assume_role_policy = < Date: Thu, 20 Apr 2023 13:09:39 -0400 Subject: [PATCH 2/5] Tweak CHANGELOG entry. --- .changelog/29578.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/29578.txt b/.changelog/29578.txt index f72e45f4a6f..3f473e98ffd 100644 --- a/.changelog/29578.txt +++ b/.changelog/29578.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_appsync_datasource: Add `opensearchservice_config` configuration block for OpenSearch Service data sources +resource/aws_appsync_datasource: Add `opensearchservice_config` argument ``` \ No newline at end of file From 7e824804f952be8ffd162d892f9f6ead56eac94d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Apr 2023 13:14:48 -0400 Subject: [PATCH 3/5] r/aws_appsync_datasource: Document 'opensearchservice_config'. --- .../docs/r/appsync_datasource.html.markdown | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/website/docs/r/appsync_datasource.html.markdown b/website/docs/r/appsync_datasource.html.markdown index 989976ef045..2306eca5598 100644 --- a/website/docs/r/appsync_datasource.html.markdown +++ b/website/docs/r/appsync_datasource.html.markdown @@ -81,13 +81,14 @@ The following arguments are supported: * `name` - (Required) User-supplied name for the data source. * `type` - (Required) Type of the Data Source. Valid values: `AWS_LAMBDA`, `AMAZON_DYNAMODB`, `AMAZON_ELASTICSEARCH`, `HTTP`, `NONE`, `RELATIONAL_DATABASE`, `AMAZON_EVENTBRIDGE`. * `description` - (Optional) Description of the data source. -* `service_role_arn` - (Optional) IAM service role ARN for the data source. * `dynamodb_config` - (Optional) DynamoDB settings. See [below](#dynamodb_config) * `elasticsearch_config` - (Optional) Amazon Elasticsearch settings. See [below](#elasticsearch_config) +* `event_bridge_config` - (Optional) AWS EventBridge settings. See [below](#event_bridge_config) * `http_config` - (Optional) HTTP settings. See [below](#http_config) * `lambda_config` - (Optional) AWS Lambda settings. See [below](#lambda_config) +* `opensearchservice_config` - (Optional) Amazon OpenSearch Service settings. See [below](#opensearchservice_config) * `relational_database_config` (Optional) AWS RDS settings. See [Relational Database Config](#relational_database_config) -* `event_bridge_config` - (Optional) AWS EventBridge settings. See [below](#event_bridge_config) +* `service_role_arn` - (Optional) IAM service role ARN for the data source. ### dynamodb_config @@ -104,6 +105,12 @@ The following arguments are supported: * `endpoint` - (Required) HTTP endpoint of the Elasticsearch domain. * `region` - (Optional) AWS region of Elasticsearch domain. Defaults to current region. +### event_bridge_config + +The following arguments are supported: + +* `event_bus_arn` - (Required) ARN for the EventBridge bus. + ### http_config The following arguments are supported: @@ -125,13 +132,6 @@ The following arguments are supported: * `signing_region` - (Optional) Signing Amazon Web Services Region for IAM authorization. * `signing_service_name`- (Optional) Signing service name for IAM authorization. -### relational_database_config - -The following arguments are supported: - -* `http_endpoint_config` - (Required) Amazon RDS HTTP endpoint configuration. See [HTTP Endpoint Config](#http_endpoint_config). -* `source_type` - (Optional) Source type for the relational database. Valid values: `RDS_HTTP_ENDPOINT`. - #### http_endpoint_config The following arguments are supported: @@ -148,11 +148,19 @@ The following arguments are supported: * `function_arn` - (Required) ARN for the Lambda function. -### event_bridge_config +### opensearchservice_config The following arguments are supported: -* `event_bus_arn` - (Required) ARN for the EventBridge bus. +* `endpoint` - (Required) HTTP endpoint of the OpenSearch domain. +* `region` - (Optional) AWS region of the OpenSearch domain. Defaults to current region. + +### relational_database_config + +The following arguments are supported: + +* `http_endpoint_config` - (Required) Amazon RDS HTTP endpoint configuration. See [HTTP Endpoint Config](#http_endpoint_config). +* `source_type` - (Optional) Source type for the relational database. Valid values: `RDS_HTTP_ENDPOINT`. ## Attributes Reference From e0846659b6f2c49d21a2df9ef02b6346f78cd274 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Apr 2023 13:20:31 -0400 Subject: [PATCH 4/5] r/aws_appsync_datasource: Alphabetize attributes. --- internal/service/appsync/datasource.go | 148 +++++++++--------- internal/service/appsync/datasource_test.go | 8 +- .../docs/r/appsync_datasource.html.markdown | 20 +-- 3 files changed, 88 insertions(+), 88 deletions(-) diff --git a/internal/service/appsync/datasource.go b/internal/service/appsync/datasource.go index c6c3549edd2..d2820da59c3 100644 --- a/internal/service/appsync/datasource.go +++ b/internal/service/appsync/datasource.go @@ -34,18 +34,9 @@ func ResourceDataSource() *schema.Resource { Type: schema.TypeString, Required: true, }, - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile(`[_A-Za-z][_0-9A-Za-z]*`), "must match [_A-Za-z][_0-9A-Za-z]*"), - }, - "type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice(appsync.DataSourceType_Values(), true), - StateFunc: func(v interface{}) string { - return strings.ToUpper(v.(string)) - }, + "arn": { + Type: schema.TypeString, + Computed: true, }, "description": { Type: schema.TypeString, @@ -57,23 +48,6 @@ func ResourceDataSource() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "region": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "table_name": { - Type: schema.TypeString, - Required: true, - }, - "use_caller_credentials": { - Type: schema.TypeBool, - Optional: true, - }, - "versioned": { - Type: schema.TypeBool, - Optional: true, - }, "delta_sync_config": { Type: schema.TypeList, Optional: true, @@ -95,6 +69,23 @@ func ResourceDataSource() *schema.Resource { }, }, }, + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "table_name": { + Type: schema.TypeString, + Required: true, + }, + "use_caller_credentials": { + Type: schema.TypeBool, + Optional: true, + }, + "versioned": { + Type: schema.TypeBool, + Optional: true, + }, }, }, ConflictsWith: []string{"elasticsearch_config", "http_config", "lambda_config", "relational_database_config", "opensearchservice_config"}, @@ -105,37 +96,33 @@ func ResourceDataSource() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "endpoint": { + Type: schema.TypeString, + Required: true, + }, "region": { Type: schema.TypeString, Optional: true, Computed: true, }, - "endpoint": { - Type: schema.TypeString, - Required: true, - }, }, }, ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config", "opensearchservice_config"}, }, - "opensearchservice_config": { + "event_bridge_config": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "region": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "endpoint": { - Type: schema.TypeString, - Required: true, + "event_bus_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, }, }, }, - ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config", "elasticsearch_config"}, + ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "http_config", "lambda_config", "relational_database_config"}, }, "http_config": { Type: schema.TypeList, @@ -143,10 +130,6 @@ func ResourceDataSource() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "endpoint": { - Type: schema.TypeString, - Required: true, - }, "authorization_config": { Type: schema.TypeList, Optional: true, @@ -179,6 +162,10 @@ func ResourceDataSource() *schema.Resource { }, }, }, + "endpoint": { + Type: schema.TypeString, + Required: true, + }, }, }, ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "lambda_config", "relational_database_config"}, @@ -198,28 +185,42 @@ func ResourceDataSource() *schema.Resource { }, ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "http_config", "relational_database_config"}, }, - "relational_database_config": { + "opensearchservice_config": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "source_type": { - Type: schema.TypeString, - Optional: true, - Default: appsync.RelationalDatabaseSourceTypeRdsHttpEndpoint, - ValidateFunc: validation.StringInSlice(appsync.RelationalDatabaseSourceType_Values(), true), + "endpoint": { + Type: schema.TypeString, + Required: true, }, + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + ConflictsWith: []string{"dynamodb_config", "http_config", "lambda_config", "elasticsearch_config"}, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`[_A-Za-z][_0-9A-Za-z]*`), "must match [_A-Za-z][_0-9A-Za-z]*"), + }, + "relational_database_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ "http_endpoint_config": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "db_cluster_identifier": { - Type: schema.TypeString, - Required: true, - }, "aws_secret_store_arn": { Type: schema.TypeString, Required: true, @@ -229,6 +230,10 @@ func ResourceDataSource() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "db_cluster_identifier": { + Type: schema.TypeString, + Required: true, + }, "region": { Type: schema.TypeString, Optional: true, @@ -241,33 +246,28 @@ func ResourceDataSource() *schema.Resource { }, }, }, - }, - }, - ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "http_config", "lambda_config"}, - }, - "event_bridge_config": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "event_bus_arn": { + "source_type": { Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, + Optional: true, + Default: appsync.RelationalDatabaseSourceTypeRdsHttpEndpoint, + ValidateFunc: validation.StringInSlice(appsync.RelationalDatabaseSourceType_Values(), true), }, }, }, - ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "http_config", "lambda_config", "relational_database_config"}, + ConflictsWith: []string{"dynamodb_config", "elasticsearch_config", "opensearchservice_config", "http_config", "lambda_config"}, }, "service_role_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - "arn": { - Type: schema.TypeString, - Computed: true, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(appsync.DataSourceType_Values(), true), + StateFunc: func(v interface{}) string { + return strings.ToUpper(v.(string)) + }, }, }, } diff --git a/internal/service/appsync/datasource_test.go b/internal/service/appsync/datasource_test.go index c362d82702e..32018057aa6 100644 --- a/internal/service/appsync/datasource_test.go +++ b/internal/service/appsync/datasource_test.go @@ -30,18 +30,18 @@ func testAccDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccDataSourceConfig_typeNone(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckExistsDataSource(ctx, resourceName), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "appsync", regexp.MustCompile(fmt.Sprintf("apis/.+/datasources/%s", rName))), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "dynamodb_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "elasticsearch_config.#", "0"), - resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "event_bridge_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "http_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "lambda_config.#", "0"), - resource.TestCheckResourceAttr(resourceName, "relational_database_config.#", "0"), - resource.TestCheckResourceAttr(resourceName, "event_bridge_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "opensearchservice_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "relational_database_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "type", "NONE"), ), }, diff --git a/website/docs/r/appsync_datasource.html.markdown b/website/docs/r/appsync_datasource.html.markdown index 2306eca5598..30681a874b4 100644 --- a/website/docs/r/appsync_datasource.html.markdown +++ b/website/docs/r/appsync_datasource.html.markdown @@ -132,16 +132,6 @@ The following arguments are supported: * `signing_region` - (Optional) Signing Amazon Web Services Region for IAM authorization. * `signing_service_name`- (Optional) Signing service name for IAM authorization. -#### http_endpoint_config - -The following arguments are supported: - -* `db_cluster_identifier` - (Required) Amazon RDS cluster identifier. -* `aws_secret_store_arn` - (Required) AWS secret store ARN for database credentials. -* `database_name` - (Optional) Logical database name. -* `region` - (Optional) AWS Region for RDS HTTP endpoint. Defaults to current region. -* `schema` - (Optional) Logical schema name. - ### lambda_config The following arguments are supported: @@ -162,6 +152,16 @@ The following arguments are supported: * `http_endpoint_config` - (Required) Amazon RDS HTTP endpoint configuration. See [HTTP Endpoint Config](#http_endpoint_config). * `source_type` - (Optional) Source type for the relational database. Valid values: `RDS_HTTP_ENDPOINT`. +#### http_endpoint_config + +The following arguments are supported: + +* `db_cluster_identifier` - (Required) Amazon RDS cluster identifier. +* `aws_secret_store_arn` - (Required) AWS secret store ARN for database credentials. +* `database_name` - (Optional) Logical database name. +* `region` - (Optional) AWS Region for RDS HTTP endpoint. Defaults to current region. +* `schema` - (Optional) Logical schema name. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 80585400cd5d02aae557d135fa5693a663a773bf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 20 Apr 2023 13:37:25 -0400 Subject: [PATCH 5/5] r/aws_appsync_datasource: Tidy up acceptance tests. --- internal/service/appsync/datasource.go | 98 ++++++----- internal/service/appsync/datasource_test.go | 181 ++++++++++---------- internal/service/appsync/function_test.go | 2 +- internal/service/appsync/resolver_test.go | 4 +- 4 files changed, 150 insertions(+), 135 deletions(-) diff --git a/internal/service/appsync/datasource.go b/internal/service/appsync/datasource.go index d2820da59c3..05628f7d84f 100644 --- a/internal/service/appsync/datasource.go +++ b/internal/service/appsync/datasource.go @@ -11,10 +11,12 @@ import ( "github.com/aws/aws-sdk-go/service/appsync" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -25,6 +27,7 @@ func ResourceDataSource() *schema.Resource { ReadWithoutTimeout: resourceDataSourceRead, UpdateWithoutTimeout: resourceDataSourceUpdate, DeleteWithoutTimeout: resourceDataSourceDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -278,9 +281,10 @@ func resourceDataSourceCreate(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).AppSyncConn() region := meta.(*conns.AWSClient).Region + name := d.Get("name").(string) input := &appsync.CreateDataSourceInput{ ApiId: aws.String(d.Get("api_id").(string)), - Name: aws.String(d.Get("name").(string)), + Name: aws.String(name), Type: aws.String(d.Get("type").(string)), } @@ -323,7 +327,7 @@ func resourceDataSourceCreate(ctx context.Context, d *schema.ResourceData, meta _, err := conn.CreateDataSourceWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Appsync Datasource: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Appsync Data Source (%s): %s", name, err) } d.SetId(d.Get("api_id").(string) + "-" + d.Get("name").(string)) @@ -338,59 +342,46 @@ func resourceDataSourceRead(ctx context.Context, d *schema.ResourceData, meta in apiID, name, err := DecodeID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading Appsync Data Source (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - input := &appsync.GetDataSourceInput{ - ApiId: aws.String(apiID), - Name: aws.String(name), + dataSource, err := FindDataSourceByTwoPartKey(ctx, conn, apiID, name) + + if tfresource.NotFound(err) && !d.IsNewResource() { + log.Printf("[WARN] AppSync Datasource %q not found, removing from state", d.Id()) + d.SetId("") + return diags } - resp, err := conn.GetDataSourceWithContext(ctx, input) if err != nil { - if tfawserr.ErrCodeEquals(err, appsync.ErrCodeNotFoundException) && !d.IsNewResource() { - log.Printf("[WARN] AppSync Datasource %q not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading Appsync Data Source (%s): %s", d.Id(), err) } - dataSource := resp.DataSource - d.Set("api_id", apiID) d.Set("arn", dataSource.DataSourceArn) d.Set("description", dataSource.Description) - if err := d.Set("dynamodb_config", flattenDynamoDBDataSourceConfig(dataSource.DynamodbConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting dynamodb_config: %s", err) } - if err := d.Set("elasticsearch_config", flattenElasticsearchDataSourceConfig(dataSource.ElasticsearchConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting elasticsearch_config: %s", err) } - - if err := d.Set("opensearchservice_config", flattenOpenSearchServiceDataSourceConfig(dataSource.OpenSearchServiceConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting opensearchservice_config: %s", err) + if err := d.Set("event_bridge_config", flattenEventBridgeDataSourceConfig(dataSource.EventBridgeConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting event_bridge_config: %s", err) } - if err := d.Set("http_config", flattenHTTPDataSourceConfig(dataSource.HttpConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting http_config: %s", err) } - if err := d.Set("lambda_config", flattenLambdaDataSourceConfig(dataSource.LambdaConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting lambda_config: %s", err) } - + d.Set("name", dataSource.Name) + if err := d.Set("opensearchservice_config", flattenOpenSearchServiceDataSourceConfig(dataSource.OpenSearchServiceConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting opensearchservice_config: %s", err) + } if err := d.Set("relational_database_config", flattenRelationalDatabaseDataSourceConfig(dataSource.RelationalDatabaseConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting relational_database_config: %s", err) } - - if err := d.Set("event_bridge_config", flattenEventBridgeDataSourceConfig(dataSource.EventBridgeConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting event_bridge_config: %s", err) - } - - d.Set("name", dataSource.Name) d.Set("service_role_arn", dataSource.ServiceRoleArn) d.Set("type", dataSource.Type) @@ -405,7 +396,7 @@ func resourceDataSourceUpdate(ctx context.Context, d *schema.ResourceData, meta apiID, name, err := DecodeID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "updating Appsync Data Source (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } input := &appsync.UpdateDataSourceInput{ @@ -426,10 +417,6 @@ func resourceDataSourceUpdate(ctx context.Context, d *schema.ResourceData, meta input.ElasticsearchConfig = expandElasticsearchDataSourceConfig(v.([]interface{}), region) } - if v, ok := d.GetOk("opensearchservice_config"); ok { - input.OpenSearchServiceConfig = expandOpenSearchServiceDataSourceConfig(v.([]interface{}), region) - } - if v, ok := d.GetOk("http_config"); ok { input.HttpConfig = expandHTTPDataSourceConfig(v.([]interface{})) } @@ -438,15 +425,20 @@ func resourceDataSourceUpdate(ctx context.Context, d *schema.ResourceData, meta input.LambdaConfig = expandLambdaDataSourceConfig(v.([]interface{})) } - if v, ok := d.GetOk("service_role_arn"); ok { - input.ServiceRoleArn = aws.String(v.(string)) + if v, ok := d.GetOk("opensearchservice_config"); ok { + input.OpenSearchServiceConfig = expandOpenSearchServiceDataSourceConfig(v.([]interface{}), region) } if v, ok := d.GetOk("relational_database_config"); ok { input.RelationalDatabaseConfig = expandRelationalDatabaseDataSourceConfig(v.([]interface{}), region) } + if v, ok := d.GetOk("service_role_arn"); ok { + input.ServiceRoleArn = aws.String(v.(string)) + } + _, err = conn.UpdateDataSourceWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating Appsync Data Source (%s): %s", d.Id(), err) } @@ -461,7 +453,7 @@ func resourceDataSourceDelete(ctx context.Context, d *schema.ResourceData, meta apiID, name, err := DecodeID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting Appsync Data Source (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } input := &appsync.DeleteDataSourceInput{ @@ -470,16 +462,44 @@ func resourceDataSourceDelete(ctx context.Context, d *schema.ResourceData, meta } _, err = conn.DeleteDataSourceWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, appsync.ErrCodeNotFoundException) { + return diags + } + if err != nil { - if tfawserr.ErrCodeEquals(err, appsync.ErrCodeNotFoundException) { - return diags - } return sdkdiag.AppendErrorf(diags, "deleting Appsync Data Source (%s): %s", d.Id(), err) } return diags } +func FindDataSourceByTwoPartKey(ctx context.Context, conn *appsync.AppSync, apiID, name string) (*appsync.DataSource, error) { + input := &appsync.GetDataSourceInput{ + ApiId: aws.String(apiID), + Name: aws.String(name), + } + + output, err := conn.GetDataSourceWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, appsync.ErrCodeNotFoundException) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.DataSource == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.DataSource, nil +} + func DecodeID(id string) (string, string, error) { idParts := strings.SplitN(id, "-", 2) if len(idParts) != 2 { diff --git a/internal/service/appsync/datasource_test.go b/internal/service/appsync/datasource_test.go index 32018057aa6..32b9b3b7373 100644 --- a/internal/service/appsync/datasource_test.go +++ b/internal/service/appsync/datasource_test.go @@ -6,15 +6,14 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/appsync" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "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/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfappsync "github.com/hashicorp/terraform-provider-aws/internal/service/appsync" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func testAccDataSource_basic(t *testing.T) { @@ -628,19 +627,19 @@ func testAccCheckDataSourceDestroy(ctx context.Context) resource.TestCheckFunc { return err } - input := &appsync.GetDataSourceInput{ - ApiId: aws.String(apiID), - Name: aws.String(name), + _, err = tfappsync.FindDataSourceByTwoPartKey(ctx, conn, apiID, name) + + if tfresource.NotFound(err) { + continue } - _, err = conn.GetDataSourceWithContext(ctx, input) if err != nil { - if tfawserr.ErrCodeEquals(err, appsync.ErrCodeNotFoundException) { - return nil - } return err } + + return fmt.Errorf("Appsync Data Source %s still exists", rs.Primary.ID) } + return nil } } @@ -651,8 +650,9 @@ func testAccCheckExistsDataSource(ctx context.Context, name string) resource.Tes if !ok { return fmt.Errorf("Not found: %s", name) } + if rs.Primary.ID == "" { - return fmt.Errorf("Resource has no ID: %s", name) + return fmt.Errorf("No Appsync Data Source ID found: %s", name) } apiID, name, err := tfappsync.DecodeID(rs.Primary.ID) @@ -663,22 +663,17 @@ func testAccCheckExistsDataSource(ctx context.Context, name string) resource.Tes conn := acctest.Provider.Meta().(*conns.AWSClient).AppSyncConn() - input := &appsync.GetDataSourceInput{ - ApiId: aws.String(apiID), - Name: aws.String(name), - } - - _, err = conn.GetDataSourceWithContext(ctx, input) + _, err = tfappsync.FindDataSourceByTwoPartKey(ctx, conn, apiID, name) return err } } -func testAccDatasourceConfig_dynamoDBBase(rName string) string { +func testAccDatasourceConfig_baseDynamoDB(rName string) string { return fmt.Sprintf(` resource "aws_dynamodb_table" "test" { hash_key = "UserId" - name = %q + name = %[1]q read_capacity = 1 write_capacity = 1 @@ -689,7 +684,7 @@ resource "aws_dynamodb_table" "test" { } resource "aws_iam_role" "test" { - name = %q + name = %[1]q assume_role_policy = <