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: option to filter cluster snapshots by tags #31602

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/31602.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
data-source/aws_db_cluster_snapshot: Add `tags` argument
```
52 changes: 38 additions & 14 deletions internal/service/rds/cluster_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"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"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
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"
Expand All @@ -26,7 +27,7 @@ import (

const clusterSnapshotCreateTimeout = 2 * time.Minute

// @SDKResource("aws_db_cluster_snapshot", name="Cluster Snapshot")
// @SDKResource("aws_db_cluster_snapshot", name="DB Cluster Snapshot")
// @Tags(identifierAttribute="db_cluster_snapshot_arn")
func ResourceClusterSnapshot() *schema.Resource {
return &schema.Resource{
Expand Down Expand Up @@ -219,38 +220,61 @@ func FindDBClusterSnapshotByID(ctx context.Context, conn *rds.RDS, id string) (*
input := &rds.DescribeDBClusterSnapshotsInput{
DBClusterSnapshotIdentifier: aws.String(id),
}
output, err := findDBClusterSnapshot(ctx, conn, input)

output, err := conn.DescribeDBClusterSnapshotsWithContext(ctx, input)
if err != nil {
return nil, err
}

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterSnapshotNotFoundFault) {
// Eventual consistency check.
if aws.StringValue(output.DBClusterSnapshotIdentifier) != id {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

return output, nil
}

func findDBClusterSnapshot(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClusterSnapshotsInput) (*rds.DBClusterSnapshot, error) {
output, err := findDBClusterSnapshots(ctx, conn, input, tfslices.PredicateTrue[*rds.DBClusterSnapshot]())

if err != nil {
return nil, err
}

if output == nil || len(output.DBClusterSnapshots) == 0 || output.DBClusterSnapshots[0] == nil {
return nil, tfresource.NewEmptyResultError(input)
}
return tfresource.AssertSinglePtrResult(output)
}

if count := len(output.DBClusterSnapshots); count > 1 {
return nil, tfresource.NewTooManyResultsError(count, input)
}
func findDBClusterSnapshots(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBClusterSnapshotsInput, filter tfslices.Predicate[*rds.DBClusterSnapshot]) ([]*rds.DBClusterSnapshot, error) {
var output []*rds.DBClusterSnapshot

dbClusterSnapshot := output.DBClusterSnapshots[0]
err := conn.DescribeDBClusterSnapshotsPagesWithContext(ctx, input, func(page *rds.DescribeDBClusterSnapshotsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

// Eventual consistency check.
if aws.StringValue(dbClusterSnapshot.DBClusterSnapshotIdentifier) != id {
for _, v := range page.DBClusterSnapshots {
if v != nil && filter(v) {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBClusterSnapshotNotFoundFault) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

return dbClusterSnapshot, nil
if err != nil {
return nil, err
}

return output, nil
}

func statusDBClusterSnapshot(ctx context.Context, conn *rds.RDS, id string) retry.StateRefreshFunc {
Expand Down
43 changes: 23 additions & 20 deletions internal/service/rds/cluster_snapshot_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/names"
)

// @SDKDataSource("aws_db_cluster_snapshot")
// @SDKDataSource("aws_db_cluster_snapshot", name="DB Cluster Snapshot")
// @Tags
func DataSourceClusterSnapshot() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceClusterSnapshotRead,
Expand All @@ -33,18 +36,16 @@ func DataSourceClusterSnapshot() *schema.Resource {
Computed: true,
},
"db_cluster_identifier": {
Type: schema.TypeString,
Optional: true,
AtLeastOneOf: []string{"db_cluster_identifier", "db_cluster_snapshot_identifier"},
Type: schema.TypeString,
Optional: true,
},
"db_cluster_snapshot_arn": {
Type: schema.TypeString,
Computed: true,
},
"db_cluster_snapshot_identifier": {
Type: schema.TypeString,
Optional: true,
AtLeastOneOf: []string{"db_cluster_identifier", "db_cluster_snapshot_identifier"},
Type: schema.TypeString,
Optional: true,
},
"engine": {
Type: schema.TypeString,
Expand Down Expand Up @@ -101,7 +102,7 @@ func DataSourceClusterSnapshot() *schema.Resource {
Type: schema.TypeBool,
Computed: true,
},
"tags": tftags.TagsSchemaComputed(),
names.AttrTags: tftags.TagsSchemaComputed(),
"vpc_id": {
Type: schema.TypeString,
Computed: true,
Expand All @@ -113,7 +114,6 @@ func DataSourceClusterSnapshot() *schema.Resource {
func dataSourceClusterSnapshotRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).RDSConn(ctx)
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

input := &rds.DescribeDBClusterSnapshotsInput{
IncludePublic: aws.Bool(d.Get("include_public").(bool)),
Expand All @@ -132,25 +132,32 @@ func dataSourceClusterSnapshotRead(ctx context.Context, d *schema.ResourceData,
input.SnapshotType = aws.String(v.(string))
}

output, err := conn.DescribeDBClusterSnapshotsWithContext(ctx, input)
f := tfslices.PredicateTrue[*rds.DBClusterSnapshot]()
if tags := getTagsIn(ctx); len(tags) > 0 {
f = func(v *rds.DBClusterSnapshot) bool {
return KeyValueTags(ctx, v.TagList).ContainsAll(KeyValueTags(ctx, tags))
}
}

snapshots, err := findDBClusterSnapshots(ctx, conn, input, f)

if err != nil {
return sdkdiag.AppendErrorf(diags, "reading RDS Cluster Snapshots: %s", err)
return sdkdiag.AppendErrorf(diags, "reading RDS DB Cluster Snapshots: %s", err)
}

if len(output.DBClusterSnapshots) < 1 {
if len(snapshots) < 1 {
return sdkdiag.AppendErrorf(diags, "Your query returned no results. Please change your search criteria and try again.")
}

var snapshot *rds.DBClusterSnapshot
if len(output.DBClusterSnapshots) > 1 {
if len(snapshots) > 1 {
if d.Get("most_recent").(bool) {
snapshot = mostRecentClusterSnapshot(output.DBClusterSnapshots)
snapshot = mostRecentClusterSnapshot(snapshots)
} else {
return sdkdiag.AppendErrorf(diags, "Your query returned more than one result. Please try a more specific search criteria.")
}
} else {
snapshot = output.DBClusterSnapshots[0]
snapshot = snapshots[0]
}

d.SetId(aws.StringValue(snapshot.DBClusterSnapshotIdentifier))
Expand All @@ -173,11 +180,7 @@ func dataSourceClusterSnapshotRead(ctx context.Context, d *schema.ResourceData,
d.Set("storage_encrypted", snapshot.StorageEncrypted)
d.Set("vpc_id", snapshot.VpcId)

tags := KeyValueTags(ctx, snapshot.TagList)

if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return sdkdiag.AppendErrorf(diags, "setting tags: %s", err)
}
setTagsOut(ctx, snapshot.TagList)

return diags
}
Expand Down
61 changes: 59 additions & 2 deletions internal/service/rds/cluster_snapshot_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ func TestAccRDSClusterSnapshotDataSource_dbClusterSnapshotIdentifier(t *testing.
resource.TestCheckResourceAttrPair(dataSourceName, "source_db_cluster_snapshot_arn", resourceName, "source_db_cluster_snapshot_arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "status", resourceName, "status"),
resource.TestCheckResourceAttrPair(dataSourceName, "storage_encrypted", resourceName, "storage_encrypted"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrPair(dataSourceName, "vpc_id", resourceName, "vpc_id"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags", resourceName, "tags"),
),
},
},
Expand Down Expand Up @@ -79,8 +79,8 @@ func TestAccRDSClusterSnapshotDataSource_dbClusterIdentifier(t *testing.T) {
resource.TestCheckResourceAttrPair(dataSourceName, "source_db_cluster_snapshot_arn", resourceName, "source_db_cluster_snapshot_arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "status", resourceName, "status"),
resource.TestCheckResourceAttrPair(dataSourceName, "storage_encrypted", resourceName, "storage_encrypted"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttrPair(dataSourceName, "vpc_id", resourceName, "vpc_id"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags", resourceName, "tags"),
),
},
},
Expand Down Expand Up @@ -109,6 +109,31 @@ func TestAccRDSClusterSnapshotDataSource_mostRecent(t *testing.T) {
})
}

func TestAccRDSClusterSnapshotDataSource_tags(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
dataSourceName := "data.aws_db_cluster_snapshot.test"
resourceName := "aws_db_cluster_snapshot.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccClusterSnapshotDataSourceConfig_tags(rName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(dataSourceName, "db_cluster_identifier", resourceName, "db_cluster_identifier"),
resource.TestCheckResourceAttrPair(dataSourceName, "db_cluster_snapshot_arn", resourceName, "db_cluster_snapshot_arn"),
resource.TestCheckResourceAttrPair(dataSourceName, "db_cluster_snapshot_identifier", resourceName, "db_cluster_snapshot_identifier"),
resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"),
resource.TestCheckResourceAttr(dataSourceName, "tags.Name", rName),
),
},
},
})
}

func testAccClusterSnapshotDataSourceConfig_clusterSnapshotIdentifier(rName string) string {
return acctest.ConfigCompose(testAccClusterSnapshotConfig_base(rName), fmt.Sprintf(`
resource "aws_db_cluster_snapshot" "test" {
Expand Down Expand Up @@ -161,3 +186,35 @@ data "aws_db_cluster_snapshot" "test" {
}
`, rName))
}

func testAccClusterSnapshotDataSourceConfig_tags(rName string) string {
return acctest.ConfigCompose(testAccClusterSnapshotConfig_base(rName), fmt.Sprintf(`
resource "aws_db_cluster_snapshot" "incorrect" {
db_cluster_identifier = aws_rds_cluster.test.id
db_cluster_snapshot_identifier = "%[1]s-incorrect"

tags = {
Name = "%[1]s-incorrect"
Test = "true"
}
}

resource "aws_db_cluster_snapshot" "test" {
db_cluster_identifier = aws_db_cluster_snapshot.incorrect.db_cluster_identifier
db_cluster_snapshot_identifier = %[1]q

tags = {
Name = %[1]q
Test = "true"
}
}

data "aws_db_cluster_snapshot" "test" {
tags = {
Name = %[1]q
}

depends_on = [aws_db_cluster_snapshot.test]
}
`, rName))
}
4 changes: 3 additions & 1 deletion internal/service/rds/service_package_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions internal/service/rds/snapshot_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
"github.com/hashicorp/terraform-provider-aws/internal/slices"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/names"
)

// @SDKDataSource("aws_db_snapshot", name="DB Snapshot")
Expand Down Expand Up @@ -116,7 +117,7 @@ func DataSourceSnapshot() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"tags": tftags.TagsSchemaComputed(),
names.AttrTags: tftags.TagsSchemaComputed(),
"vpc_id": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -146,7 +147,7 @@ func dataSourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta in
input.SnapshotType = aws.String(v.(string))
}

f := slices.PredicateTrue[*rds.DBSnapshot]()
f := tfslices.PredicateTrue[*rds.DBSnapshot]()
if tags := getTagsIn(ctx); len(tags) > 0 {
f = func(v *rds.DBSnapshot) bool {
return KeyValueTags(ctx, v.TagList).ContainsAll(KeyValueTags(ctx, tags))
Expand Down
7 changes: 2 additions & 5 deletions website/docs/d/db_cluster_snapshot.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,18 @@ resource "aws_rds_cluster_instance" "aurora" {
This data source supports the following arguments:

* `most_recent` - (Optional) If more than one result is returned, use the most recent Snapshot.

* `db_cluster_identifier` - (Optional) Returns the list of snapshots created by the specific db_cluster

* `db_cluster_snapshot_identifier` - (Optional) Returns information on a specific snapshot_id.

* `snapshot_type` - (Optional) Type of snapshots to be returned. If you don't specify a SnapshotType
value, then both automated and manual DB cluster snapshots are returned. Shared and public DB Cluster Snapshots are not
included in the returned results by default. Possible values are, `automated`, `manual`, `shared`, `public` and `awsbackup`.

* `include_shared` - (Optional) Set this value to true to include shared manual DB Cluster Snapshots from other
AWS accounts that this AWS account has been given permission to copy or restore, otherwise set this value to false.
The default is `false`.

* `include_public` - (Optional) Set this value to true to include manual DB Cluster Snapshots that are public and can be
copied or restored by any AWS account, otherwise set this value to false. The default is `false`.
* `tags` - (Optional) Mapping of tags, each pair of which must exactly match
a pair on the desired DB cluster snapshot.

## Attribute Reference

Expand Down