Skip to content

Commit

Permalink
Merge pull request #37716 from nikhil-goenka/f/aws_docdb_cluster
Browse files Browse the repository at this point in the history
aws_docdb_cluster
  • Loading branch information
johnsonaj authored Jun 14, 2024
2 parents 46b15e9 + 9ce36ed commit 67a977e
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/37716.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_docdb_cluster: Add `restore_to_point_in_time` argument
```
96 changes: 96 additions & 0 deletions internal/service/docdb/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,43 @@ func ResourceCluster() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"restore_to_point_in_time": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"restore_to_time": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: verify.ValidUTCTimestamp,
ConflictsWith: []string{"restore_to_point_in_time.0.use_latest_restorable_time"},
},
"restore_type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice(RestoreType_Values(), false),
},
"source_cluster_identifier": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"use_latest_restorable_time": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"restore_to_point_in_time.0.restore_to_time"},
},
},
},
ConflictsWith: []string{
"snapshot_identifier",
},
},
"skip_final_snapshot": {
Type: schema.TypeBool,
Optional: true,
Expand All @@ -238,6 +275,9 @@ func ResourceCluster() *schema.Resource {
// allow snapshot_idenfitier to be removed without forcing re-creation
return new == ""
},
ConflictsWith: []string{
"restore_to_point_in_time",
},
},
names.AttrStorageEncrypted: {
Type: schema.TypeBool,
Expand Down Expand Up @@ -360,6 +400,62 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int
if err != nil {
return sdkdiag.AppendErrorf(diags, "creating DocumentDB Cluster (restore from snapshot) (%s): %s", identifier, err)
}
} else if v, ok := d.GetOk("restore_to_point_in_time"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
tfMap := v.([]interface{})[0].(map[string]interface{})
input := &docdb.RestoreDBClusterToPointInTimeInput{
DBClusterIdentifier: aws.String(identifier),
SourceDBClusterIdentifier: aws.String(tfMap["source_cluster_identifier"].(string)),
DeletionProtection: aws.Bool(d.Get(names.AttrDeletionProtection).(bool)),
Tags: getTagsIn(ctx),
}

if v, ok := tfMap["restore_to_time"].(string); ok && v != "" {
t, _ := time.Parse(time.RFC3339, v)
input.RestoreToTime = aws.Time(t)
}

if v, ok := tfMap["use_latest_restorable_time"].(bool); ok && v {
input.UseLatestRestorableTime = aws.Bool(v)
}

if input.RestoreToTime == nil && input.UseLatestRestorableTime == nil {
return sdkdiag.AppendErrorf(diags, `Either "restore_to_time" or "use_latest_restorable_time" must be set`)
}

if v, ok := d.GetOk("db_subnet_group_name"); ok {
input.DBSubnetGroupName = aws.String(v.(string))
}

if v, ok := d.GetOk("enabled_cloudwatch_logs_exports"); ok && len(v.([]interface{})) > 0 {
input.EnableCloudwatchLogsExports = flex.ExpandStringValueList(v.([]interface{}))
}

if v, ok := tfMap["restore_type"].(string); ok {
input.RestoreType = aws.String(v)
}

if v, ok := d.GetOk(names.AttrKMSKeyID); ok {
input.KmsKeyId = aws.String(v.(string))
}

if v, ok := d.GetOk(names.AttrPort); ok {
input.Port = aws.Int32(int32(v.(int)))
}

if v, ok := d.GetOk(names.AttrStorageType); ok {
input.StorageType = aws.String(v.(string))
}

if v := d.Get(names.AttrVPCSecurityGroupIDs).(*schema.Set); v.Len() > 0 {
input.VpcSecurityGroupIds = flex.ExpandStringValueSet(v)
}

_, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) {
return conn.RestoreDBClusterToPointInTime(ctx, input)
}, errCodeInvalidParameterValue, "IAM role ARN value is invalid or does not include the required permissions")
if err != nil {
return sdkdiag.AppendErrorf(diags, "creating DocumentDB Cluster (restore to point-in-time) (%s): %s", identifier, err)
}
} else {
// Secondary DocDB clusters part of a global cluster will not supply the master_password
if _, ok := d.GetOk("global_cluster_identifier"); !ok {
Expand Down
76 changes: 76 additions & 0 deletions internal/service/docdb/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,43 @@ func TestAccDocDBCluster_backupsUpdate(t *testing.T) {
})
}

func TestAccDocDBCluster_pointInTimeRestore(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster awstypes.DBCluster
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
sourceResourceName := "aws_docdb_cluster.test"
resourceName := "aws_docdb_cluster.restore"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.DocDBServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckClusterDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccClusterConfig_pointInTimeRestoreSource(rName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckClusterExists(ctx, sourceResourceName, &dbCluster),
testAccCheckClusterExists(ctx, resourceName, &dbCluster),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
names.AttrAllowMajorVersionUpgrade,
names.AttrApplyImmediately,
names.AttrFinalSnapshotIdentifier,
"master_password",
"restore_to_point_in_time",
"skip_final_snapshot",
},
},
},
})
}

func TestAccDocDBCluster_port(t *testing.T) {
ctx := acctest.Context(t)
var dbCluster1, dbCluster2 awstypes.DBCluster
Expand Down Expand Up @@ -1233,6 +1270,45 @@ resource "aws_docdb_cluster" "test" {
`, rName, port))
}

func testAccClusterConfig_baseForPITR(rName string) string {
return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(`
resource "aws_docdb_cluster" "test" {
cluster_identifier = %[1]q
availability_zones = [
data.aws_availability_zones.available.names[0],
data.aws_availability_zones.available.names[1],
data.aws_availability_zones.available.names[2]
]
master_password = "avoid-plaintext-passwords"
master_username = "tfacctest"
skip_final_snapshot = true
enabled_cloudwatch_logs_exports = [
"audit",
"profiler",
]
}
`, rName))
}

func testAccClusterConfig_pointInTimeRestoreSource(rName string) string {
return acctest.ConfigCompose(testAccClusterConfig_baseForPITR(rName), fmt.Sprintf(`
resource "aws_docdb_cluster" "restore" {
cluster_identifier = "%[1]s-restore"
restore_to_point_in_time {
source_cluster_identifier = aws_docdb_cluster.test.cluster_identifier
restore_type = "full-copy"
use_latest_restorable_time = true
}
skip_final_snapshot = true
}
`, rName))
}

func testAccClusterConfig_deleteProtection(rName string, isProtected bool) string {
return fmt.Sprintf(`
resource "aws_docdb_cluster" "test" {
Expand Down
12 changes: 12 additions & 0 deletions internal/service/docdb/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ func storageType_Values() []string {
storageTypeStandard,
}
}

const (
RestoreTypeCopyOnWrite = "copy-on-write"
RestoreTypeFullCopy = "full-copy"
)

func RestoreType_Values() []string {
return []string{
RestoreTypeCopyOnWrite,
RestoreTypeFullCopy,
}
}
10 changes: 10 additions & 0 deletions website/docs/r/docdb_cluster.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ This argument supports the following arguments:
* `preferred_backup_window` - (Optional) The daily time range during which automated backups are created if automated backups are enabled using the BackupRetentionPeriod parameter.Time in UTC
Default: A 30-minute window selected at random from an 8-hour block of time per regionE.g., 04:00-09:00
* `preferred_maintenance_window` - (Optional) The weekly time range during which system maintenance can occur, in (UTC) e.g., wed:04:00-wed:04:30
* `restore_to_point_in_time` - (Optional, Forces new resource) A configuration block for restoring a DB instance to an arbitrary point in time. Requires the `identifier` argument to be set with the name of the new DB instance to be created. See [Restore To Point In Time](#restore-to-point-in-time) below for details.
* `skip_final_snapshot` - (Optional) Determines whether a final DB snapshot is created before the DB cluster is deleted. If true is specified, no DB snapshot is created. If false is specified, a DB snapshot is created before the DB cluster is deleted, using the value from `final_snapshot_identifier`. Default is `false`.
* `snapshot_identifier` - (Optional) Specifies whether or not to create this cluster from a snapshot. You can use either the name or ARN when specifying a DB cluster snapshot, or the ARN when specifying a DB snapshot. Automated snapshots **should not** be used for this attribute, unless from a different cluster. Automated snapshots are deleted as part of cluster destruction when the resource is replaced.
* `storage_encrypted` - (Optional) Specifies whether the DB cluster is encrypted. The default is `false`.
Expand All @@ -78,6 +79,15 @@ Default: A 30-minute window selected at random from an 8-hour block of time per
* `vpc_security_group_ids` - (Optional) List of VPC security groups to associate
with the Cluster

### Restore To Point In Time

The `restore_to_point_in_time` block supports the following arguments:

* `restore_to_time` - (Optional) The date and time to restore from. Value must be a time in Universal Coordinated Time (UTC) format and must be before the latest restorable time for the DB instance. Cannot be specified with `use_latest_restorable_time`.
* `restore_type` - (Optional) The type of restore to be performed. Valid values are `full-copy`, `copy-on-write`.
* `source_cluster_identifier` - (Required) The identifier of the source DB cluster from which to restore. Must match the identifier of an existing DB cluster.
* `use_latest_restorable_time` - (Optional) A boolean value that indicates whether the DB cluster is restored from the latest backup time. Defaults to `false`. Cannot be specified with `restore_to_time`.

## Attribute Reference

This resource exports the following attributes in addition to the arguments above:
Expand Down

0 comments on commit 67a977e

Please sign in to comment.