Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement ListTags for all taggable Lightsail resources #37711

Merged
merged 10 commits into from
May 29, 2024
39 changes: 39 additions & 0 deletions .changelog/37711.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
```release-note:bug
resource/aws_lightsail_database: Prevent destroy failure when resource is already deleted outside Terraform
```

```release-note:bug
resource/aws_lightsail_key_pair: Prevent destroy failure when resource is already deleted outside Terraform
```

```release-note:bug
resource/aws_lightsail_lb: Prevent destroy failure when resource is already deleted outside Terraform
```

```release-note:enhancement
resource/aws_lightsail_bucket: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_certificate: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_container_service: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_database: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_distribution: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_key_pair: Add support to `ListTags` function for proper key-only tag handling
```

```release-note:enhancement
resource/aws_lightsail_lb: Add support to `ListTags` function for proper key-only tag handling
```
19 changes: 19 additions & 0 deletions internal/errs/errs.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,22 @@ func As[T error](err error) (T, bool) {
ok := errors.As(err, &as)
return as, ok
}

var _ ErrorWithErrorMessage = &ErrorWithMessage{}

// ErrorWithMessage is a simple error type that implements the errorMessager
type ErrorWithMessage struct {
error
}

func (e *ErrorWithMessage) ErrorMessage() string {
if e == nil || e.error == nil {
return ""
}
return e.Error()
}

// NewErrorWithMessage returns a new ErrorWithMessage
func NewErrorWithMessage(err error) *ErrorWithMessage {
return &ErrorWithMessage{error: err}
}
2 changes: 1 addition & 1 deletion internal/service/lightsail/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
)

// @SDKResource("aws_lightsail_bucket", name="Bucket")
// @Tags(identifierAttribute="id")
// @Tags(identifierAttribute="id", resourceType="Bucket")
func ResourceBucket() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceBucketCreate,
Expand Down
56 changes: 54 additions & 2 deletions internal/service/lightsail/bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,58 @@ func TestAccLightsailBucket_tags(t *testing.T) {
})
}

func TestAccLightsailBucket_keyOnlyTags(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lightsail_bucket.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, strings.ToLower(lightsail.ServiceID))
testAccPreCheck(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, strings.ToLower(lightsail.ServiceID)),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckBucketDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccBucketConfig_tags1(rName, acctest.CtKey1, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
names.AttrForceDelete,
},
},
{
Config: testAccBucketConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
{
Config: testAccBucketConfig_tags1(rName, acctest.CtKey2, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckBucketExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
},
})
}

func testAccCheckBucketExists(ctx context.Context, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand Down Expand Up @@ -285,7 +337,7 @@ resource "aws_lightsail_bucket" "test" {
`, rName)
}

func testAccBucketConfig_bundleId(rName string, rBundleId string) string {
func testAccBucketConfig_bundleId(rName, rBundleId string) string {
return fmt.Sprintf(`
resource "aws_lightsail_bucket" "test" {
name = %[1]q
Expand All @@ -294,7 +346,7 @@ resource "aws_lightsail_bucket" "test" {
`, rName, rBundleId)
}

func testAccBucketConfig_tags1(rName string, tagKey1, tagValue1 string) string {
func testAccBucketConfig_tags1(rName, tagKey1, tagValue1 string) string {
return fmt.Sprintf(`
resource "aws_lightsail_bucket" "test" {
name = %[1]q
Expand Down
2 changes: 1 addition & 1 deletion internal/service/lightsail/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

// @SDKResource("aws_lightsail_certificate", name="Certificate")
// @Tags(identifierAttribute="id")
// @Tags(identifierAttribute="id", resourceType="Certificate")
func ResourceCertificate() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceCertificateCreate,
Expand Down
58 changes: 54 additions & 4 deletions internal/service/lightsail/certificate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,56 @@ func TestAccLightsailCertificate_tags(t *testing.T) {
})
}

func TestAccLightsailCertificate_keyOnlyTags(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lightsail_certificate.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
domainName := acctest.ACMCertificateRandomSubDomain(acctest.RandomDomainName())

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, strings.ToLower(lightsail.ServiceID))
testAccPreCheck(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, strings.ToLower(lightsail.ServiceID)),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckCertificateDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccCertificateConfig_tags1(rName, domainName, acctest.CtKey1, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckCertificateExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccCertificateConfig_tags2(rName, domainName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckCertificateExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
{
Config: testAccCertificateConfig_tags1(rName, domainName, acctest.CtKey2, ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckCertificateExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
},
})
}

func TestAccLightsailCertificate_disappears(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lightsail_certificate.test"
Expand Down Expand Up @@ -275,7 +325,7 @@ func testAccCheckCertificateExists(ctx context.Context, n string) resource.TestC
}
}

func testAccCertificateConfig_basic(rName string, domainName string) string {
func testAccCertificateConfig_basic(rName, domainName string) string {
return fmt.Sprintf(`
resource "aws_lightsail_certificate" "test" {
name = %[1]q
Expand All @@ -284,7 +334,7 @@ resource "aws_lightsail_certificate" "test" {
`, rName, domainName)
}

func testAccCertificateConfig_subjectAlternativeNames(rName string, domainName string, san string) string {
func testAccCertificateConfig_subjectAlternativeNames(rName, domainName, san string) string {
return fmt.Sprintf(`
resource "aws_lightsail_certificate" "test" {
name = %[1]q
Expand All @@ -294,7 +344,7 @@ resource "aws_lightsail_certificate" "test" {
`, rName, domainName, san)
}

func testAccCertificateConfig_tags1(resourceName string, domainName string, tagKey1, tagValue1 string) string {
func testAccCertificateConfig_tags1(resourceName, domainName, tagKey1, tagValue1 string) string {
return fmt.Sprintf(`
resource "aws_lightsail_certificate" "test" {
name = %[1]q
Expand All @@ -306,7 +356,7 @@ resource "aws_lightsail_certificate" "test" {
`, resourceName, domainName, tagKey1, tagValue1)
}

func testAccCertificateConfig_tags2(resourceName, domainName string, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
func testAccCertificateConfig_tags2(resourceName, domainName, tagKey1, tagValue1, tagKey2, tagValue2 string) string {
return fmt.Sprintf(`
resource "aws_lightsail_certificate" "test" {
name = %[1]q
Expand Down
2 changes: 1 addition & 1 deletion internal/service/lightsail/container_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

// @SDKResource("aws_lightsail_container_service", name="Container Service")
// @Tags(identifierAttribute="id")
// @Tags(identifierAttribute="id", resourceType="ContainerService")
func ResourceContainerService() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceContainerServiceCreate,
Expand Down
49 changes: 49 additions & 0 deletions internal/service/lightsail/container_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,55 @@ func TestAccLightsailContainerService_tags(t *testing.T) {
})
}

func TestAccLightsailContainerService_keyOnlyTags(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_lightsail_container_service.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, strings.ToLower(lightsail.ServiceID))
testAccPreCheck(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, strings.ToLower(lightsail.ServiceID)),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckContainerServiceDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccContainerServiceConfig_tags1(rName, acctest.CtKey1, ""),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckContainerServiceExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccContainerServiceConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, ""),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckContainerServiceExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
{
Config: testAccContainerServiceConfig_tags1(rName, acctest.CtKey2, ""),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckContainerServiceExists(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, ""),
),
},
},
})
}

func testAccCheckContainerServiceDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).LightsailClient(ctx)
Expand Down
37 changes: 36 additions & 1 deletion internal/service/lightsail/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import (
"github.com/aws/aws-sdk-go-v2/service/lightsail"
"github.com/aws/aws-sdk-go-v2/service/lightsail/types"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
"github.com/hashicorp/terraform-provider-aws/names"
)
Expand All @@ -27,7 +29,7 @@ const (
)

// @SDKResource("aws_lightsail_database", name="Database")
// @Tags(identifierAttribute="id")
// @Tags(identifierAttribute="id", resourceType="Database")
func ResourceDatabase() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceDatabaseCreate,
Expand Down Expand Up @@ -400,6 +402,10 @@ func resourceDatabaseDelete(ctx context.Context, d *schema.ResourceData, meta in

// Some Operations can complete before the Database enters the Available state. Added a waiter to make sure the Database is available before continuing.
if _, err := waitDatabaseModified(ctx, conn, aws.String(d.Id())); err != nil {
if IsANotFoundError(err) {
return diags
}

return sdkdiag.AppendErrorf(diags, "waiting for Lightsail Relational Database (%s) to become available: %s", d.Id(), err)
}

Expand All @@ -420,6 +426,10 @@ func resourceDatabaseDelete(ctx context.Context, d *schema.ResourceData, meta in

output, err := conn.DeleteRelationalDatabase(ctx, input)

if IsANotFoundError(err) {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting Lightsail Relational Database (%s): %s", d.Id(), err)
}
Expand All @@ -440,3 +450,28 @@ func resourceDatabaseImport(ctx context.Context, d *schema.ResourceData, meta in
d.Set("skip_final_snapshot", true)
return []*schema.ResourceData{d}, nil
}

func FindDatabaseById(ctx context.Context, conn *lightsail.Client, id string) (*types.RelationalDatabase, error) {
in := &lightsail.GetRelationalDatabaseInput{
RelationalDatabaseName: aws.String(id),
}

out, err := conn.GetRelationalDatabase(ctx, in)

if IsANotFoundError(err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: in,
}
}

if err != nil {
return nil, err
}

if out == nil || out.RelationalDatabase == nil {
return nil, tfresource.NewEmptyResultError(in)
}

return out.RelationalDatabase, nil
}
Loading
Loading