From 93fa7127e35486ed7f63240c28ab23969c4b4923 Mon Sep 17 00:00:00 2001 From: Yarden Sachs Date: Thu, 1 Oct 2020 20:30:43 -0700 Subject: [PATCH 1/7] Added keyvaluetags --- .../keyvaluetags/generators/listtags/main.go | 1 + .../generators/servicetags/main.go | 1 + .../generators/updatetags/main.go | 1 + aws/internal/keyvaluetags/list_tags_gen.go | 18 +++++++++ .../service_generation_customizations.go | 5 +++ aws/internal/keyvaluetags/service_tags_gen.go | 28 ++++++++++++++ aws/internal/keyvaluetags/update_tags_gen.go | 37 +++++++++++++++++++ 7 files changed, 91 insertions(+) diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index 84f2c4954503..ebd9a627a0bf 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 d01d72cd8592..544f2fcef488 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 d7e2d8d91ec3..309b3dff0c0f 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 31875f548c19..27f4e1309eea 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 8e6d73b61662..808c332e1e97 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 92b4375dbeba..9386b0917c9b 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 0892d4d545df..b3b5e1282a07 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. From c62c396a18c71bba6b52b2cee2baad4950209eca Mon Sep 17 00:00:00 2001 From: Yarden Sachs Date: Thu, 1 Oct 2020 22:57:43 -0700 Subject: [PATCH 2/7] Added Timestream Database --- aws/provider.go | 1 + aws/resource_aws_timestreamwrite_database.go | 148 ++++++++++++ ...ource_aws_timestreamwrite_database_test.go | 219 ++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 aws/resource_aws_timestreamwrite_database.go create mode 100644 aws/resource_aws_timestreamwrite_database_test.go diff --git a/aws/provider.go b/aws/provider.go index bad1b91ce822..ec3908adb61d 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 000000000000..62e2379feeb4 --- /dev/null +++ b/aws/resource_aws_timestreamwrite_database.go @@ -0,0 +1,148 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/timestreamwrite" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func resourceAwsTimestreamWriteDatabase() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsTimestreamWriteDatabaseCreate, + Read: resourceAwsTimestreamWriteDatabaseRead, + Update: resourceAwsTimestreamWriteDatabaseUpdate, + Delete: resourceAwsTimestreamWriteDatabaseDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "database_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "kms_key_id": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAwsTimestreamWriteDatabaseCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).timestreamwriteconn + + input := ×treamwrite.CreateDatabaseInput{ + DatabaseName: aws.String(d.Get("database_name").(string)), + } + if v, ok := d.GetOk("kms_key_id"); ok { + input.KmsKeyId = aws.String(v.(string)) + } + + if attr, ok := d.GetOk("tags"); ok { + input.Tags = keyvaluetags.New(attr.(map[string]interface{})).IgnoreAws().TimestreamwriteTags() + } + + _, err := conn.CreateDatabase(input) + if err != nil { + return err + } + + d.SetId(d.Get("database_name").(string)) + + return resourceAwsTimestreamWriteDatabaseRead(d, meta) +} + +func resourceAwsTimestreamWriteDatabaseRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).timestreamwriteconn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + resp, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(d.Id()), + }) + if err != nil { + if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Timestream Database %q not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("database_name", resp.Database.DatabaseName) + d.Set("kms_key_id", resp.Database.KmsKeyId) + d.Set("arn", resp.Database.Arn) + + arn := aws.StringValue(resp.Database.Arn) + + tags, err := keyvaluetags.TimestreamwriteListTags(conn, arn) + + if err != nil { + return fmt.Errorf("error listing tags for Timestream Database (%s): %s", arn, err) + } + + if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsTimestreamWriteDatabaseUpdate(d *schema.ResourceData, meta interface{}) error { + 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.UpdateDatabase(input) + if err != nil { + return err + } + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.TimestreamwriteUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating Timesteram Database (%s) tags: %s", d.Get("arn").(string), err) + } + } + + return resourceAwsTimestreamWriteDatabaseRead(d, meta) +} + +func resourceAwsTimestreamWriteDatabaseDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).timestreamwriteconn + + input := ×treamwrite.DeleteDatabaseInput{ + DatabaseName: aws.String(d.Id()), + } + + _, err := conn.DeleteDatabase(input) + if err != nil { + if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { + return nil + } + return 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 000000000000..c1d046b4a925 --- /dev/null +++ b/aws/resource_aws_timestreamwrite_database_test.go @@ -0,0 +1,219 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/timestreamwrite" + "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 TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test_database" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSTimestreamWriteDatabaseConfigNoTags(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "database_name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test_database" + rName := acctest.RandomWithPrefix("tf-acc-test") + kmsResourceName := "aws_kms_key.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + 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_Tags(t *testing.T) { + resourceName := "aws_timestreamwrite_database.test_database" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + 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"), + ), + }, + { + 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"), + ), + }, + { + Config: testAccAWSTimestreamWriteDatabaseConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.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 + } + + _, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(rs.Primary.ID), + }) + + if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { + continue + } + + if err == 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 Timestream Database ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).timestreamwriteconn + + _, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + DatabaseName: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + return nil + } +} + +func testAccAWSTimestreamWriteDatabaseConfigNoTags(rName string) string { + return fmt.Sprintf(` +resource "aws_timestreamwrite_database" "test_database" { + database_name = %[1]q +} +`, rName) +} + +func testAccAWSTimestreamWriteDatabaseConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_timestreamwrite_database" "test_database" { + 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" { + 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" "foo" { + description = "Terraform acc test" + + policy = < Date: Fri, 2 Oct 2020 13:40:00 -0700 Subject: [PATCH 3/7] Adding documentation --- .../r/timestreamwrite_database.html.markdown | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 website/docs/r/timestreamwrite_database.html.markdown diff --git a/website/docs/r/timestreamwrite_database.html.markdown b/website/docs/r/timestreamwrite_database.html.markdown new file mode 100644 index 000000000000..f0785cc9cff8 --- /dev/null +++ b/website/docs/r/timestreamwrite_database.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "TimestreamWrite" +layout: "aws" +page_title: "AWS: aws_timestreamwrite_database" +description: |- + Provides a Timestream database resource. +--- + +# Resource: aws_timestreamwrite_database + +Provides a Timestream database resource. + +## Example Usage + +### Basic usage + +```hcl +resource "aws_timestreamwrite_database" "test_database" { + database_name = "database-example" +} +``` + +### Full usage + +```hcl +resource "aws_timestreamwrite_database" "test_database" { + database_name = "database-example" + kms_key_id = aws_kms_key.foo.arn + + tags = { + Name = "value" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `database_name` – (Required) The name of the Timestream database. Minimum length of 3. Maximum length of 64. + +* `kms_key_id` - (Optional) The KMS key for the database. You can specify a key ID using any of the following: + * Key ID: `1234abcd-12ab-34cd-56ef-1234567890ab` + * Key ARN: `arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab` + * Alias name: `alias/ExampleAlias` + * Alias ARN: `arn:aws:kms:us-east-1:111122223333:alias/ExampleAlias` + If the KMS key is not specified, the database will be encrypted with a Timestream managed KMS key located in your account. Refer to [AWS managed KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#aws-managed-cmk) for more info. + +* `tags` - (Optional) A map of tags to assign to the resource + + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name that uniquely identifies this database. + +* `kms_key_id` - The identifier of the KMS key used to encrypt the data stored in the database. + +## Import + +Timestream databases can be imported using the `database_name`, e.g. + +``` +$ terraform import aws_timestreamwrite_database.my_database my_database +``` + From a870d28038babf165f51d559e70166628ce27bff Mon Sep 17 00:00:00 2001 From: Yarden Sachs Date: Mon, 19 Oct 2020 11:06:07 -0700 Subject: [PATCH 4/7] Fixes --- aws/resource_aws_timestreamwrite_database.go | 14 +++++++---- ...ource_aws_timestreamwrite_database_test.go | 23 +++++++++++-------- .../r/timestreamwrite_database.html.markdown | 11 ++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/aws/resource_aws_timestreamwrite_database.go b/aws/resource_aws_timestreamwrite_database.go index 62e2379feeb4..42b8282015c2 100644 --- a/aws/resource_aws_timestreamwrite_database.go +++ b/aws/resource_aws_timestreamwrite_database.go @@ -34,9 +34,10 @@ func resourceAwsTimestreamWriteDatabase() *schema.Resource { }, "kms_key_id": { - Type: schema.TypeString, - Computed: true, - Optional: true, + Type: schema.TypeString, + Computed: true, + Optional: true, + ValidateFunc: validateArn, }, "tags": tagsSchema(), @@ -58,12 +59,15 @@ func resourceAwsTimestreamWriteDatabaseCreate(d *schema.ResourceData, meta inter input.Tags = keyvaluetags.New(attr.(map[string]interface{})).IgnoreAws().TimestreamwriteTags() } - _, err := conn.CreateDatabase(input) + resp, err := conn.CreateDatabase(input) + if err != nil { return err } - d.SetId(d.Get("database_name").(string)) + name := aws.StringValue(resp.Database.DatabaseName) + + d.SetId(name) return resourceAwsTimestreamWriteDatabaseRead(d, meta) } diff --git a/aws/resource_aws_timestreamwrite_database_test.go b/aws/resource_aws_timestreamwrite_database_test.go index c1d046b4a925..54d497040693 100644 --- a/aws/resource_aws_timestreamwrite_database_test.go +++ b/aws/resource_aws_timestreamwrite_database_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -12,7 +13,7 @@ import ( ) func TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { - resourceName := "aws_timestreamwrite_database.test_database" + resourceName := "aws_timestreamwrite_database.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -24,7 +25,9 @@ func TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { Config: testAccAWSTimestreamWriteDatabaseConfigNoTags(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"), ), }, @@ -38,9 +41,9 @@ func TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { } func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { - resourceName := "aws_timestreamwrite_database.test_database" + resourceName := "aws_timestreamwrite_database.test" rName := acctest.RandomWithPrefix("tf-acc-test") - kmsResourceName := "aws_kms_key.foo" + kmsResourceName := "aws_kms_key.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -65,7 +68,7 @@ func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { } func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { - resourceName := "aws_timestreamwrite_database.test_database" + resourceName := "aws_timestreamwrite_database.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -158,7 +161,7 @@ func testAccCheckAWSTimestreamWriteDatabaseExists(n string) resource.TestCheckFu func testAccAWSTimestreamWriteDatabaseConfigNoTags(rName string) string { return fmt.Sprintf(` -resource "aws_timestreamwrite_database" "test_database" { +resource "aws_timestreamwrite_database" "test" { database_name = %[1]q } `, rName) @@ -166,7 +169,7 @@ resource "aws_timestreamwrite_database" "test_database" { func testAccAWSTimestreamWriteDatabaseConfigTags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` -resource "aws_timestreamwrite_database" "test_database" { +resource "aws_timestreamwrite_database" "test" { database_name = %[1]q tags = { @@ -178,7 +181,7 @@ resource "aws_timestreamwrite_database" "test_database" { func testAccAWSTimestreamWriteDatabaseConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return fmt.Sprintf(` -resource "aws_timestreamwrite_database" "test_database" { +resource "aws_timestreamwrite_database" "test" { database_name = %[1]q tags = { @@ -191,7 +194,7 @@ resource "aws_timestreamwrite_database" "test_database" { func testAccAWSTimestreamWriteDatabaseConfigKmsKey(rName string) string { return fmt.Sprintf(` -resource "aws_kms_key" "foo" { +resource "aws_kms_key" "test" { description = "Terraform acc test" policy = < Date: Fri, 7 May 2021 12:58:28 -0400 Subject: [PATCH 5/7] Update CHANGELOG for #15463; add ErrorCheck to tests --- .changelog/15463.txt | 3 +++ aws/resource_aws_timestreamwrite_database_test.go | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .changelog/15463.txt diff --git a/.changelog/15463.txt b/.changelog/15463.txt new file mode 100644 index 000000000000..31f66b4670ac --- /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/resource_aws_timestreamwrite_database_test.go b/aws/resource_aws_timestreamwrite_database_test.go index 54d497040693..8e6dda9af93c 100644 --- a/aws/resource_aws_timestreamwrite_database_test.go +++ b/aws/resource_aws_timestreamwrite_database_test.go @@ -18,6 +18,7 @@ func TestAccAWSTimestreamWriteDatabase_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, Steps: []resource.TestStep{ @@ -47,6 +48,7 @@ func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, Steps: []resource.TestStep{ @@ -73,6 +75,7 @@ func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, Steps: []resource.TestStep{ From 13f913d975fa528185f2c8c79834cfe07c74a3b9 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Fri, 7 May 2021 21:46:11 -0400 Subject: [PATCH 6/7] CR updates --- aws/resource_aws_timestreamwrite_database.go | 134 ++++++++----- ...ource_aws_timestreamwrite_database_test.go | 178 +++++++++++++++--- .../r/timestreamwrite_database.html.markdown | 22 +-- 3 files changed, 257 insertions(+), 77 deletions(-) diff --git a/aws/resource_aws_timestreamwrite_database.go b/aws/resource_aws_timestreamwrite_database.go index 42b8282015c2..cd12ca4407d7 100644 --- a/aws/resource_aws_timestreamwrite_database.go +++ b/aws/resource_aws_timestreamwrite_database.go @@ -1,24 +1,29 @@ 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{ - Create: resourceAwsTimestreamWriteDatabaseCreate, - Read: resourceAwsTimestreamWriteDatabaseRead, - Update: resourceAwsTimestreamWriteDatabaseUpdate, - Delete: resourceAwsTimestreamWriteDatabaseDelete, + CreateWithoutTimeout: resourceAwsTimestreamWriteDatabaseCreate, + ReadWithoutTimeout: resourceAwsTimestreamWriteDatabaseRead, + UpdateWithoutTimeout: resourceAwsTimestreamWriteDatabaseUpdate, + DeleteWithoutTimeout: resourceAwsTimestreamWriteDatabaseDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ @@ -31,83 +36,121 @@ func resourceAwsTimestreamWriteDatabase() *schema.Resource { 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, - Computed: true, Optional: true, + Computed: true, ValidateFunc: validateArn, }, + "table_count": { + Type: schema.TypeInt, + Computed: true, + }, + "tags": tagsSchema(), + + "tags_all": tagsSchemaComputed(), }, + + CustomizeDiff: SetTagsDiff, } } -func resourceAwsTimestreamWriteDatabaseCreate(d *schema.ResourceData, meta interface{}) error { +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(d.Get("database_name").(string)), + DatabaseName: aws.String(dbName), } + if v, ok := d.GetOk("kms_key_id"); ok { input.KmsKeyId = aws.String(v.(string)) } - if attr, ok := d.GetOk("tags"); ok { - input.Tags = keyvaluetags.New(attr.(map[string]interface{})).IgnoreAws().TimestreamwriteTags() + if len(tags) > 0 { + input.Tags = tags.IgnoreAws().TimestreamwriteTags() } - resp, err := conn.CreateDatabase(input) + resp, err := conn.CreateDatabaseWithContext(ctx, input) if err != nil { - return err + return diag.FromErr(fmt.Errorf("error creating Timestream Database (%s): %w", dbName, err)) } - name := aws.StringValue(resp.Database.DatabaseName) + if resp == nil || resp.Database == nil { + return diag.FromErr(fmt.Errorf("error creating Timestream Database (%s): empty output", dbName)) + } - d.SetId(name) + d.SetId(aws.StringValue(resp.Database.DatabaseName)) - return resourceAwsTimestreamWriteDatabaseRead(d, meta) + return resourceAwsTimestreamWriteDatabaseRead(ctx, d, meta) } -func resourceAwsTimestreamWriteDatabaseRead(d *schema.ResourceData, meta interface{}) error { +func resourceAwsTimestreamWriteDatabaseRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).timestreamwriteconn + defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - resp, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + 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 { - if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { - log.Printf("[WARN] Timestream Database %q not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return diag.FromErr(fmt.Errorf("error reading Timestream Database (%s): %w", d.Id(), err)) } - d.Set("database_name", resp.Database.DatabaseName) - d.Set("kms_key_id", resp.Database.KmsKeyId) - d.Set("arn", resp.Database.Arn) + if resp == nil || resp.Database == nil { + return diag.FromErr(fmt.Errorf("error reading Timestream Database (%s): empty output", d.Id())) + } - arn := aws.StringValue(resp.Database.Arn) + 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 fmt.Errorf("error listing tags for Timestream Database (%s): %s", arn, err) + return diag.FromErr(fmt.Errorf("error listing tags for Timestream Database (%s): %w", arn, err)) } - if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", 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(d *schema.ResourceData, meta interface{}) error { +func resourceAwsTimestreamWriteDatabaseUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*AWSClient).timestreamwriteconn if d.HasChange("kms_key_id") { @@ -116,36 +159,39 @@ func resourceAwsTimestreamWriteDatabaseUpdate(d *schema.ResourceData, meta inter KmsKeyId: aws.String(d.Get("kms_key_id").(string)), } - _, err := conn.UpdateDatabase(input) + _, err := conn.UpdateDatabaseWithContext(ctx, input) + if err != nil { - return err + return diag.FromErr(fmt.Errorf("error updating Timestream Database (%s): %w", d.Id(), err)) } } - if d.HasChange("tags") { - o, n := d.GetChange("tags") + 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 fmt.Errorf("error updating Timesteram Database (%s) tags: %s", d.Get("arn").(string), err) + return diag.FromErr(fmt.Errorf("error updating Timestream Database (%s) tags: %w", d.Get("arn").(string), err)) } } - return resourceAwsTimestreamWriteDatabaseRead(d, meta) + return resourceAwsTimestreamWriteDatabaseRead(ctx, d, meta) } -func resourceAwsTimestreamWriteDatabaseDelete(d *schema.ResourceData, meta interface{}) error { +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.DeleteDatabase(input) + _, err := conn.DeleteDatabaseWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, timestreamwrite.ErrCodeResourceNotFoundException) { + return nil + } + if err != nil { - if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { - return nil - } - return err + 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 index 8e6dda9af93c..7419d34f171a 100644 --- a/aws/resource_aws_timestreamwrite_database_test.go +++ b/aws/resource_aws_timestreamwrite_database_test.go @@ -2,28 +2,90 @@ 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) }, + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSTimestreamWriteDatabaseConfigNoTags(rName), + Config: testAccAWSTimestreamWriteDatabaseConfigBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSTimestreamWriteDatabaseExists(resourceName), testAccCheckResourceAttrRegionalARN(resourceName, "arn", "timestream", fmt.Sprintf("database/%s", rName)), @@ -47,7 +109,7 @@ func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { kmsResourceName := "aws_kms_key.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, @@ -69,12 +131,53 @@ func TestAccAWSTimestreamWriteDatabase_kmsKey(t *testing.T) { }) } +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) }, + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSTimestreamWrite(t) }, ErrorCheck: testAccErrorCheck(t, timestreamwrite.EndpointsID), Providers: testAccProviders, CheckDestroy: testAccCheckAWSTimestreamWriteDatabaseDestroy, @@ -85,6 +188,8 @@ func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { 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"), ), }, { @@ -94,6 +199,9 @@ func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { 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"), ), }, { @@ -102,6 +210,8 @@ func TestAccAWSTimestreamWriteDatabase_Tags(t *testing.T) { 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"), ), }, { @@ -121,15 +231,19 @@ func testAccCheckAWSTimestreamWriteDatabaseDestroy(s *terraform.State) error { continue } - _, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + output, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ DatabaseName: aws.String(rs.Primary.ID), }) - if isAWSErr(err, timestreamwrite.ErrCodeResourceNotFoundException, "") { + if tfawserr.ErrCodeEquals(err, timestreamwrite.ErrCodeResourceNotFoundException) { continue } - if err == nil { + if err != nil { + return err + } + + if output != nil && output.Database != nil { return fmt.Errorf("Timestream Database (%s) still exists", rs.Primary.ID) } } @@ -145,12 +259,12 @@ func testAccCheckAWSTimestreamWriteDatabaseExists(n string) resource.TestCheckFu } if rs.Primary.ID == "" { - return fmt.Errorf("No Timestream Database ID is set") + return fmt.Errorf("no resource ID is set") } conn := testAccProvider.Meta().(*AWSClient).timestreamwriteconn - _, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ + output, err := conn.DescribeDatabase(×treamwrite.DescribeDatabaseInput{ DatabaseName: aws.String(rs.Primary.ID), }) @@ -158,11 +272,31 @@ func testAccCheckAWSTimestreamWriteDatabaseExists(n string) resource.TestCheckFu return err } + if output == nil || output.Database == nil { + return fmt.Errorf("Timestream Database (%s) not found", rs.Primary.ID) + } + return nil } } -func testAccAWSTimestreamWriteDatabaseConfigNoTags(rName string) string { +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 @@ -198,21 +332,21 @@ resource "aws_timestreamwrite_database" "test" { func testAccAWSTimestreamWriteDatabaseConfigKmsKey(rName string) string { return fmt.Sprintf(` resource "aws_kms_key" "test" { - description = "Terraform acc test" + description = %[1]q policy = < Date: Sat, 8 May 2021 10:42:32 -0400 Subject: [PATCH 7/7] add note in kms_key_id schema attribute --- aws/resource_aws_timestreamwrite_database.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_timestreamwrite_database.go b/aws/resource_aws_timestreamwrite_database.go index cd12ca4407d7..9f6fe619f965 100644 --- a/aws/resource_aws_timestreamwrite_database.go +++ b/aws/resource_aws_timestreamwrite_database.go @@ -43,9 +43,13 @@ func resourceAwsTimestreamWriteDatabase() *schema.Resource { }, "kms_key_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, + 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, },