Skip to content

Commit a0e8ff6

Browse files
authored
feat: added support for creating a new PostgreSQL instance pointing to a PITR (#114)
1 parent 2be96e0 commit a0e8ff6

File tree

11 files changed

+248
-4
lines changed

11 files changed

+248
-4
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ You need the following permissions to run this module.
5454
- [ Complete example with byok encryption, CBR rules and storing credentials in secrets manager](examples/complete)
5555
- [ Default example](examples/default)
5656
- [ Financial Services Cloud profile example](examples/fscloud)
57+
- [ Point in time recovery example (PITR)](examples/pitr)
5758
- [ Replica example](examples/replica)
5859
<!-- END EXAMPLES HOOK -->
5960
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
@@ -94,6 +95,8 @@ You need the following permissions to run this module.
9495
| <a name="input_members"></a> [members](#input\_members) | Number of members | `number` | `3` | no |
9596
| <a name="input_name"></a> [name](#input\_name) | Name of the Postgresql instance | `string` | n/a | yes |
9697
| <a name="input_pg_version"></a> [pg\_version](#input\_pg\_version) | Version of the postgresql instance | `string` | `null` | no |
98+
| <a name="input_pitr_id"></a> [pitr\_id](#input\_pitr\_id) | (Optional) The ID of the postgresql instance that you want to recover back to. Here ID of the postgresql instance is expected to be up and in running state. | `string` | `null` | no |
99+
| <a name="input_pitr_time"></a> [pitr\_time](#input\_pitr\_time) | (Optional) The timestamp in UTC format (%Y-%m-%dT%H:%M:%SZ) that you want to restore to. To retrieve the timestamp, run the command (ibmcloud cdb postgresql earliest-pitr-timestamp <deployment name or CRN>) | `string` | `null` | no |
97100
| <a name="input_plan_validation"></a> [plan\_validation](#input\_plan\_validation) | Enable or disable validating the database parameters for postgres during the plan phase | `bool` | `true` | no |
98101
| <a name="input_region"></a> [region](#input\_region) | The region postgresql is to be created on. The region must support BYOK if key\_protect\_key\_crn is used | `string` | `"us-south"` | no |
99102
| <a name="input_remote_leader_crn"></a> [remote\_leader\_crn](#input\_remote\_leader\_crn) | The CRN of the leader database to make the replica(read-only) deployment. | `string` | `null` | no |

examples/pitr/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Point in time recovery example (PITR)
2+
3+
This example provides an end-to-end creation of a new PostgreSQL instance, then creating a new PostgreSQL instance pointing to a PITR time. This example uses the IBM Cloud terraform provider to:
4+
5+
- Create a new resource group if one is not passed in.
6+
- Create a new ICD Postgresql database instance.
7+
- Create a new PostgreSQL instance pointing to a PITR time.

examples/pitr/main.tf

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
##############################################################################
2+
# Resource Group
3+
##############################################################################
4+
5+
module "resource_group" {
6+
source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-resource-group.git?ref=v1.0.5"
7+
# if an existing resource group is not set (null) create a new one using prefix
8+
resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null
9+
existing_resource_group_name = var.resource_group
10+
}
11+
12+
data "ibm_database_point_in_time_recovery" "database_pitr" {
13+
deployment_id = var.pitr_id
14+
}
15+
16+
# New ICD postgresql database instance pointing to a PITR time
17+
module "postgresql_db_pitr" {
18+
source = "../.."
19+
resource_group_id = module.resource_group.resource_group_id
20+
name = "${var.prefix}-postgres-pitr"
21+
region = var.region
22+
resource_tags = var.resource_tags
23+
configuration = var.configuration
24+
member_memory_mb = var.member_memory_mb
25+
member_disk_mb = var.member_disk_mb
26+
member_cpu_count = var.member_cpu_count
27+
pitr_id = var.pitr_id
28+
pitr_time = data.ibm_database_point_in_time_recovery.database_pitr.earliest_point_in_time_recovery_time
29+
}

examples/pitr/outputs.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
##############################################################################
2+
# Outputs
3+
##############################################################################
4+
5+
output "pitr_postgresql_db_id" {
6+
description = "PITR Postgresql instance id"
7+
value = module.postgresql_db_pitr.id
8+
}
9+
10+
output "pitr_postgresql_db_version" {
11+
description = "PITR Postgresql instance version"
12+
value = module.postgresql_db_pitr.version
13+
}
14+
15+
output "pitr_time" {
16+
description = "PITR timestamp in UTC format (%Y-%m-%dT%H:%M:%SZ) used to create PITR instance"
17+
value = data.ibm_database_point_in_time_recovery.database_pitr.earliest_point_in_time_recovery_time
18+
}

examples/pitr/provider.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
provider "ibm" {
2+
ibmcloud_api_key = var.ibmcloud_api_key
3+
region = var.region
4+
}

examples/pitr/variables.tf

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
variable "ibmcloud_api_key" {
2+
type = string
3+
description = "The IBM Cloud API Key"
4+
sensitive = true
5+
}
6+
7+
variable "region" {
8+
type = string
9+
description = "Region to provision all resources created by this example."
10+
default = "us-south"
11+
}
12+
13+
variable "prefix" {
14+
type = string
15+
description = "Prefix to append to all resources created by this example"
16+
default = "pg"
17+
}
18+
19+
variable "resource_group" {
20+
type = string
21+
description = "An existing resource group name to use for this example, if unset a new resource group will be created"
22+
default = null
23+
}
24+
25+
variable "resource_tags" {
26+
type = list(string)
27+
description = "Optional list of tags to be added to created resources"
28+
default = []
29+
}
30+
31+
variable "configuration" {
32+
description = "(Optional, Json String) Database Configuration in JSON format."
33+
type = object({
34+
max_connections = optional(number)
35+
max_prepared_transactions = optional(number)
36+
deadlock_timeout = optional(number)
37+
effective_io_concurrency = optional(number)
38+
max_replication_slots = optional(number)
39+
max_wal_senders = optional(number)
40+
shared_buffers = optional(number)
41+
synchronous_commit = optional(string)
42+
wal_level = optional(string)
43+
archive_timeout = optional(number)
44+
log_min_duration_statement = optional(number)
45+
})
46+
default = null
47+
}
48+
49+
variable "pitr_id" {
50+
type = string
51+
description = "The ID of the postgresql instance that you want to recover back to. Here ID of the postgresql instance is expected to be up and in running state."
52+
}
53+
54+
variable "member_memory_mb" {
55+
type = string
56+
description = "Memory allocation required for postgresql read-only replica database"
57+
default = "7680"
58+
validation {
59+
condition = alltrue([
60+
var.member_memory_mb >= 3072,
61+
var.member_memory_mb <= 114688
62+
])
63+
error_message = "member group memory must be >= 3072 and <= 114688 in increments of 384"
64+
}
65+
}
66+
67+
variable "member_disk_mb" {
68+
type = string
69+
description = "Disk allocation required for postgresql read-only replica database"
70+
default = "15360"
71+
validation {
72+
condition = alltrue([
73+
var.member_disk_mb >= 15360,
74+
var.member_disk_mb <= 4194304
75+
])
76+
error_message = "member group disk must be >= 15360 and <= 4194304 in increments of 1536"
77+
}
78+
}
79+
80+
variable "member_cpu_count" {
81+
type = string
82+
description = "CPU allocation required for the postgresql read-only replica database"
83+
default = "9"
84+
validation {
85+
condition = alltrue([
86+
var.member_cpu_count >= 9,
87+
var.member_cpu_count <= 28
88+
])
89+
error_message = "member group cpu must be >= 9 and <= 28 in increments of 1"
90+
}
91+
}

examples/pitr/version.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
required_version = ">= 1.3.0"
3+
required_providers {
4+
# Pin to the lowest provider version of the range defined in the main module's version.tf to ensure lowest version still works
5+
ibm = {
6+
source = "IBM-Cloud/ibm"
7+
version = "1.49.0"
8+
}
9+
}
10+
}

main.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ resource "ibm_database" "postgresql_db" {
2727
key_protect_key = var.key_protect_key_crn
2828
backup_encryption_key_crn = local.kp_backup_crn
2929

30+
point_in_time_recovery_deployment_id = var.pitr_id
31+
point_in_time_recovery_time = var.pitr_time
32+
3033
dynamic "allowlist" {
3134
for_each = (var.allowlist != null ? var.allowlist : [])
3235
content {

module-metadata.json

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@
150150
"immutable": true,
151151
"computed": true
152152
},
153+
"pitr_id": {
154+
"name": "pitr_id",
155+
"type": "string",
156+
"description": "(Optional) The ID of the postgresql instance that you want to recover back to. Here ID of the postgresql instance is expected to be up and in running state.",
157+
"source": [
158+
"ibm_database.postgresql_db.point_in_time_recovery_deployment_id"
159+
],
160+
"pos": {
161+
"filename": "variables.tf",
162+
"line": 240
163+
}
164+
},
165+
"pitr_time": {
166+
"name": "pitr_time",
167+
"type": "string",
168+
"description": "(Optional) The timestamp in UTC format (%Y-%m-%dT%H:%M:%SZ) that you want to restore to. To retrieve the timestamp, run the command (ibmcloud cdb postgresql earliest-pitr-timestamp \u003cdeployment name or CRN\u003e)",
169+
"source": [
170+
"ibm_database.postgresql_db.point_in_time_recovery_time"
171+
],
172+
"pos": {
173+
"filename": "variables.tf",
174+
"line": 246
175+
}
176+
},
153177
"plan_validation": {
154178
"name": "plan_validation",
155179
"type": "bool",
@@ -351,6 +375,8 @@
351375
"location": "region",
352376
"name": "name",
353377
"plan_validation": "plan_validation",
378+
"point_in_time_recovery_deployment_id": "pitr_id",
379+
"point_in_time_recovery_time": "pitr_time",
354380
"remote_leader_id": "remote_leader_crn",
355381
"resource_group_id": "resource_group_id",
356382
"service_endpoints": "service_endpoints",
@@ -378,7 +404,7 @@
378404
},
379405
"pos": {
380406
"filename": "main.tf",
381-
"line": 145
407+
"line": 148
382408
}
383409
}
384410
},
@@ -457,7 +483,7 @@
457483
},
458484
"pos": {
459485
"filename": "main.tf",
460-
"line": 107
486+
"line": 110
461487
}
462488
}
463489
}

tests/pr_test.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
package test
33

44
import (
5-
"testing"
6-
75
"github.com/stretchr/testify/assert"
6+
"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common"
87
"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper"
8+
"log"
9+
"os"
10+
"testing"
911
)
1012

1113
// Use existing resource group
@@ -15,10 +17,13 @@ const autoscaleExampleTerraformDir = "examples/autoscale"
1517
const fsCloudTerraformDir = "examples/fscloud"
1618
const completeExampleTerraformDir = "examples/complete"
1719
const replicaExampleTerraformDir = "examples/replica"
20+
const pitrTerraformDir = "examples/pitr"
1821

1922
// Restricting due to limited availability of BYOK in certain regions
2023
const regionSelectionPath = "../common-dev-assets/common-go-assets/icd-region-prefs.yaml"
2124

25+
const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml"
26+
2227
func TestRunDefaultExample(t *testing.T) {
2328
t.Parallel()
2429

@@ -119,3 +124,35 @@ func TestRunUpgradeExample(t *testing.T) {
119124
assert.NotNil(t, output, "Expected some output")
120125
}
121126
}
127+
128+
var permanentResources map[string]interface{}
129+
130+
// TestMain will be run before any parallel tests, used to read data from yaml for use with tests
131+
func TestMain(m *testing.M) {
132+
133+
var err error
134+
permanentResources, err = common.LoadMapFromYaml(yamlLocation)
135+
if err != nil {
136+
log.Fatal(err)
137+
}
138+
139+
os.Exit(m.Run())
140+
}
141+
142+
func TestRunPointInTimeRecoveryDBExample(t *testing.T) {
143+
t.Parallel()
144+
145+
options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{
146+
Testing: t,
147+
TerraformDir: pitrTerraformDir,
148+
Prefix: "pg-pitr",
149+
ResourceGroup: resourceGroup,
150+
TerraformVars: map[string]interface{}{
151+
"pitr_id": permanentResources["postgresqlCrn"],
152+
},
153+
})
154+
155+
output, err := options.RunTestConsistency()
156+
assert.Nil(t, err, "This should not have errored")
157+
assert.NotNil(t, output, "Expected some output")
158+
}

0 commit comments

Comments
 (0)