Skip to content

Commit

Permalink
Merge pull request #28424 from DrFaust92/rds-snap-share
Browse files Browse the repository at this point in the history
r/db_snapshot - add support for sharing snapshots
  • Loading branch information
ewbankkit committed Mar 7, 2023
2 parents 35a4b59 + 215b1dc commit 72f6465
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 201 deletions.
3 changes: 3 additions & 0 deletions .changelog/28424.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/db_snapshot: Add `shared_accounts` argument
```
5 changes: 5 additions & 0 deletions internal/service/rds/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ const (
EventSubscriptionStatusModifying = "modifying"
)

const (
DBSnapshotAvailable = "available"
DBSnapshotCreating = "creating"
)

const (
ClusterEngineAurora = "aurora"
ClusterEngineAuroraMySQL = "aurora-mysql"
Expand Down
173 changes: 92 additions & 81 deletions internal/service/rds/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ package rds

import (
"context"
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"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/flex"
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 @@ -25,33 +25,33 @@ func ResourceSnapshot() *schema.Resource {
ReadWithoutTimeout: resourceSnapshotRead,
UpdateWithoutTimeout: resourceSnapshotUpdate,
DeleteWithoutTimeout: resourceSnapshotDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Timeouts: &schema.ResourceTimeout{
Read: schema.DefaultTimeout(20 * time.Minute),
Create: schema.DefaultTimeout(20 * time.Minute),
},

Schema: map[string]*schema.Schema{
"db_snapshot_identifier": {
"allocated_storage": {
Type: schema.TypeInt,
Computed: true,
},
"availability_zone": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Computed: true,
},
"db_instance_identifier": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"allocated_storage": {
Type: schema.TypeInt,
Computed: true,
},
"availability_zone": {
"db_snapshot_identifier": {
Type: schema.TypeString,
Computed: true,
Required: true,
ForceNew: true,
},
"db_snapshot_arn": {
Type: schema.TypeString,
Expand Down Expand Up @@ -89,6 +89,11 @@ func ResourceSnapshot() *schema.Resource {
Type: schema.TypeInt,
Computed: true,
},
"shared_accounts": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"source_db_snapshot_identifier": {
Type: schema.TypeString,
Computed: true,
Expand All @@ -109,12 +114,12 @@ func ResourceSnapshot() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
"vpc_id": {
Type: schema.TypeString,
Computed: true,
},
"tags": tftags.TagsSchema(),
"tags_all": tftags.TagsSchemaComputed(),
},

CustomizeDiff: verify.SetTagsDiff,
Expand All @@ -126,32 +131,38 @@ func resourceSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta in
conn := meta.(*conns.AWSClient).RDSConn()
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(tftags.New(ctx, d.Get("tags").(map[string]interface{})))
dBInstanceIdentifier := d.Get("db_instance_identifier").(string)

params := &rds.CreateDBSnapshotInput{
DBInstanceIdentifier: aws.String(dBInstanceIdentifier),
DBSnapshotIdentifier: aws.String(d.Get("db_snapshot_identifier").(string)),
dbSnapshotID := d.Get("db_snapshot_identifier").(string)
input := &rds.CreateDBSnapshotInput{
DBInstanceIdentifier: aws.String(d.Get("db_instance_identifier").(string)),
DBSnapshotIdentifier: aws.String(dbSnapshotID),
Tags: Tags(tags.IgnoreAWS()),
}

resp, err := conn.CreateDBSnapshotWithContext(ctx, params)
output, err := conn.CreateDBSnapshotWithContext(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating AWS DB Snapshot (%s): %s", dBInstanceIdentifier, err)
return sdkdiag.AppendErrorf(diags, "creating RDS DB Snapshot (%s): %s", dbSnapshotID, err)
}
d.SetId(aws.StringValue(resp.DBSnapshot.DBSnapshotIdentifier))

stateConf := &resource.StateChangeConf{
Pending: []string{"creating"},
Target: []string{"available"},
Refresh: resourceSnapshotStateRefreshFunc(ctx, d, meta),
Timeout: d.Timeout(schema.TimeoutRead),
MinTimeout: 10 * time.Second,
Delay: 30 * time.Second, // Wait 30 secs before starting

d.SetId(aws.StringValue(output.DBSnapshot.DBSnapshotIdentifier))

if err := waitDBSnapshotCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for RDS DB Snapshot (%s) create: %s", d.Id(), err)
}

_, err = stateConf.WaitForStateContext(ctx)
if err != nil {
return sdkdiag.AppendErrorf(diags, "creating AWS DB Snapshot (%s): waiting for completion: %s", dBInstanceIdentifier, err)
if v, ok := d.GetOk("shared_accounts"); ok && v.(*schema.Set).Len() > 0 {
input := &rds.ModifyDBSnapshotAttributeInput{
AttributeName: aws.String("restore"),
DBSnapshotIdentifier: aws.String(d.Id()),
ValuesToAdd: flex.ExpandStringSet(v.(*schema.Set)),
}

_, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err)
}
}

return append(diags, resourceSnapshotRead(ctx, d, meta)...)
Expand All @@ -163,29 +174,24 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

params := &rds.DescribeDBSnapshotsInput{
DBSnapshotIdentifier: aws.String(d.Id()),
}
resp, err := conn.DescribeDBSnapshotsWithContext(ctx, params)
snapshot, err := FindDBSnapshotByID(ctx, conn, d.Id())

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) {
log.Printf("[WARN] AWS DB Snapshot (%s) is already gone", d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] RDS DB Snapshot (%s) not found, removing from state", d.Id())
d.SetId("")
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "describing AWS DB Snapshot %s: %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s): %s", d.Id(), err)
}

snapshot := resp.DBSnapshots[0]

arn := aws.StringValue(snapshot.DBSnapshotArn)
d.Set("db_snapshot_identifier", snapshot.DBSnapshotIdentifier)
d.Set("db_instance_identifier", snapshot.DBInstanceIdentifier)
d.Set("allocated_storage", snapshot.AllocatedStorage)
d.Set("availability_zone", snapshot.AvailabilityZone)
d.Set("db_instance_identifier", snapshot.DBInstanceIdentifier)
d.Set("db_snapshot_arn", arn)
d.Set("db_snapshot_identifier", snapshot.DBSnapshotIdentifier)
d.Set("encrypted", snapshot.Encrypted)
d.Set("engine", snapshot.Engine)
d.Set("engine_version", snapshot.EngineVersion)
Expand Down Expand Up @@ -217,33 +223,47 @@ func resourceSnapshotRead(ctx context.Context, d *schema.ResourceData, meta inte
return sdkdiag.AppendErrorf(diags, "setting tags_all: %s", err)
}

return diags
}

func resourceSnapshotDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).RDSConn()

log.Printf("[DEBUG] Deleting RDS DB Snapshot: %s", d.Id())
_, err := conn.DeleteDBSnapshotWithContext(ctx, &rds.DeleteDBSnapshotInput{
input := &rds.DescribeDBSnapshotAttributesInput{
DBSnapshotIdentifier: aws.String(d.Id()),
})

if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) {
return diags
}

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

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Snapshot (%s): %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "reading RDS DB Snapshot (%s) attribute: %s", d.Id(), err)
}

d.Set("shared_accounts", flex.FlattenStringSet(output.DBSnapshotAttributesResult.DBSnapshotAttributes[0].AttributeValues))

return diags
}

func resourceSnapshotUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).RDSConn()

if d.HasChange("shared_accounts") {
o, n := d.GetChange("shared_accounts")
os := o.(*schema.Set)
ns := n.(*schema.Set)

additionList := ns.Difference(os)
removalList := os.Difference(ns)

input := &rds.ModifyDBSnapshotAttributeInput{
AttributeName: aws.String("restore"),
DBSnapshotIdentifier: aws.String(d.Id()),
ValuesToAdd: flex.ExpandStringSet(additionList),
ValuesToRemove: flex.ExpandStringSet(removalList),
}

_, err := conn.ModifyDBSnapshotAttributeWithContext(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "modifying RDS DB Snapshot (%s) attribute: %s", d.Id(), err)
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

Expand All @@ -255,31 +275,22 @@ func resourceSnapshotUpdate(ctx context.Context, d *schema.ResourceData, meta in
return diags
}

func resourceSnapshotStateRefreshFunc(ctx context.Context,
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
conn := meta.(*conns.AWSClient).RDSConn()

opts := &rds.DescribeDBSnapshotsInput{
DBSnapshotIdentifier: aws.String(d.Id()),
}

log.Printf("[DEBUG] DB Snapshot describe configuration: %#v", opts)

resp, err := conn.DescribeDBSnapshotsWithContext(ctx, opts)
if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) {
return nil, "", nil
}
if err != nil {
return nil, "", fmt.Errorf("Error retrieving DB Snapshots: %s", err)
}
func resourceSnapshotDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).RDSConn()

if len(resp.DBSnapshots) != 1 {
return nil, "", fmt.Errorf("No snapshots returned for %s", d.Id())
}
log.Printf("[DEBUG] Deleting RDS DB Snapshot: %s", d.Id())
_, err := conn.DeleteDBSnapshotWithContext(ctx, &rds.DeleteDBSnapshotInput{
DBSnapshotIdentifier: aws.String(d.Id()),
})

snapshot := resp.DBSnapshots[0]
if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBSnapshotNotFoundFault) {
return diags
}

return resp, *snapshot.Status, nil
if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting RDS DB Snapshot (%s): %s", d.Id(), err)
}

return diags
}
Loading

0 comments on commit 72f6465

Please sign in to comment.