Skip to content

Commit 763317e

Browse files
committed
feat: essentials database version support
1 parent 847777f commit 763317e

File tree

6 files changed

+240
-0
lines changed

6 files changed

+240
-0
lines changed

docs/data-sources/rediscloud_essentials_database.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ data "rediscloud_essentials_database" "example" {
3939
## Attribute Reference
4040

4141
* `protocol` - The protocol of the database. Either `redis`, `memcached` or `stack`.
42+
* `redis_version` - The Redis database version.
4243
* `cloud_provider` - The Cloud Provider hosting this database.
4344
* `region` - The region within the Cloud Provider where this database is hosted.
4445
* `redis_version_compliance` - The compliance level (redis version) of this database.

docs/resources/rediscloud_essentials_database.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ The following arguments are supported:
5858
* `subscription_id` - (Required) The ID of the subscription to create the database in. **Modifying this attribute will force creation of a new resource.**
5959
* `name` - (Required) A meaningful name to identify the database.
6060
* `protocol` - (Optional) Database protocol. 'stack' is a suite of all Redis' data modules. Default: 'stack'. Either: 'redis', 'memcached' or 'stack'. **'redis' is only used with Pay-As-You-Go databases.**
61+
* `redis_version` - (Optional) Defines the Redis database version. If omitted, the Redis version will be set to the default version.
6162
* `resp_version` - (Optional) RESP version must be compatible with the Redis version.
6263
* `data_persistence` - (Required) Rate of database data persistence (in persistent storage). Either: 'none', 'aof-every-1-second', 'aof-every-write', 'snapshot-every-1-hour', 'snapshot-every-6-hours' or 'snapshot-every-12-hours'.
6364
* `data_eviction` - (Optional) Data items eviction method. Either: 'allkeys-lru', 'allkeys-lfu', 'allkeys-random', 'volatile-lru', 'volatile-lfu', 'volatile-random', 'volatile-ttl' or 'noeviction'. Default: 'volatile-lru'.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
terraform {
2+
required_providers {
3+
rediscloud = {
4+
source = "RedisLabs/rediscloud"
5+
}
6+
}
7+
}
8+
9+
variable "subscription_id" {
10+
type = number
11+
}
12+
13+
variable "database_name" {
14+
type = string
15+
}
16+
17+
variable "redis_version" {
18+
type = string
19+
}
20+
21+
variable "password" {
22+
type = string
23+
sensitive = true
24+
}
25+
26+
resource "rediscloud_essentials_database" "example" {
27+
subscription_id = var.subscription_id
28+
name = var.database_name
29+
protocol = "redis"
30+
redis_version = var.redis_version
31+
replication = false
32+
data_persistence = "none"
33+
34+
password = var.password
35+
36+
alert {
37+
name = "throughput-higher-than"
38+
value = 80
39+
}
40+
}
41+
42+
output "database_id" {
43+
value = rediscloud_essentials_database.example.db_id
44+
}
45+
46+
output "redis_version" {
47+
value = rediscloud_essentials_database.example.redis_version
48+
}
49+
50+
output "public_endpoint" {
51+
value = rediscloud_essentials_database.example.public_endpoint
52+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
terraform {
2+
required_providers {
3+
rediscloud = {
4+
source = "RedisLabs/rediscloud"
5+
}
6+
}
7+
}
8+
9+
variable "subscription_id" {
10+
type = number
11+
}
12+
13+
variable "database_name" {
14+
type = string
15+
}
16+
17+
variable "redis_version" {
18+
type = string
19+
}
20+
21+
variable "password" {
22+
type = string
23+
sensitive = true
24+
}
25+
26+
resource "rediscloud_essentials_database" "example" {
27+
subscription_id = var.subscription_id
28+
name = var.database_name
29+
protocol = "redis"
30+
redis_version = var.redis_version # This will be updated during test
31+
replication = false
32+
data_persistence = "aof-every-write"
33+
34+
password = var.password
35+
36+
alert {
37+
name = "throughput-higher-than"
38+
value = 85
39+
}
40+
}
41+
42+
output "database_id" {
43+
value = rediscloud_essentials_database.example.db_id
44+
}
45+
46+
output "redis_version" {
47+
value = rediscloud_essentials_database.example.redis_version
48+
}
49+
50+
output "public_endpoint" {
51+
value = rediscloud_essentials_database.example.public_endpoint
52+
}

provider/resource_rediscloud_essentials_database.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ func resourceRedisCloudEssentialsDatabase() *schema.Resource {
7676
Computed: true,
7777
ForceNew: true,
7878
},
79+
"redis_version": {
80+
Description: "Defines the Redis database version. If omitted, the Redis version will be set to the default version",
81+
Type: schema.TypeString,
82+
Optional: true,
83+
Computed: true,
84+
},
7985
"cloud_provider": {
8086
Description: "The Cloud Provider hosting this database",
8187
Type: schema.TypeString,
@@ -319,6 +325,10 @@ func resourceRedisCloudEssentialsDatabaseCreate(ctx context.Context, d *schema.R
319325
createDatabaseRequest.Protocol = redis.String(protocol)
320326
}
321327

328+
utils.SetStringIfNotEmpty(d, "redis_version", func(s *string) {
329+
createDatabaseRequest.RedisVersion = s
330+
})
331+
322332
respVersion := d.Get("resp_version").(string)
323333
if respVersion != "" {
324334
createDatabaseRequest.RespVersion = redis.String(respVersion)
@@ -466,6 +476,9 @@ func resourceRedisCloudEssentialsDatabaseRead(ctx context.Context, d *schema.Res
466476
if err := d.Set("protocol", redis.StringValue(db.Protocol)); err != nil {
467477
return diag.FromErr(err)
468478
}
479+
if err := d.Set("redis_version", redis.StringValue(db.RedisVersion)); err != nil {
480+
return diag.FromErr(err)
481+
}
469482
if err := d.Set("cloud_provider", redis.StringValue(db.Provider)); err != nil {
470483
return diag.FromErr(err)
471484
}
@@ -599,6 +612,23 @@ func resourceRedisCloudEssentialsDatabaseUpdate(ctx context.Context, d *schema.R
599612
utils.SubscriptionMutex.Lock(subId)
600613
defer utils.SubscriptionMutex.Unlock(subId)
601614

615+
// Handle redis_version upgrades before other updates
616+
if d.HasChange("redis_version") {
617+
originalVersion, newVersion := d.GetChange("redis_version")
618+
619+
// Only perform upgrade if both versions are non-empty (prevents unnecessary upgrades on creation)
620+
if originalVersion.(string) != "" && newVersion.(string) != "" {
621+
if upgradeDiags, unlocked := upgradeRedisVersionEssentials(ctx, api, subId, databaseId, newVersion.(string)); upgradeDiags != nil {
622+
if !unlocked {
623+
utils.SubscriptionMutex.Unlock(subId)
624+
}
625+
return append(diag.Diagnostics{}, upgradeDiags...)
626+
}
627+
// Lock again since the upgrade function unlocked it
628+
utils.SubscriptionMutex.Lock(subId)
629+
}
630+
}
631+
602632
updateDatabaseRequest := fixedDatabases.UpdateFixedDatabase{
603633
Name: redis.String(d.Get("name").(string)),
604634
DataPersistence: redis.String(d.Get("data_persistence").(string)),
@@ -734,6 +764,35 @@ func resourceRedisCloudEssentialsDatabaseDelete(ctx context.Context, d *schema.R
734764
return diags
735765
}
736766

767+
func upgradeRedisVersionEssentials(ctx context.Context, api *client.ApiClient, subId int, dbId int, newVersion string) (diag.Diagnostics, bool) {
768+
log.Printf("[INFO] Requesting Redis version change to %s...", newVersion)
769+
770+
upgrade := fixedDatabases.UpgradeRedisVersion{
771+
TargetRedisVersion: redis.String(newVersion),
772+
}
773+
774+
if err := api.Client.FixedDatabases.UpgradeRedisVersion(ctx, subId, dbId, upgrade); err != nil {
775+
utils.SubscriptionMutex.Unlock(subId)
776+
return diag.Errorf("failed to change Redis version to %s: %v", newVersion, err), true
777+
}
778+
779+
log.Printf("[INFO] Redis version change request to %s accepted by API", newVersion)
780+
781+
// Wait for database to be active
782+
if err := waitForEssentialsDatabaseToBeActive(ctx, subId, dbId, api); err != nil {
783+
utils.SubscriptionMutex.Unlock(subId)
784+
return diag.FromErr(err), true
785+
}
786+
787+
// Wait for subscription to be active
788+
if err := waitForEssentialsSubscriptionToBeActive(ctx, subId, api); err != nil {
789+
utils.SubscriptionMutex.Unlock(subId)
790+
return diag.FromErr(err), true
791+
}
792+
793+
return nil, false
794+
}
795+
737796
func waitForEssentialsDatabaseToBeActive(ctx context.Context, subId, id int, api *client.ApiClient) error {
738797
wait := &retry.StateChangeConf{
739798
Delay: 30 * time.Second,
@@ -771,6 +830,7 @@ func waitForEssentialsDatabaseToBeActive(ctx context.Context, subId, id int, api
771830
return nil
772831
}
773832

833+
774834
func writeReplica(replica fixedDatabases.ReplicaOf) []map[string]interface{} {
775835
tf := map[string]interface{}{}
776836
syncSources := make([]map[string]interface{}, 0)

provider/resource_rediscloud_essentials_database_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,40 @@ func TestAccResourceRedisCloudEssentialsDatabase_DisableDefaultUser(t *testing.T
334334
})
335335
}
336336

337+
// Test redis_version field support - ensures version can be specified and read
338+
func TestAccResourceRedisCloudEssentialsDatabase_RedisVersion(t *testing.T) {
339+
utils.AccRequiresEnvVar(t, "EXECUTE_TESTS")
340+
341+
subscriptionName := acctest.RandomWithPrefix(testResourcePrefix)
342+
databaseName := subscriptionName + "-db"
343+
344+
const resourceName = "rediscloud_essentials_database.example"
345+
const datasourceName = "data.rediscloud_essentials_database.example"
346+
347+
resource.Test(t, resource.TestCase{
348+
PreCheck: func() { testAccPreCheck(t) },
349+
ProviderFactories: providerFactories,
350+
CheckDestroy: testAccCheckEssentialsSubscriptionDestroy,
351+
Steps: []resource.TestStep{
352+
{
353+
Config: fmt.Sprintf(testAccResourceRedisCloudEssentialsDatabaseRedisVersion, subscriptionName, databaseName),
354+
Check: resource.ComposeAggregateTestCheckFunc(
355+
// Test the resource has redis_version set
356+
resource.TestMatchResourceAttr(resourceName, "id", regexp.MustCompile("^\\d+/\\d+$")),
357+
resource.TestCheckResourceAttrSet(resourceName, "subscription_id"),
358+
resource.TestCheckResourceAttrSet(resourceName, "db_id"),
359+
resource.TestCheckResourceAttr(resourceName, "name", databaseName),
360+
resource.TestCheckResourceAttrSet(resourceName, "redis_version"),
361+
362+
// Test the datasource also returns redis_version
363+
resource.TestMatchResourceAttr(datasourceName, "id", regexp.MustCompile("^\\d+/\\d+$")),
364+
resource.TestCheckResourceAttrSet(datasourceName, "redis_version"),
365+
),
366+
},
367+
},
368+
})
369+
}
370+
337371
const testAccResourceRedisCloudEssentialsDatabaseDisableDefaultUserCreate = `
338372
339373
data "rediscloud_payment_method" "card" {
@@ -417,3 +451,43 @@ resource "rediscloud_essentials_database" "example" {
417451
}
418452
}
419453
`
454+
455+
const testAccResourceRedisCloudEssentialsDatabaseRedisVersion = `
456+
457+
data "rediscloud_payment_method" "card" {
458+
card_type = "Visa"
459+
last_four_numbers = "5556"
460+
}
461+
462+
data "rediscloud_essentials_plan" "example" {
463+
name = "Single-Zone_1GB"
464+
cloud_provider = "AWS"
465+
region = "us-east-1"
466+
}
467+
468+
data "rediscloud_essentials_database" "example" {
469+
subscription_id = rediscloud_essentials_subscription.example.id
470+
name = rediscloud_essentials_database.example.name
471+
}
472+
473+
resource "rediscloud_essentials_subscription" "example" {
474+
name = "%s"
475+
plan_id = data.rediscloud_essentials_plan.example.id
476+
payment_method_id = data.rediscloud_payment_method.card.id
477+
}
478+
479+
resource "rediscloud_essentials_database" "example" {
480+
subscription_id = rediscloud_essentials_subscription.example.id
481+
name = "%s"
482+
enable_default_user = true
483+
password = "j43589rhe39f"
484+
485+
data_persistence = "none"
486+
replication = false
487+
488+
alert {
489+
name = "throughput-higher-than"
490+
value = 80
491+
}
492+
}
493+
`

0 commit comments

Comments
 (0)