diff --git a/aws/data_source_aws_qldb_ledger.go b/aws/data_source_aws_qldb_ledger.go new file mode 100644 index 000000000000..58e3cac689c7 --- /dev/null +++ b/aws/data_source_aws_qldb_ledger.go @@ -0,0 +1,63 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/qldb" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + + "log" + "regexp" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func dataSourceAwsQLDBLedger() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsQLDBLedgerRead, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 32), + validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9_-]+`), "must contain only alphanumeric characters, underscores, and hyphens"), + ), + }, + + "deletion_protection": { + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func dataSourceAwsQLDBLedgerRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).qldbconn + + target := d.Get("name") + + req := &qldb.DescribeLedgerInput{ + Name: aws.String(target.(string)), + } + + log.Printf("[DEBUG] Reading QLDB Ledger: %s", req) + resp, err := conn.DescribeLedger(req) + + if err != nil { + return fmt.Errorf("Error describing ledger: %s", err) + } + + d.SetId(aws.StringValue(resp.Name)) + d.Set("arn", resp.Arn) + d.Set("deletion_protection", resp.DeletionProtection) + + return nil +} diff --git a/aws/data_source_aws_qldb_ledger_test.go b/aws/data_source_aws_qldb_ledger_test.go new file mode 100644 index 000000000000..f35e303d780d --- /dev/null +++ b/aws/data_source_aws_qldb_ledger_test.go @@ -0,0 +1,52 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccDataSourceAwsQLDBLedger(t *testing.T) { + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(7)) // QLDB name cannot be longer than 32 characters + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsQLDBLedgerConfig(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair("data.aws_qldb_ledger.by_name", "arn", "aws_qldb_ledger.tf_test", "arn"), + resource.TestCheckResourceAttrPair("data.aws_qldb_ledger.by_name", "deletion_protection", "aws_qldb_ledger.tf_test", "deletion_protection"), + resource.TestCheckResourceAttrPair("data.aws_qldb_ledger.by_name", "name", "aws_qldb_ledger.tf_test", "name"), + ), + }, + }, + }) +} + +func testAccDataSourceAwsQLDBLedgerConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_qldb_ledger" "tf_wrong1" { + name = "%[1]s1" + deletion_protection = false +} + +resource "aws_qldb_ledger" "tf_test" { + name = "%[1]s2" + deletion_protection = false +} + +resource "aws_qldb_ledger" "tf_wrong2" { + name = "%[1]s3" + deletion_protection = false +} + +data "aws_qldb_ledger" "by_name" { + name = "${aws_qldb_ledger.tf_test.name}" +} +`, rName) +} diff --git a/aws/internal/keyvaluetags/generators/listtags/main.go b/aws/internal/keyvaluetags/generators/listtags/main.go index 813fbc191943..7fc867b70596 100644 --- a/aws/internal/keyvaluetags/generators/listtags/main.go +++ b/aws/internal/keyvaluetags/generators/listtags/main.go @@ -69,6 +69,7 @@ var serviceNames = []string{ "neptune", "opsworks", "organizations", + "qldb", "rds", "route53resolver", "sagemaker", diff --git a/aws/internal/keyvaluetags/generators/servicetags/main.go b/aws/internal/keyvaluetags/generators/servicetags/main.go index 327e567bbc90..9fae3fa30d91 100644 --- a/aws/internal/keyvaluetags/generators/servicetags/main.go +++ b/aws/internal/keyvaluetags/generators/servicetags/main.go @@ -112,6 +112,7 @@ var mapServiceNames = []string{ "mediapackage", "mq", "opsworks", + "qldb", "pinpoint", "resourcegroups", "securityhub", diff --git a/aws/internal/keyvaluetags/generators/updatetags/main.go b/aws/internal/keyvaluetags/generators/updatetags/main.go index 11fc44a7877c..a95f9329b237 100644 --- a/aws/internal/keyvaluetags/generators/updatetags/main.go +++ b/aws/internal/keyvaluetags/generators/updatetags/main.go @@ -73,6 +73,7 @@ var serviceNames = []string{ "neptune", "opsworks", "organizations", + "qldb", "ram", "rds", "redshift", diff --git a/aws/internal/keyvaluetags/list_tags_gen.go b/aws/internal/keyvaluetags/list_tags_gen.go index 426e545275b4..82228ac8861b 100644 --- a/aws/internal/keyvaluetags/list_tags_gen.go +++ b/aws/internal/keyvaluetags/list_tags_gen.go @@ -56,6 +56,7 @@ import ( "github.com/aws/aws-sdk-go/service/neptune" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/organizations" + "github.com/aws/aws-sdk-go/service/qldb" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/route53resolver" "github.com/aws/aws-sdk-go/service/sagemaker" @@ -953,6 +954,23 @@ func OrganizationsListTags(conn *organizations.Organizations, identifier string) return OrganizationsKeyValueTags(output.Tags), nil } +// QldbListTags lists qldb service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func QldbListTags(conn *qldb.QLDB, identifier string) (KeyValueTags, error) { + input := &qldb.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(input) + + if err != nil { + return New(nil), err + } + + return QldbKeyValueTags(output.Tags), nil +} + // RdsListTags lists rds 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 663cec634480..f0b049e22f83 100644 --- a/aws/internal/keyvaluetags/service_generation_customizations.go +++ b/aws/internal/keyvaluetags/service_generation_customizations.go @@ -67,6 +67,7 @@ import ( "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/organizations" "github.com/aws/aws-sdk-go/service/pinpoint" + "github.com/aws/aws-sdk-go/service/qldb" "github.com/aws/aws-sdk-go/service/ram" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" @@ -213,6 +214,8 @@ func ServiceClientType(serviceName string) string { funcType = reflect.TypeOf(organizations.New) case "pinpoint": funcType = reflect.TypeOf(pinpoint.New) + case "qldb": + funcType = reflect.TypeOf(qldb.New) case "ram": funcType = reflect.TypeOf(ram.New) case "rds": diff --git a/aws/internal/keyvaluetags/service_tags_gen.go b/aws/internal/keyvaluetags/service_tags_gen.go index 180e5394aae6..21e53e9b49fd 100644 --- a/aws/internal/keyvaluetags/service_tags_gen.go +++ b/aws/internal/keyvaluetags/service_tags_gen.go @@ -317,6 +317,16 @@ func PinpointKeyValueTags(tags map[string]*string) KeyValueTags { return New(tags) } +// QldbTags returns qldb service tags. +func (tags KeyValueTags) QldbTags() map[string]*string { + return aws.StringMap(tags.Map()) +} + +// QldbKeyValueTags creates KeyValueTags from qldb service tags. +func QldbKeyValueTags(tags map[string]*string) KeyValueTags { + return New(tags) +} + // ResourcegroupsTags returns resourcegroups service tags. func (tags KeyValueTags) ResourcegroupsTags() map[string]*string { return aws.StringMap(tags.Map()) diff --git a/aws/internal/keyvaluetags/update_tags_gen.go b/aws/internal/keyvaluetags/update_tags_gen.go index 983534cd5ff5..3fbfdef5b10b 100644 --- a/aws/internal/keyvaluetags/update_tags_gen.go +++ b/aws/internal/keyvaluetags/update_tags_gen.go @@ -62,6 +62,7 @@ import ( "github.com/aws/aws-sdk-go/service/neptune" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/organizations" + "github.com/aws/aws-sdk-go/service/qldb" "github.com/aws/aws-sdk-go/service/ram" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" @@ -2094,6 +2095,42 @@ func OrganizationsUpdateTags(conn *organizations.Organizations, identifier strin return nil } +// QldbUpdateTags updates qldb service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func QldbUpdateTags(conn *qldb.QLDB, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := New(oldTagsMap) + newTags := New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &qldb.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: aws.StringSlice(removedTags.Keys()), + } + + _, err := conn.UntagResource(input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %s", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &qldb.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: updatedTags.IgnoreAws().QldbTags(), + } + + _, err := conn.TagResource(input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %s", identifier, err) + } + } + + return nil +} + // RamUpdateTags updates ram 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 0655be2d4c65..45fbddccf071 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -241,6 +241,7 @@ func Provider() terraform.ResourceProvider { "aws_partition": dataSourceAwsPartition(), "aws_prefix_list": dataSourceAwsPrefixList(), "aws_pricing_product": dataSourceAwsPricingProduct(), + "aws_qldb_ledger": dataSourceAwsQLDBLedger(), "aws_ram_resource_share": dataSourceAwsRamResourceShare(), "aws_rds_cluster": dataSourceAwsRdsCluster(), "aws_redshift_cluster": dataSourceAwsRedshiftCluster(), @@ -634,6 +635,7 @@ func Provider() terraform.ResourceProvider { "aws_organizations_organizational_unit": resourceAwsOrganizationsOrganizationalUnit(), "aws_placement_group": resourceAwsPlacementGroup(), "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), + "aws_qldb_ledger": resourceAwsQLDBLedger(), "aws_quicksight_group": resourceAwsQuickSightGroup(), "aws_ram_principal_association": resourceAwsRamPrincipalAssociation(), "aws_ram_resource_association": resourceAwsRamResourceAssociation(), diff --git a/aws/resource_aws_qldb_ledger.go b/aws/resource_aws_qldb_ledger.go new file mode 100644 index 000000000000..0ae4325454ef --- /dev/null +++ b/aws/resource_aws_qldb_ledger.go @@ -0,0 +1,270 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/qldb" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceAwsQLDBLedger() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsQLDBLedgerCreate, + Read: resourceAwsQLDBLedgerRead, + Update: resourceAwsQLDBLedgerUpdate, + Delete: resourceAwsQLDBLedgerDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 32), + validation.StringMatch(regexp.MustCompile(`^[A-Za-z0-9_-]+`), "must contain only alphanumeric characters, underscores, and hyphens"), + ), + }, + + "deletion_protection": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceAwsQLDBLedgerCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).qldbconn + + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } else { + name = resource.PrefixedUniqueId("tf") + } + + if err := d.Set("name", name); err != nil { + return fmt.Errorf("error setting name: %s", err) + } + + // Create the QLDB Ledger + // The qldb.PermissionsModeAllowAll is currently hardcoded because AWS doesn't support changing the mode. + createOpts := &qldb.CreateLedgerInput{ + Name: aws.String(d.Get("name").(string)), + PermissionsMode: aws.String(qldb.PermissionsModeAllowAll), + DeletionProtection: aws.Bool(d.Get("deletion_protection").(bool)), + Tags: keyvaluetags.New(d.Get("tags").(map[string]interface{})).IgnoreAws().QldbTags(), + } + + log.Printf("[DEBUG] QLDB Ledger create config: %#v", *createOpts) + qldbResp, err := conn.CreateLedger(createOpts) + if err != nil { + return fmt.Errorf("Error creating QLDB Ledger: %s", err) + } + + // Set QLDB ledger name + d.SetId(*qldbResp.Name) + + log.Printf("[INFO] QLDB Ledger name: %s", d.Id()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{qldb.LedgerStateCreating}, + Target: []string{qldb.LedgerStateActive}, + Refresh: qldbLedgerRefreshStatusFunc(conn, d.Id()), + Timeout: 8 * time.Minute, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for QLDB Ledger status to be \"%s\": %s", qldb.LedgerStateActive, err) + } + + // Update our attributes and return + return resourceAwsQLDBLedgerRead(d, meta) +} + +func resourceAwsQLDBLedgerRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).qldbconn + + // Refresh the QLDB state + input := &qldb.DescribeLedgerInput{ + Name: aws.String(d.Id()), + } + + qldbLedger, err := conn.DescribeLedger(input) + + if isAWSErr(err, qldb.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] QLDB Ledger (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error describing QLDB Ledger (%s): %s", d.Id(), err) + } + + // QLDB stuff + if err := d.Set("name", qldbLedger.Name); err != nil { + return fmt.Errorf("error setting name: %s", err) + } + + if err := d.Set("deletion_protection", qldbLedger.DeletionProtection); err != nil { + return fmt.Errorf("error setting deletion protection: %s", err) + } + + // ARN + if err := d.Set("arn", qldbLedger.Arn); err != nil { + return fmt.Errorf("error setting ARN: %s", err) + } + + // Tags + log.Printf("[INFO] Fetching tags for %s", d.Id()) + tags, err := keyvaluetags.QldbListTags(conn, d.Get("arn").(string)) + if err != nil { + return fmt.Errorf("Error listing tags for QLDB Ledger: %s", err) + } + + if err := d.Set("tags", tags.IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + return nil +} + +func resourceAwsQLDBLedgerUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).qldbconn + + // Turn on partial mode + d.Partial(true) + + if d.HasChange("deletion_protection") { + val := d.Get("deletion_protection").(bool) + modifyOpts := &qldb.UpdateLedgerInput{ + Name: aws.String(d.Id()), + DeletionProtection: aws.Bool(val), + } + log.Printf( + "[INFO] Modifying deletion_protection QLDB attribute for %s: %#v", + d.Id(), modifyOpts) + if _, err := conn.UpdateLedger(modifyOpts); err != nil { + + return err + } + + d.SetPartial("deletion_protection") + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.QldbUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + + d.Partial(false) + return resourceAwsQLDBLedgerRead(d, meta) +} + +func resourceAwsQLDBLedgerDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).qldbconn + deleteLedgerOpts := &qldb.DeleteLedgerInput{ + Name: aws.String(d.Id()), + } + log.Printf("[INFO] Deleting QLDB Ledger: %s", d.Id()) + + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + _, err := conn.DeleteLedger(deleteLedgerOpts) + + if isAWSErr(err, qldb.ErrCodeResourceInUseException, "") { + return resource.RetryableError(err) + } + + if err != nil { + return resource.NonRetryableError(err) + } + + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.DeleteLedger(deleteLedgerOpts) + } + + if isAWSErr(err, qldb.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting QLDB Ledger (%s): %s", d.Id(), err) + } + + if err := waitForQLDBLedgerDeletion(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for QLDB Ledger (%s) deletion: %s", d.Id(), err) + } + + return nil +} + +func qldbLedgerRefreshStatusFunc(conn *qldb.QLDB, ledger string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &qldb.DescribeLedgerInput{ + Name: aws.String(ledger), + } + resp, err := conn.DescribeLedger(input) + if err != nil { + return nil, "failed", err + } + return resp, aws.StringValue(resp.State), nil + } +} + +func waitForQLDBLedgerDeletion(conn *qldb.QLDB, ledgerName string) error { + stateConf := resource.StateChangeConf{ + Pending: []string{qldb.LedgerStateCreating, + qldb.LedgerStateActive, + qldb.LedgerStateDeleting}, + Target: []string{""}, + Timeout: 5 * time.Minute, + MinTimeout: 1 * time.Second, + Refresh: func() (interface{}, string, error) { + resp, err := conn.DescribeLedger(&qldb.DescribeLedgerInput{ + Name: aws.String(ledgerName), + }) + + if isAWSErr(err, qldb.ErrCodeResourceNotFoundException, "") { + return 1, "", nil + } + + if err != nil { + return nil, qldb.ErrCodeResourceInUseException, err + } + + return resp, aws.StringValue(resp.State), nil + }, + } + + _, err := stateConf.WaitForState() + + return err +} diff --git a/aws/resource_aws_qldb_ledger_test.go b/aws/resource_aws_qldb_ledger_test.go new file mode 100644 index 000000000000..5eb90afb8b7d --- /dev/null +++ b/aws/resource_aws_qldb_ledger_test.go @@ -0,0 +1,241 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/qldb" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + + "github.com/aws/aws-sdk-go/aws" +) + +func init() { + resource.AddTestSweepers("aws_qldb_ledger", &resource.Sweeper{ + Name: "aws_qldb_ledger", + F: testSweepQLDBLedgers, + }) +} + +func testSweepQLDBLedgers(region string) error { + client, err := sharedClientForRegion(region) + + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + + conn := client.(*AWSClient).qldbconn + input := &qldb.ListLedgersInput{} + page, err := conn.ListLedgers(input) + + if err != nil { + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping QLDB Ledger sweep for %s: %s", region, err) + return nil + } + return fmt.Errorf("Error listing QLDB Ledgers: %s", err) + } + + for _, item := range page.Ledgers { + input := &qldb.DeleteLedgerInput{ + Name: item.Name, + } + name := aws.StringValue(item.Name) + + log.Printf("[INFO] Deleting QLDB Ledger: %s", name) + _, err = conn.DeleteLedger(input) + + if err != nil { + log.Printf("[ERROR] Failed to delete QLDB Ledger %s: %s", name, err) + continue + } + + if err := waitForQLDBLedgerDeletion(conn, name); err != nil { + log.Printf("[ERROR] Error waiting for QLDB Ledger (%s) deletion: %s", name, err) + } + } + + return nil +} + +func TestAccAWSQLDBLedger_basic(t *testing.T) { + var qldbCluster qldb.DescribeLedgerOutput + rInt := acctest.RandInt() + resourceName := "aws_qldb_ledger.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSQLDBLedgerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSQLDBLedgerConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSQLDBLedgerExists(resourceName, &qldbCluster), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "qldb", regexp.MustCompile(`ledger/.+`)), + resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile("test-ledger-[0-9]+")), + resource.TestCheckResourceAttr(resourceName, "permissions_mode", "ALLOW_ALL"), + resource.TestCheckResourceAttr(resourceName, "deletion_protection", "false"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAWSQLDBLedgerDestroy(s *terraform.State) error { + return testAccCheckAWSLedgerDestroyWithProvider(s, testAccProvider) +} + +func testAccCheckAWSLedgerDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { + conn := provider.Meta().(*AWSClient).qldbconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_qldb_ledger" { + continue + } + + // Try to find the Group + var err error + resp, err := conn.DescribeLedger( + &qldb.DescribeLedgerInput{ + Name: aws.String(rs.Primary.ID), + }) + + if err == nil { + if len(aws.StringValue(resp.Name)) != 0 && aws.StringValue(resp.Name) == rs.Primary.ID { + return fmt.Errorf("QLDB Ledger %s still exists", rs.Primary.ID) + } + } + + // Return nil if the cluster is already destroyed + if isAWSErr(err, qldb.ErrCodeResourceNotFoundException, "") { + continue + } + + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckAWSQLDBLedgerExists(n string, v *qldb.DescribeLedgerOutput) 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 QLDB Ledger ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).qldbconn + resp, err := conn.DescribeLedger(&qldb.DescribeLedgerInput{ + Name: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if *resp.Name == rs.Primary.ID { + *v = *resp + return nil + } + + return fmt.Errorf("QLDB Ledger (%s) not found", rs.Primary.ID) + } +} + +func testAccAWSQLDBLedgerConfig(n int) string { + return fmt.Sprintf(` +resource "aws_qldb_ledger" "test" { + name = "test-ledger-%d" + deletion_protection = false +} +`, n) +} + +func TestAccAWSQLDBLedger_Tags(t *testing.T) { + var cluster1, cluster2, cluster3 qldb.DescribeLedgerOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_qldb_ledger.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSQLDBLedgerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSQLDBLedgerConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSQLDBLedgerExists(resourceName, &cluster1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSQLDBLedgerConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSQLDBLedgerExists(resourceName, &cluster2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSQLDBLedgerConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSQLDBLedgerExists(resourceName, &cluster3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func testAccAWSQLDBLedgerConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_qldb_ledger" "test" { + name = %[1]q + deletion_protection = false + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccAWSQLDBLedgerConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_qldb_ledger" "test" { + name = %[1]q + deletion_protection = false + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/aws.erb b/website/aws.erb index 8b880c6a1b07..2b5faa26beb3 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2235,6 +2235,27 @@ +
  • + Quantum Ledger Database (QLDB) + +
  • QuickSight