Skip to content

Commit 4d2a4a8

Browse files
committed
feat(rdb): add comprehensive tests and documentation for engine upgrade feature
1 parent a9c1ef2 commit 4d2a4a8

File tree

4 files changed

+314
-38
lines changed

4 files changed

+314
-38
lines changed

docs/resources/rdb_instance.md

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,6 @@ resource "scaleway_rdb_instance" "main" {
2525
}
2626
```
2727

28-
### Example Engine Upgrade
29-
30-
```terraform
31-
resource "scaleway_rdb_instance" "main" {
32-
name = "test-rdb-upgrade"
33-
node_type = "DB-DEV-S"
34-
engine = "PostgreSQL-14" # Start with older version
35-
is_ha_cluster = false
36-
disable_backup = true
37-
user_name = "my_user"
38-
password = "thiZ_is_v&ry_s3cret"
39-
}
40-
41-
# Upgrade to newer version by changing the engine value
42-
# Available versions can be found in the upgradable_versions attribute
43-
resource "scaleway_rdb_instance" "upgraded" {
44-
name = "test-rdb-upgrade"
45-
node_type = "DB-DEV-S"
46-
engine = "PostgreSQL-15" # Upgrade to newer version
47-
is_ha_cluster = false
48-
disable_backup = true
49-
user_name = "my_user"
50-
password = "thiZ_is_v&ry_s3cret"
51-
}
52-
53-
# Check available upgrade versions
54-
output "upgradable_versions" {
55-
value = scaleway_rdb_instance.main.upgradable_versions
56-
}
57-
```
58-
5928
### Example Block Storage Low Latency
6029

6130
```terraform
@@ -108,6 +77,38 @@ resource "scaleway_rdb_instance" "main" {
10877
}
10978
```
11079

80+
### Example Engine Upgrade
81+
82+
```terraform
83+
# Initial creation with PostgreSQL 14
84+
resource "scaleway_rdb_instance" "main" {
85+
name = "my-database"
86+
node_type = "DB-DEV-S"
87+
engine = "PostgreSQL-14"
88+
is_ha_cluster = false
89+
disable_backup = true
90+
user_name = "my_user"
91+
password = "thiZ_is_v&ry_s3cret"
92+
}
93+
94+
# Check available versions for upgrade
95+
output "upgradable_versions" {
96+
value = scaleway_rdb_instance.main.upgradable_versions
97+
}
98+
99+
# To upgrade to PostgreSQL 15, simply change the engine value
100+
# This will trigger a blue/green upgrade with automatic endpoint migration
101+
# resource "scaleway_rdb_instance" "main" {
102+
# name = "my-database"
103+
# node_type = "DB-DEV-S"
104+
# engine = "PostgreSQL-15" # Changed from PostgreSQL-14
105+
# is_ha_cluster = false
106+
# disable_backup = true
107+
# user_name = "my_user"
108+
# password = "thiZ_is_v&ry_s3cret"
109+
# }
110+
```
111+
111112
### Examples of endpoint configuration
112113

113114
Database Instances can have a maximum of 1 public endpoint and 1 private endpoint. They can have both, or none.
@@ -174,7 +175,7 @@ interruption.
174175

175176
- `engine` - (Required) Database Instance's engine version (e.g. `PostgreSQL-11`).
176177

177-
~> **Important** Updates to `engine` will perform a blue/green upgrade using a snapshot and endpoint migration. This ensures minimal downtime but any writes between the snapshot and the switch will be lost. Available upgrade versions can be found in the `upgradable_versions` computed attribute.
178+
~> **Important** Updates to `engine` will perform a blue/green upgrade using `MajorUpgradeWorkflow`. This creates a new instance from a snapshot, migrates endpoints automatically, and updates the Terraform state with the new instance ID. The upgrade ensures minimal downtime but **any writes between the snapshot and the endpoint migration will be lost**. Use the `upgradable_versions` computed attribute to check available versions for upgrade.
178179

179180
- `volume_type` - (Optional, default to `lssd`) Type of volume where data are stored (`lssd`, `sbs_5k` or `sbs_15k`).
180181

@@ -275,12 +276,12 @@ are of the form `{region}/{id}`, e.g. `fr-par/11111111-1111-1111-1111-1111111111
275276
- `id` - The ID of the IPv4 address resource.
276277
- `address` - The private IPv4 address.
277278
- `certificate` - Certificate of the Database Instance.
278-
- `upgradable_versions` - List of available engine versions for upgrade.
279-
- `id` - Version ID for upgrade requests.
280-
- `name` - Engine name.
281-
- `version` - Version string.
282-
- `minor_version` - Minor version string.
283279
- `organization_id` - The organization ID the Database Instance is associated with.
280+
- `upgradable_versions` - List of available engine versions for upgrade. Each version contains:
281+
- `id` - Version ID to use in upgrade requests.
282+
- `name` - Engine version name (e.g., `PostgreSQL-15`).
283+
- `version` - Version string (e.g., `15.5`).
284+
- `minor_version` - Minor version string (e.g., `15.5.0`).
284285

285286
## Limitations
286287

internal/services/rdb/instance.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,14 @@ func ResourceRdbInstanceUpdate(ctx context.Context, d *schema.ResourceData, m an
969969
newEngineStr, oldEngine.(string), availableVersions))
970970
}
971971

972+
// MajorUpgradeWorkflow performs a blue/green deployment:
973+
// 1. Creates a snapshot of the current instance
974+
// 2. Creates a new instance with the target engine version
975+
// 3. Restores data from the snapshot
976+
// 4. Migrates endpoints (WithEndpoints=true) to the new instance
977+
// 5. Returns the new instance with a different ID
978+
// Note: Scaleway manages the lifecycle of the source instance. It remains available
979+
// for potential rollback and is eventually cleaned up by Scaleway's infrastructure.
972980
upgradeInstanceRequests = append(upgradeInstanceRequests,
973981
rdb.UpgradeInstanceRequest{
974982
Region: region,

internal/services/rdb/instance_test.go

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package rdb_test
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67

78
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
89
"github.com/hashicorp/terraform-plugin-testing/terraform"
910
rdbSDK "github.com/scaleway/scaleway-sdk-go/api/rdb/v1"
1011
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
1113
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/rdb"
1214
rdbchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/rdb/testfuncs"
1315
vpcchecks "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/vpc/testfuncs"
@@ -1607,6 +1609,234 @@ func TestAccInstance_EngineUpgrade(t *testing.T) {
16071609
})
16081610
}
16091611

1612+
func TestAccInstance_EngineUpgrade_InvalidVersion(t *testing.T) {
1613+
tt := acctest.NewTestTools(t)
1614+
defer tt.Cleanup()
1615+
1616+
latestVersion := rdbchecks.GetLatestEngineVersion(tt, postgreSQLEngineName)
1617+
1618+
resource.ParallelTest(t, resource.TestCase{
1619+
PreCheck: func() { acctest.PreCheck(t) },
1620+
ProtoV6ProviderFactories: tt.ProviderFactories,
1621+
CheckDestroy: rdbchecks.IsInstanceDestroyed(tt),
1622+
Steps: []resource.TestStep{
1623+
{
1624+
Config: fmt.Sprintf(`
1625+
resource "scaleway_rdb_instance" "main" {
1626+
name = "test-rdb-invalid-upgrade"
1627+
node_type = "db-dev-s"
1628+
engine = %q
1629+
is_ha_cluster = false
1630+
disable_backup = true
1631+
user_name = "test_user"
1632+
password = "thiZ_is_v&ry_s3cret"
1633+
tags = ["terraform-test", "invalid-upgrade"]
1634+
volume_type = "sbs_5k"
1635+
volume_size_in_gb = 10
1636+
}
1637+
`, latestVersion),
1638+
Check: resource.ComposeTestCheckFunc(
1639+
isInstancePresent(tt, "scaleway_rdb_instance.main"),
1640+
resource.TestCheckResourceAttr("scaleway_rdb_instance.main", "engine", latestVersion),
1641+
),
1642+
},
1643+
{
1644+
Config: fmt.Sprintf(`
1645+
resource "scaleway_rdb_instance" "main" {
1646+
name = "test-rdb-invalid-upgrade"
1647+
node_type = "db-dev-s"
1648+
engine = "PostgreSQL-99.99"
1649+
is_ha_cluster = false
1650+
disable_backup = true
1651+
user_name = "test_user"
1652+
password = "thiZ_is_v&ry_s3cret"
1653+
tags = ["terraform-test", "invalid-upgrade"]
1654+
volume_type = "sbs_5k"
1655+
volume_size_in_gb = 10
1656+
}
1657+
`),
1658+
ExpectError: regexp.MustCompile(`engine version PostgreSQL-99\.99 is not available for upgrade`),
1659+
},
1660+
},
1661+
})
1662+
}
1663+
1664+
func TestAccInstance_UpgradableVersionsRead(t *testing.T) {
1665+
tt := acctest.NewTestTools(t)
1666+
defer tt.Cleanup()
1667+
1668+
latestVersion := rdbchecks.GetLatestEngineVersion(tt, postgreSQLEngineName)
1669+
1670+
resource.ParallelTest(t, resource.TestCase{
1671+
PreCheck: func() { acctest.PreCheck(t) },
1672+
ProtoV6ProviderFactories: tt.ProviderFactories,
1673+
CheckDestroy: rdbchecks.IsInstanceDestroyed(tt),
1674+
Steps: []resource.TestStep{
1675+
{
1676+
Config: fmt.Sprintf(`
1677+
resource "scaleway_rdb_instance" "main" {
1678+
name = "test-rdb-upgradable-versions"
1679+
node_type = "db-dev-s"
1680+
engine = %q
1681+
is_ha_cluster = false
1682+
disable_backup = true
1683+
user_name = "test_user"
1684+
password = "thiZ_is_v&ry_s3cret"
1685+
volume_type = "sbs_5k"
1686+
volume_size_in_gb = 10
1687+
}
1688+
1689+
output "upgradable_versions" {
1690+
value = scaleway_rdb_instance.main.upgradable_versions
1691+
}
1692+
`, latestVersion),
1693+
Check: resource.ComposeTestCheckFunc(
1694+
isInstancePresent(tt, "scaleway_rdb_instance.main"),
1695+
resource.TestCheckResourceAttrSet("scaleway_rdb_instance.main", "upgradable_versions.#"),
1696+
func(s *terraform.State) error {
1697+
rs, ok := s.RootModule().Resources["scaleway_rdb_instance.main"]
1698+
if !ok {
1699+
return fmt.Errorf("resource not found: scaleway_rdb_instance.main")
1700+
}
1701+
1702+
upgradableVersionsCount := rs.Primary.Attributes["upgradable_versions.#"]
1703+
if upgradableVersionsCount == "" || upgradableVersionsCount == "0" {
1704+
return fmt.Errorf("expected at least one upgradable version, got %s", upgradableVersionsCount)
1705+
}
1706+
1707+
// Check that each version has the required fields
1708+
for i := 0; ; i++ {
1709+
idKey := fmt.Sprintf("upgradable_versions.%d.id", i)
1710+
if _, ok := rs.Primary.Attributes[idKey]; !ok {
1711+
break
1712+
}
1713+
nameKey := fmt.Sprintf("upgradable_versions.%d.name", i)
1714+
versionKey := fmt.Sprintf("upgradable_versions.%d.version", i)
1715+
minorKey := fmt.Sprintf("upgradable_versions.%d.minor_version", i)
1716+
1717+
if rs.Primary.Attributes[nameKey] == "" {
1718+
return fmt.Errorf("upgradable_versions[%d].name is empty", i)
1719+
}
1720+
if rs.Primary.Attributes[versionKey] == "" {
1721+
return fmt.Errorf("upgradable_versions[%d].version is empty", i)
1722+
}
1723+
if rs.Primary.Attributes[minorKey] == "" {
1724+
return fmt.Errorf("upgradable_versions[%d].minor_version is empty", i)
1725+
}
1726+
}
1727+
1728+
return nil
1729+
},
1730+
),
1731+
},
1732+
},
1733+
})
1734+
}
1735+
1736+
func TestAccInstance_EngineUpgrade_OldInstanceDestroyed(t *testing.T) {
1737+
tt := acctest.NewTestTools(t)
1738+
defer tt.Cleanup()
1739+
1740+
oldVersion, newVersion := rdbchecks.GetEngineVersionsForUpgrade(tt, postgreSQLEngineName)
1741+
if oldVersion == newVersion {
1742+
t.Skip("Need at least 2 different PostgreSQL versions for upgrade testing")
1743+
}
1744+
1745+
var oldInstanceID string
1746+
1747+
resource.ParallelTest(t, resource.TestCase{
1748+
PreCheck: func() { acctest.PreCheck(t) },
1749+
ProtoV6ProviderFactories: tt.ProviderFactories,
1750+
CheckDestroy: rdbchecks.IsInstanceDestroyed(tt),
1751+
Steps: []resource.TestStep{
1752+
{
1753+
Config: fmt.Sprintf(`
1754+
resource "scaleway_rdb_instance" "main" {
1755+
name = "test-rdb-old-destroyed"
1756+
node_type = "db-dev-s"
1757+
engine = %q
1758+
is_ha_cluster = false
1759+
disable_backup = true
1760+
user_name = "test_user"
1761+
password = "thiZ_is_v&ry_s3cret"
1762+
tags = ["terraform-test", "old-instance-check"]
1763+
volume_type = "sbs_5k"
1764+
volume_size_in_gb = 10
1765+
}
1766+
`, oldVersion),
1767+
Check: resource.ComposeTestCheckFunc(
1768+
isInstancePresent(tt, "scaleway_rdb_instance.main"),
1769+
resource.TestCheckResourceAttr("scaleway_rdb_instance.main", "engine", oldVersion),
1770+
func(s *terraform.State) error {
1771+
rs, ok := s.RootModule().Resources["scaleway_rdb_instance.main"]
1772+
if !ok {
1773+
return fmt.Errorf("resource not found: scaleway_rdb_instance.main")
1774+
}
1775+
_, _, ID, err := rdb.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
1776+
if err != nil {
1777+
return err
1778+
}
1779+
oldInstanceID = ID
1780+
return nil
1781+
},
1782+
),
1783+
},
1784+
{
1785+
Config: fmt.Sprintf(`
1786+
resource "scaleway_rdb_instance" "main" {
1787+
name = "test-rdb-old-destroyed"
1788+
node_type = "db-dev-s"
1789+
engine = %q
1790+
is_ha_cluster = false
1791+
disable_backup = true
1792+
user_name = "test_user"
1793+
password = "thiZ_is_v&ry_s3cret"
1794+
tags = ["terraform-test", "old-instance-check"]
1795+
volume_type = "sbs_5k"
1796+
volume_size_in_gb = 10
1797+
}
1798+
`, newVersion),
1799+
Check: resource.ComposeTestCheckFunc(
1800+
isInstancePresent(tt, "scaleway_rdb_instance.main"),
1801+
resource.TestCheckResourceAttr("scaleway_rdb_instance.main", "engine", newVersion),
1802+
func(s *terraform.State) error {
1803+
rs, ok := s.RootModule().Resources["scaleway_rdb_instance.main"]
1804+
if !ok {
1805+
return fmt.Errorf("resource not found: scaleway_rdb_instance.main")
1806+
}
1807+
1808+
rdbAPI, region, newInstanceID, err := rdb.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
1809+
if err != nil {
1810+
return err
1811+
}
1812+
1813+
// Verify the instance ID has changed
1814+
if newInstanceID == oldInstanceID {
1815+
return fmt.Errorf("expected new instance ID after upgrade, but got same ID: %s", newInstanceID)
1816+
}
1817+
1818+
// Verify the old instance is destroyed (should return 404)
1819+
_, err = rdbAPI.GetInstance(&rdbSDK.GetInstanceRequest{
1820+
Region: region,
1821+
InstanceID: oldInstanceID,
1822+
})
1823+
if err == nil {
1824+
return fmt.Errorf("expected old instance %s to be destroyed, but it still exists", oldInstanceID)
1825+
}
1826+
1827+
// Check that the error is a 404
1828+
if !httperrors.Is404(err) {
1829+
return fmt.Errorf("expected 404 error for old instance %s, got: %v", oldInstanceID, err)
1830+
}
1831+
1832+
return nil
1833+
},
1834+
),
1835+
},
1836+
},
1837+
})
1838+
}
1839+
16101840
func isInstancePresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
16111841
return func(s *terraform.State) error {
16121842
rs, ok := s.RootModule().Resources[n]

0 commit comments

Comments
 (0)