diff --git a/.changelog/15463.txt b/.changelog/15463.txt new file mode 100644 index 00000000000..31f66b4670a --- /dev/null +++ b/.changelog/15463.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_timestreamwrite_database +``` \ No newline at end of file diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index 84f2c495450..ebd9a627a0b 100644 --- a/aws/internal/keyvaluetags/generators/listtags/main.go +++ b/aws/internal/keyvaluetags/generators/listtags/main.go @@ -116,6 +116,7 @@ var serviceNames = []string{ "ssoadmin", "storagegateway", "swf", + "timestreamwrite", "transfer", "waf", "wafregional", diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index d01d72cd859..544f2fcef48 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -97,6 +97,7 @@ var sliceServiceNames = []string{ "ssoadmin", "storagegateway", "swf", + "timestreamwrite", "transfer", "waf", "wafregional", diff --git a/aws/internal/keyvaluetags/generators/updatetags/main.go b/aws/internal/keyvaluetags/generators/updatetags/main.go index d7e2d8d91ec..309b3dff0c0 100644 --- a/aws/internal/keyvaluetags/generators/updatetags/main.go +++ b/aws/internal/keyvaluetags/generators/updatetags/main.go @@ -124,6 +124,7 @@ var serviceNames = []string{ "storagegateway", "swf", "synthetics", + "timestreamwrite", "transfer", "waf", "wafregional", diff --git a/aws/internal/keyvaluetags/list_tags_gen.go b/aws/internal/keyvaluetags/list_tags_gen.go index 31875f548c1..27f4e1309ee 100644 --- a/aws/internal/keyvaluetags/list_tags_gen.go +++ b/aws/internal/keyvaluetags/list_tags_gen.go @@ -103,6 +103,7 @@ import ( "github.com/aws/aws-sdk-go/service/ssoadmin" "github.com/aws/aws-sdk-go/service/storagegateway" "github.com/aws/aws-sdk-go/service/swf" + "github.com/aws/aws-sdk-go/service/timestreamwrite" "github.com/aws/aws-sdk-go/service/transfer" "github.com/aws/aws-sdk-go/service/waf" "github.com/aws/aws-sdk-go/service/wafregional" @@ -1808,6 +1809,23 @@ func SwfListTags(conn *swf.SWF, identifier string) (KeyValueTags, error) { return SwfKeyValueTags(output.Tags), nil } +// TimestreamwriteListTags lists timestreamwrite service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func TimestreamwriteListTags(conn *timestreamwrite.TimestreamWrite, identifier string) (KeyValueTags, error) { + input := ×treamwrite.ListTagsForResourceInput{ + ResourceARN: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(input) + + if err != nil { + return New(nil), err + } + + return TimestreamwriteKeyValueTags(output.Tags), nil +} + // TransferListTags lists transfer service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/internal/keyvaluetags/service_generation_customizations.go b/aws/internal/keyvaluetags/service_generation_customizations.go index 8e6d73b6166..808c332e1e9 100644 --- a/aws/internal/keyvaluetags/service_generation_customizations.go +++ b/aws/internal/keyvaluetags/service_generation_customizations.go @@ -115,6 +115,7 @@ import ( "github.com/aws/aws-sdk-go/service/storagegateway" "github.com/aws/aws-sdk-go/service/swf" "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/aws/aws-sdk-go/service/timestreamwrite" "github.com/aws/aws-sdk-go/service/transfer" "github.com/aws/aws-sdk-go/service/waf" "github.com/aws/aws-sdk-go/service/wafregional" @@ -349,6 +350,8 @@ func ServiceClientType(serviceName string) string { funcType = reflect.TypeOf(swf.New) case "synthetics": funcType = reflect.TypeOf(synthetics.New) + case "timestreamwrite": + funcType = reflect.TypeOf(timestreamwrite.New) case "transfer": funcType = reflect.TypeOf(transfer.New) case "waf": @@ -752,6 +755,8 @@ func ServiceTagInputIdentifierField(serviceName string) string { return "ResourceId" case "storagegateway": return "ResourceARN" + case "timestreamwrite": + return "ResourceARN" case "transfer": return "Arn" case "waf": diff --git a/aws/internal/keyvaluetags/service_tags_gen.go b/aws/internal/keyvaluetags/service_tags_gen.go index 92b4375dbeb..9386b0917c9 100644 --- a/aws/internal/keyvaluetags/service_tags_gen.go +++ b/aws/internal/keyvaluetags/service_tags_gen.go @@ -85,6 +85,7 @@ import ( "github.com/aws/aws-sdk-go/service/ssoadmin" "github.com/aws/aws-sdk-go/service/storagegateway" "github.com/aws/aws-sdk-go/service/swf" + "github.com/aws/aws-sdk-go/service/timestreamwrite" "github.com/aws/aws-sdk-go/service/transfer" "github.com/aws/aws-sdk-go/service/waf" "github.com/aws/aws-sdk-go/service/wafv2" @@ -2790,6 +2791,33 @@ func SwfKeyValueTags(tags []*swf.ResourceTag) KeyValueTags { return New(m) } +// TimestreamwriteTags returns timestreamwrite service tags. +func (tags KeyValueTags) TimestreamwriteTags() []*timestreamwrite.Tag { + result := make([]*timestreamwrite.Tag, 0, len(tags)) + + for k, v := range tags.Map() { + tag := ×treamwrite.Tag{ + Key: aws.String(k), + Value: aws.String(v), + } + + result = append(result, tag) + } + + return result +} + +// TimestreamwriteKeyValueTags creates KeyValueTags from timestreamwrite service tags. +func TimestreamwriteKeyValueTags(tags []*timestreamwrite.Tag) KeyValueTags { + m := make(map[string]*string, len(tags)) + + for _, tag := range tags { + m[aws.StringValue(tag.Key)] = tag.Value + } + + return New(m) +} + // TransferTags returns transfer service tags. func (tags KeyValueTags) TransferTags() []*transfer.Tag { result := make([]*transfer.Tag, 0, len(tags)) diff --git a/aws/internal/keyvaluetags/update_tags_gen.go b/aws/internal/keyvaluetags/update_tags_gen.go index 0892d4d545d..b3b5e1282a0 100644 --- a/aws/internal/keyvaluetags/update_tags_gen.go +++ b/aws/internal/keyvaluetags/update_tags_gen.go @@ -113,6 +113,7 @@ import ( "github.com/aws/aws-sdk-go/service/storagegateway" "github.com/aws/aws-sdk-go/service/swf" "github.com/aws/aws-sdk-go/service/synthetics" + "github.com/aws/aws-sdk-go/service/timestreamwrite" "github.com/aws/aws-sdk-go/service/transfer" "github.com/aws/aws-sdk-go/service/waf" "github.com/aws/aws-sdk-go/service/wafregional" @@ -3979,6 +3980,42 @@ func SyntheticsUpdateTags(conn *synthetics.Synthetics, identifier string, oldTag return nil } +// TimestreamwriteUpdateTags updates timestreamwrite service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func TimestreamwriteUpdateTags(conn *timestreamwrite.TimestreamWrite, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := ×treamwrite.UntagResourceInput{ + ResourceARN: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.IgnoreAws().Keys()), + } + + _, err := conn.UntagResource(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := ×treamwrite.TagResourceInput{ + ResourceARN: aws.String(identifier), + Tags: updatedTags.IgnoreAws().TimestreamwriteTags(), + } + + _, err := conn.TagResource(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + // TransferUpdateTags updates transfer service tags. // The identifier is typically the Amazon Resource Name (ARN), although // it may also be a different identifier depending on the service. diff --git a/aws/provider.go b/aws/provider.go index bad1b91ce82..ec3908adb61 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -1055,6 +1055,7 @@ func Provider() *schema.Provider { "aws_subnet": resourceAwsSubnet(), "aws_swf_domain": resourceAwsSwfDomain(), "aws_synthetics_canary": resourceAwsSyntheticsCanary(), + "aws_timestreamwrite_database": resourceAwsTimestreamWriteDatabase(), "aws_transfer_server": resourceAwsTransferServer(), "aws_transfer_ssh_key": resourceAwsTransferSshKey(), "aws_transfer_user": resourceAwsTransferUser(), diff --git a/aws/resource_aws_timestreamwrite_database.go b/aws/resource_aws_timestreamwrite_database.go new file mode 100644 index 00000000000..9f6fe619f96 --- /dev/null +++ b/aws/resource_aws_timestreamwrite_database.go @@ -0,0 +1,202 @@ +package aws + +import ( + "context" + "fmt" + "log" + "regexp" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/timestreamwrite" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsTimestreamWriteDatabase() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAwsTimestreamWriteDatabaseCreate, + ReadWithoutTimeout: resourceAwsTimestreamWriteDatabaseRead, + UpdateWithoutTimeout: resourceAwsTimestreamWriteDatabaseUpdate, + DeleteWithoutTimeout: resourceAwsTimestreamWriteDatabaseDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "database_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(3, 64), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`), "must only include alphanumeric, underscore, period, or hyphen characters"), + ), + }, + + "kms_key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + // The Timestream API accepts the KmsKeyId as an ID, ARN, alias, or alias ARN but always returns the ARN of the key. + // The ARN is of the format 'arn:aws:kms:REGION:ACCOUNT_ID:key/KMS_KEY_ID'. Appropriate diff suppression + // would require an extra API call to the kms service's DescribeKey method to decipher aliases. + // To avoid importing an extra service in this resource, input here is restricted to only ARNs. + ValidateFunc: validateArn, + }, + + "table_count": { + Type: schema.TypeInt, + Computed: true, + }, + + "tags": tagsSchema(), + + "tags_all": tagsSchemaComputed(), + }, + + CustomizeDiff: SetTagsDiff, + } +} + +func resourceAwsTimestreamWriteDatabaseCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).timestreamwriteconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) + + dbName := d.Get("database_name").(string) + + input := ×treamwrite.CreateDatabaseInput{ + DatabaseName: aws.String(dbName), + } + + if v, ok := d.GetOk("kms_key_id"); ok { + input.KmsKeyId = aws.String(v.(string)) + } + + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().TimestreamwriteTags() + } + + resp, err := conn.CreateDatabaseWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Timestream Database (%s): %w", dbName, err)) + } + + if resp == nil || resp.Database == nil { + return diag.FromErr(fmt.Errorf("error creating Timestream Database (%s): empty output", dbName)) + } + + d.SetId(aws.StringValue(resp.Database.DatabaseName)) + + return resourceAwsTimestreamWriteDatabaseRead(ctx, d, meta) +} + +func resourceAwsTimestreamWriteDatabaseRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).timestreamwriteconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := ×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(d.Id()), + } + + resp, err := conn.DescribeDatabaseWithContext(ctx, input) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, timestreamwrite.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Timestream Database %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Timestream Database (%s): %w", d.Id(), err)) + } + + if resp == nil || resp.Database == nil { + return diag.FromErr(fmt.Errorf("error reading Timestream Database (%s): empty output", d.Id())) + } + + db := resp.Database + arn := aws.StringValue(db.Arn) + + d.Set("arn", arn) + d.Set("database_name", db.DatabaseName) + d.Set("kms_key_id", db.KmsKeyId) + d.Set("table_count", db.TableCount) + + tags, err := keyvaluetags.TimestreamwriteListTags(conn, arn) + + if err != nil { + return diag.FromErr(fmt.Errorf("error listing tags for Timestream Database (%s): %w", arn, err)) + } + + tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting tags: %w", err)) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting tags_all: %w", err)) + } + + return nil +} + +func resourceAwsTimestreamWriteDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).timestreamwriteconn + + if d.HasChange("kms_key_id") { + input := ×treamwrite.UpdateDatabaseInput{ + DatabaseName: aws.String(d.Id()), + KmsKeyId: aws.String(d.Get("kms_key_id").(string)), + } + + _, err := conn.UpdateDatabaseWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error updating Timestream Database (%s): %w", d.Id(), err)) + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := keyvaluetags.TimestreamwriteUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return diag.FromErr(fmt.Errorf("error updating Timestream Database (%s) tags: %w", d.Get("arn").(string), err)) + } + } + + return resourceAwsTimestreamWriteDatabaseRead(ctx, d, meta) +} + +func resourceAwsTimestreamWriteDatabaseDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).timestreamwriteconn + + input := ×treamwrite.DeleteDatabaseInput{ + DatabaseName: aws.String(d.Id()), + } + + _, err := conn.DeleteDatabaseWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, timestreamwrite.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error deleting Timestream Database (%s): %w", d.Id(), err)) + } + + return nil +} diff --git a/aws/resource_aws_timestreamwrite_database_test.go b/aws/resource_aws_timestreamwrite_database_test.go new file mode 100644 index 00000000000..7419d34f171 --- /dev/null +++ b/aws/resource_aws_timestreamwrite_database_test.go @@ -0,0 +1,359 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/timestreamwrite" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/go-multierror" + "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" +) + +func init() { + resource.AddTestSweepers("aws_timestreamwrite_database", &resource.Sweeper{ + Name: "aws_timestreamwrite_database", + F: testSweepTimestreamWriteDatabases, + }) +} + +func testSweepTimestreamWriteDatabases(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).timestreamwriteconn + + var sweeperErrs *multierror.Error + + input := ×treamwrite.ListDatabasesInput{} + + err = conn.ListDatabasesPages(input, func(page *timestreamwrite.ListDatabasesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, database := range page.Databases { + if database == nil { + continue + } + + dbName := aws.StringValue(database.DatabaseName) + + r := resourceAwsTimestreamWriteDatabase() + d := r.Data(nil) + d.SetId(dbName) + + err := r.Delete(d, client) + + if err != nil { + sweeperErr := fmt.Errorf("error deleting Timestream Database (%s): %w", dbName, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue + } + } + + return !lastPage + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Timestream Database sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Timestream Databases: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTimestreamWriteDatabaseConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + testAccCheckResourceAttrRegionalARN(resourceName, "arn", "timestream", fmt.Sprintf("database/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "database_name", rName), + testAccMatchResourceAttrRegionalARN(resourceName, "kms_key_id", "kms", regexp.MustCompile(`key/.+`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + kmsResourceName := "aws_kms_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTimestreamWriteDatabaseConfigKmsKey(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "database_name", rName), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSTimestreamWriteDatabase_updateKmsKey(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + kmsResourceName := "aws_kms_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTimestreamWriteDatabaseConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "kms_key_id", "kms", regexp.MustCompile(`key/.+`)), + ), + }, + { + Config: testAccAWSTimestreamWriteDatabaseConfigKmsKey(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSTimestreamWriteDatabaseConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + testAccMatchResourceAttrRegionalARN(resourceName, "kms_key_id", "kms", regexp.MustCompile(`key/.+`)), + ), + }, + }, + }) +} + +func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTimestreamWriteDatabaseConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1"), + ), + }, + { + Config: testAccAWSTimestreamWriteDatabaseConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + Config: testAccAWSTimestreamWriteDatabaseConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + resource.TestCheckResourceAttr(resourceName, "tags_all.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags_all.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSTimestreamWriteDatabaseDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).timestreamwriteconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_timestreamwrite_database" { + continue + } + + output, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(rs.Primary.ID), + }) + + if tfawserr.ErrCodeEquals(err, timestreamwrite.ErrCodeResourceNotFoundException) { + continue + } + + if err != nil { + return err + } + + if output != nil && output.Database != nil { + return fmt.Errorf("Timestream Database (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSTimestreamWriteDatabaseExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no resource ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).timestreamwriteconn + + output, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if output == nil || output.Database == nil { + return fmt.Errorf("Timestream Database (%s) not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccPreCheckAWSTimestreamWrite(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).timestreamwriteconn + + input := ×treamwrite.ListDatabasesInput{} + + _, err := conn.ListDatabases(input) + + if testAccPreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccAWSTimestreamWriteDatabaseConfigBasic(rName string) string { + return fmt.Sprintf(` +resource "aws_timestreamwrite_database" "test" { + database_name = %[1]q +} +`, rName) +} + +func testAccAWSTimestreamWriteDatabaseConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_timestreamwrite_database" "test" { + database_name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSTimestreamWriteDatabaseConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_timestreamwrite_database" "test" { + database_name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAWSTimestreamWriteDatabaseConfigKmsKey(rName string) string { + return fmt.Sprintf(` +resource "aws_kms_key" "test" { + description = %[1]q + + policy = <