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

aws_docdb_cluster #37716

Merged
merged 14 commits into from
Jun 14, 2024
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
Loading