Skip to content

Commit

Permalink
Added backup and import attributes to resources (#486)
Browse files Browse the repository at this point in the history
* Added backup and import attributes to resources

* Readded note syntax, it works in a non-IntelliJ markdown renderer

* More documentation
  • Loading branch information
JohnSharpe authored Apr 19, 2024
1 parent d055000 commit 73d8dc3
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 56 deletions.
12 changes: 6 additions & 6 deletions docs/resources/rediscloud_active_active_subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ description: |-
Creates an Active-Active Subscription within your Redis Enterprise Cloud Account.
This resource is responsible for creating and managing subscriptions.

> **Note:** The payment_method property is ignored after Subscription creation.
~> **Note:** The payment_method property is ignored after Subscription creation.

> **Note:** The creation_plan block allows the API server to create a well-optimised infrastructure for your databases in the cluster.
~> **Note:** The creation_plan block allows the API server to create a well-optimised infrastructure for your databases in the cluster.
The attributes inside the block are used by the provider to create initial
databases. Those databases will be deleted after provisioning a new
subscription, then the databases defined as separate resources will be attached to
Expand Down Expand Up @@ -75,7 +75,7 @@ The creation_plan `region` block supports:
* `write_operations_per_second` - (Required) Throughput measurement for an active-active subscription
* `read_operations_per_second` - (Required) Throughput measurement for an active-active subscription

> **Note:** If changes are made to attributes in the subscription which require the subscription to be recreated (such as `cloud_provider`), the creation_plan will need to be defined in order to change these attributes. This is because the creation_plan is always required when a subscription is created.
~> **Note:** If changes are made to attributes in the subscription which require the subscription to be recreated (such as `cloud_provider`), the creation_plan will need to be defined in order to change these attributes. This is because the creation_plan is always required when a subscription is created.

### Timeouts

Expand All @@ -93,8 +93,8 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/d
$ terraform import rediscloud_active_active_subscription.subscription-resource 12345678
```

> **Note:** the payment_method property and creation_plan block will be ignored during imports.
~> **Note:** the payment_method property and creation_plan block will be ignored during imports.

> **Note:** when importing an existing Subscription, upon providing a `redis_version`, Terraform will always try to
recreatethe resource. The API doesn't return this value, so we can't detect changes between states.
~> **Note:** when importing an existing Subscription, upon providing a `redis_version`, Terraform will always try to
recreate the resource. The API doesn't return this value, so we can't detect changes between states.

Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l
* `db_id` - Identifier of the database created
* `public_endpoint` - A map of which public endpoints can to access the database per region, uses region name as key.
* `private_endpoint` - A map of which private endpoints can to access the database per region, uses region name as key.
* `latest_import_status` - An object containing the JSON-formatted response detailing the latest import status (or an error if the lookup failed).

## Import
`rediscloud_active_active_subscription_database` can be imported using the ID of the Active-Active subscription and the ID of the database in the format {subscription ID}/{database ID}, e.g.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ The `database` block supports:
* `local_write_operations_per_second` - (Required) Local write operations per second for this active-active region
* `local_read_operations_per_second` - (Required) Local read operations per second for this active-active region

## Attribute Reference

* `latest_backup_status` - An object containing the JSON-formatted response detailing the latest backup status (or an error if the lookup failed).

### Timeouts

Expand Down
16 changes: 8 additions & 8 deletions docs/resources/rediscloud_subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ description: |-
Creates a Flexible Subscription within your Redis Enterprise Cloud Account.
This resource is responsible for creating and managing subscriptions.

> **Note:** The payment_method property is ignored after Subscription creation.
~> **Note:** The payment_method property is ignored after Subscription creation.

> **Note:** The creation_plan block allows the API server to create a well-optimised infrastructure for your databases in the cluster.
~> **Note:** The creation_plan block allows the API server to create a well-optimised infrastructure for your databases in the cluster.
The attributes inside the block are used by the provider to create initial
databases. Those databases will be deleted after provisioning a new
subscription, then the databases defined as separate resources will be attached to
Expand Down Expand Up @@ -75,7 +75,7 @@ The `allowlist` block supports:
* `security_group_ids` - (Required) Set of security groups that are allowed to access the databases associated with this subscription
* `cidrs` - (Optional) Set of CIDR ranges that are allowed to access the databases associated with this subscription

> **Note:** `allowlist` is only available when you run on your own cloud account, and not one that Redis provided (i.e `cloud_account_id` != 1)
~> **Note:** `allowlist` is only available when you run on your own cloud account, and not one that Redis provided (i.e `cloud_account_id` != 1)

The `cloud_provider` block supports:

Expand All @@ -99,9 +99,9 @@ Example: `modules = ["RedisJSON", "RediSearch", "RedisTimeSeries", "RedisBloom"]
Estimated average size (measured in bytes) of the items stored in the database. The value needs to
be the maximum average item size defined in one of your databases. Default: 1000

> **Note:** If the number of modules exceeds the `quantity` then additional creation-plan databases will be created with the modules defined in the `modules` block.
~> **Note:** If the number of modules exceeds the `quantity` then additional creation-plan databases will be created with the modules defined in the `modules` block.

> **Note:** If changes are made to attributes in the subscription which require the subscription to be recreated (such as `memory_storage` or `cloud_provider`), the creation_plan will need to be defined in order to change these attributes. This is because the creation_plan is always required when a subscription is created.
~> **Note:** If changes are made to attributes in the subscription which require the subscription to be recreated (such as `memory_storage` or `cloud_provider`), the creation_plan will need to be defined in order to change these attributes. This is because the creation_plan is always required when a subscription is created.

The cloud_provider `region` block supports:

Expand All @@ -113,7 +113,7 @@ The cloud_provider `region` block supports:
within the hosting account. **Modifying this attribute will force creation of a new resource.**
* `preferred_availability_zones` - (Optional) Availability zones deployment preferences (for the selected provider & region). If multiple_availability_zones is set to 'true', select three availability zones from the list. If you don't want to specify preferred availability zones, set this attribute to an empty list ('[]'). **Modifying this attribute will force creation of a new resource.**

> **Note:** The preferred_availability_zones parameter is required for Terraform, but is optional within the Redis Enterprise Cloud UI.
~> **Note:** The preferred_availability_zones parameter is required for Terraform, but is optional within the Redis Enterprise Cloud UI.
This difference in behaviour is to guarantee that a plan after an apply does not generate differences. In AWS Redis internal cloud account, please set the zone IDs (for example: `["use-az2", "use-az3", "use-az5"]`).

### Timeouts
Expand Down Expand Up @@ -143,7 +143,7 @@ The `networks` block has these attributes:
```
$ terraform import rediscloud_subscription.subscription-resource 12345678
```
> **Note:** the payment_method property and creation_plan block will be ignored during imports.
~> **Note:** the payment_method property and creation_plan block will be ignored during imports.

> **Note:** when importing an existing Subscription, upon providing a `redis_version`, Terraform will always try to
~> **Note:** when importing an existing Subscription, upon providing a `redis_version`, Terraform will always try to
recreate the resource. The API doesn't return this value, so we can't detect changes between states.
2 changes: 2 additions & 0 deletions docs/resources/rediscloud_subscription_database.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l
* `db_id` - Identifier of the database created
* `public_endpoint` - Public endpoint to access the database
* `private_endpoint` - Private endpoint to access the database
* `latest_backup_status` - An object containing the JSON-formatted response detailing the latest backup status (or an error if the lookup failed).
* `latest_import_status` - An object containing the JSON-formatted response detailing the latest import status (or an error if the lookup failed).

## Import
`rediscloud_subscription_database` can be imported using the ID of the subscription and the ID of the database in the format {subscription ID}/{database ID}, e.g.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,43 @@ func resourceRedisCloudActiveActiveSubscriptionDatabase() *schema.Resource {
ValidateDiagFunc: validation.ToDiagFunc(
validation.StringMatch(regexp.MustCompile("^(resp2|resp3)$"), "must be 'resp2' or 'resp3'")),
},
"latest_import_status": {
Description: "Details about the last import that took place for this active-active database",
Computed: true,
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"response": {
Description: "JSON-style details about the last import",
Computed: true,
Type: schema.TypeString,
},
"error": {
Computed: true,
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Description: "The type of error encountered while looking up the status of the last import",
Computed: true,
Type: schema.TypeString,
},
"description": {
Description: "A description of the error encountered while looking up the status of the last import",
Computed: true,
Type: schema.TypeString,
},
"status": {
Description: "Any particular HTTP status code associated with the erroneous status check",
Computed: true,
Type: schema.TypeString,
},
},
},
},
},
},
},
},
}
}
Expand Down Expand Up @@ -545,6 +582,20 @@ func resourceRedisCloudActiveActiveSubscriptionDatabaseRead(ctx context.Context,
return diag.FromErr(err)
}

var parsedLatestImportStatus []map[string]interface{}
latestImportStatus, err := api.client.LatestImport.Get(ctx, subId, dbId)
if err != nil {
// Forgive errors here, sometimes we just can't get a latest status
} else {
parsedLatestImportStatus, err = parseLatestImportStatus(latestImportStatus)
if err != nil {
return diag.FromErr(err)
}
}
if err := d.Set("latest_import_status", parsedLatestImportStatus); err != nil {
return diag.FromErr(err)
}

return diags
}

Expand Down
133 changes: 91 additions & 42 deletions provider/resource_rediscloud_active_active_subscription_regions.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,43 @@ func resourceRedisCloudActiveActiveSubscriptionRegions() *schema.Resource {
Type: schema.TypeInt,
Required: true,
},
"latest_backup_status": {
Description: "Details about the last backup that took place for this database instance",
Computed: true,
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"response": {
Description: "JSON-style details about the last backup",
Computed: true,
Type: schema.TypeString,
},
"error": {
Computed: true,
Type: schema.TypeSet,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Description: "The type of error encountered while looking up the status of the last backup",
Computed: true,
Type: schema.TypeString,
},
"description": {
Description: "A description of the error encountered while looking up the status of the last backup",
Computed: true,
Type: schema.TypeString,
},
"status": {
Description: "Any particular HTTP status code associated with the erroneous status check",
Computed: true,
Type: schema.TypeString,
},
},
},
},
},
},
},
},
},
},
Expand Down Expand Up @@ -257,7 +294,60 @@ func resourceRedisCloudActiveActiveRegionRead(ctx context.Context, d *schema.Res
return diag.FromErr(err)
}

if err := d.Set("region", buildResourceDataFromAPIRegions(existingRegions.Regions, d.Get("region").(*schema.Set))); err != nil {
regionsFromAPI := existingRegions.Regions
regionsFromConfig := d.Get("region").(*schema.Set)

var newRegions []map[string]interface{}

recreateRegions := make(map[string]bool)

// The API doesn't return a respVersion at the region level, so we just read whatever was last written to state.
respVersions := make(map[string]string)

for _, element := range regionsFromConfig.List() {
r := element.(map[string]interface{})
recreateRegions[r["region"].(string)] = r["recreate_region"].(bool)
respVersions[r["region"].(string)] = r["local_resp_version"].(string)
}

for _, region := range regionsFromAPI {
var dbs []interface{}
for _, database := range region.Databases {

var parsedLatestBackupStatus []map[string]interface{}
latestBackupStatus, err := api.client.LatestBackup.GetActiveActive(ctx, subId, *database.DatabaseId, *region.Region)
if err != nil {
// Forgive errors here, sometimes we just can't get a latest status
} else {
parsedLatestBackupStatus, err = parseLatestBackupStatus(latestBackupStatus)
if err != nil {
return diag.FromErr(err)
}
}

databaseMapString := map[string]interface{}{
"database_id": database.DatabaseId,
"database_name": database.DatabaseName,
"local_read_operations_per_second": database.ReadOperationsPerSecond,
"local_write_operations_per_second": database.WriteOperationsPerSecond,
"latest_backup_status": parsedLatestBackupStatus,
}
dbs = append(dbs, databaseMapString)
}

regionMapString := map[string]interface{}{
"region_id": region.RegionId,
"region": region.Region,
"recreate_region": recreateRegions[*region.Region],
"networking_deployment_cidr": region.DeploymentCIDR,
"vpc_id": region.VpcId,
"database": dbs,
"local_resp_version": respVersions[*region.Region],
}
newRegions = append(newRegions, regionMapString)
}

if err := d.Set("region", newRegions); err != nil {
return diag.FromErr(err)
}

Expand Down Expand Up @@ -410,47 +500,6 @@ func regionsDelete(ctx context.Context, subId int, regionsToDelete []*string, ap
return nil
}

func buildResourceDataFromAPIRegions(regionsFromAPI []*regions.Region, regionsFromConfig *schema.Set) []map[string]interface{} {
var result []map[string]interface{}

recreateRegions := make(map[string]bool)

// The API doesn't return a respVersion at the region level, so we just read whatever was last written to state.
respVersions := make(map[string]string)

for _, element := range regionsFromConfig.List() {
r := element.(map[string]interface{})
recreateRegions[r["region"].(string)] = r["recreate_region"].(bool)
respVersions[r["region"].(string)] = r["local_resp_version"].(string)
}

for _, region := range regionsFromAPI {
var dbs []interface{}
for _, database := range region.Databases {
databaseMapString := map[string]interface{}{
"database_id": database.DatabaseId,
"database_name": database.DatabaseName,
"local_read_operations_per_second": database.ReadOperationsPerSecond,
"local_write_operations_per_second": database.WriteOperationsPerSecond,
}
dbs = append(dbs, databaseMapString)
}

regionMapString := map[string]interface{}{
"region_id": region.RegionId,
"region": region.Region,
"recreate_region": recreateRegions[*region.Region],
"networking_deployment_cidr": region.DeploymentCIDR,
"vpc_id": region.VpcId,
"database": dbs,
"local_resp_version": respVersions[*region.Region],
}
result = append(result, regionMapString)
}

return result
}

func buildRegionsFromResourceData(rd *schema.Set) map[string]*RequestedRegion {
result := make(map[string]*RequestedRegion)
for _, r := range rd.List() {
Expand Down
Loading

0 comments on commit 73d8dc3

Please sign in to comment.