Skip to content

Commit 46f6c91

Browse files
authored
Merge pull request #30247 from hashicorp/td-aws_rds_cluster-pending-engine-version
Handle pending RDS engine version updates
2 parents 4d4efd7 + dd79b92 commit 46f6c91

File tree

7 files changed

+149
-17
lines changed

7 files changed

+149
-17
lines changed

.changelog/30247.txt

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
```release-note:bug
2+
resource/aws_rds_cluster: Fix inconsistent final plan errors when `engine_version` updates are not applied immediately
3+
```
4+
5+
```release-note:bug
6+
resource/aws_rds_cluster_instance: Fix inconsistent final plan errors when `engine_version` updates are not applied immediately
7+
```
8+
9+
```release-note:bug
10+
resource/aws_rds_instance: Fix inconsistent final plan errors when `engine_version` updates are not applied immediately
11+
```
12+
13+
```release-note:bug
14+
resource/aws_rds_cluster: Send `db_instance_parameter_group_name` on all modify requests when set
15+
```
16+

internal/service/rds/cluster.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,11 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int
12371237
input.DBClusterParameterGroupName = aws.String(d.Get("db_cluster_parameter_group_name").(string))
12381238
}
12391239

1240-
if d.HasChange("db_instance_parameter_group_name") {
1241-
input.DBInstanceParameterGroupName = aws.String(d.Get("db_instance_parameter_group_name").(string))
1240+
// DB instance parameter group name is not currently returned from the
1241+
// DescribeDBClusters API. This means there is no drift detection, so when
1242+
// set, the configured attribute should always be sent on modify.
1243+
if v, ok := d.GetOk("db_instance_parameter_group_name"); ok || d.HasChange("db_instance_parameter_group_name") {
1244+
input.DBInstanceParameterGroupName = aws.String(v.(string))
12421245
}
12431246

12441247
if d.HasChange("deletion_protection") {
@@ -1268,6 +1271,13 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int
12681271
input.EngineVersion = aws.String(d.Get("engine_version").(string))
12691272
}
12701273

1274+
// This can happen when updates are deferred (apply_immediately = false), and
1275+
// multiple applies occur before the maintenance window. In this case,
1276+
// continue sending the desired engine_version as part of the modify request.
1277+
if d.Get("engine_version").(string) != d.Get("engine_version_actual").(string) {
1278+
input.EngineVersion = aws.String(d.Get("engine_version").(string))
1279+
}
1280+
12711281
if d.HasChange("iam_database_authentication_enabled") {
12721282
input.EnableIAMDatabaseAuthentication = aws.Bool(d.Get("iam_database_authentication_enabled").(bool))
12731283
}
@@ -1562,7 +1572,11 @@ func removeIAMRoleFromCluster(ctx context.Context, conn *rds.RDS, clusterID, rol
15621572
func clusterSetResourceDataEngineVersionFromCluster(d *schema.ResourceData, c *rds.DBCluster) {
15631573
oldVersion := d.Get("engine_version").(string)
15641574
newVersion := aws.StringValue(c.EngineVersion)
1565-
compareActualEngineVersion(d, oldVersion, newVersion)
1575+
var pendingVersion string
1576+
if c.PendingModifiedValues != nil && c.PendingModifiedValues.EngineVersion != nil {
1577+
pendingVersion = aws.StringValue(c.PendingModifiedValues.EngineVersion)
1578+
}
1579+
compareActualEngineVersion(d, oldVersion, newVersion, pendingVersion)
15661580
}
15671581

15681582
func FindDBClusterByID(ctx context.Context, conn *rds.RDS, id string) (*rds.DBCluster, error) {

internal/service/rds/cluster_instance.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -563,5 +563,9 @@ func resourceClusterInstanceDelete(ctx context.Context, d *schema.ResourceData,
563563
func clusterSetResourceDataEngineVersionFromClusterInstance(d *schema.ResourceData, c *rds.DBInstance) {
564564
oldVersion := d.Get("engine_version").(string)
565565
newVersion := aws.StringValue(c.EngineVersion)
566-
compareActualEngineVersion(d, oldVersion, newVersion)
566+
var pendingVersion string
567+
if c.PendingModifiedValues != nil && c.PendingModifiedValues.EngineVersion != nil {
568+
pendingVersion = aws.StringValue(c.PendingModifiedValues.EngineVersion)
569+
}
570+
compareActualEngineVersion(d, oldVersion, newVersion, pendingVersion)
567571
}

internal/service/rds/cluster_test.go

+64-5
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func TestAccRDSCluster_allowMajorVersionUpgrade(t *testing.T) {
217217
CheckDestroy: testAccCheckClusterDestroy(ctx),
218218
Steps: []resource.TestStep{
219219
{
220-
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion1),
220+
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion1, true),
221221
Check: resource.ComposeTestCheckFunc(
222222
testAccCheckClusterExists(ctx, resourceName, &dbCluster1),
223223
resource.TestCheckResourceAttr(resourceName, "allow_major_version_upgrade", "true"),
@@ -240,7 +240,65 @@ func TestAccRDSCluster_allowMajorVersionUpgrade(t *testing.T) {
240240
},
241241
},
242242
{
243-
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion2),
243+
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion2, true),
244+
Check: resource.ComposeTestCheckFunc(
245+
testAccCheckClusterExists(ctx, resourceName, &dbCluster2),
246+
resource.TestCheckResourceAttr(resourceName, "allow_major_version_upgrade", "true"),
247+
resource.TestCheckResourceAttr(resourceName, "engine", engine),
248+
resource.TestCheckResourceAttr(resourceName, "engine_version", engineVersion2),
249+
),
250+
},
251+
},
252+
})
253+
}
254+
255+
func TestAccRDSCluster_allowMajorVersionUpgradeNoApplyImmediately(t *testing.T) {
256+
ctx := acctest.Context(t)
257+
if testing.Short() {
258+
t.Skip("skipping long-running test in short mode")
259+
}
260+
261+
var dbCluster1, dbCluster2 rds.DBCluster
262+
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
263+
resourceName := "aws_rds_cluster.test"
264+
// If these hardcoded versions become a maintenance burden, use DescribeDBEngineVersions
265+
// either by having a new data source created or implementing the testing similar
266+
// to TestAccDMSReplicationInstance_engineVersion
267+
engine := "aurora-postgresql"
268+
engineVersion1 := "12.9"
269+
engineVersion2 := "13.5"
270+
271+
resource.ParallelTest(t, resource.TestCase{
272+
PreCheck: func() { acctest.PreCheck(ctx, t) },
273+
ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID),
274+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
275+
CheckDestroy: testAccCheckClusterDestroy(ctx),
276+
Steps: []resource.TestStep{
277+
{
278+
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion1, false),
279+
Check: resource.ComposeTestCheckFunc(
280+
testAccCheckClusterExists(ctx, resourceName, &dbCluster1),
281+
resource.TestCheckResourceAttr(resourceName, "allow_major_version_upgrade", "true"),
282+
resource.TestCheckResourceAttr(resourceName, "engine", engine),
283+
resource.TestCheckResourceAttr(resourceName, "engine_version", engineVersion1),
284+
),
285+
},
286+
{
287+
ResourceName: resourceName,
288+
ImportState: true,
289+
ImportStateVerify: true,
290+
ImportStateVerifyIgnore: []string{
291+
"allow_major_version_upgrade",
292+
"apply_immediately",
293+
"cluster_identifier_prefix",
294+
"db_instance_parameter_group_name",
295+
"enable_global_write_forwarding",
296+
"master_password",
297+
"skip_final_snapshot",
298+
},
299+
},
300+
{
301+
Config: testAccClusterConfig_allowMajorVersionUpgrade(rName, true, engine, engineVersion2, false),
244302
Check: resource.ComposeTestCheckFunc(
245303
testAccCheckClusterExists(ctx, resourceName, &dbCluster2),
246304
resource.TestCheckResourceAttr(resourceName, "allow_major_version_upgrade", "true"),
@@ -2599,11 +2657,11 @@ resource "aws_rds_cluster" "test" {
25992657
`, identifierPrefix)
26002658
}
26012659

2602-
func testAccClusterConfig_allowMajorVersionUpgrade(rName string, allowMajorVersionUpgrade bool, engine string, engineVersion string) string {
2660+
func testAccClusterConfig_allowMajorVersionUpgrade(rName string, allowMajorVersionUpgrade bool, engine string, engineVersion string, applyImmediately bool) string {
26032661
return fmt.Sprintf(`
26042662
resource "aws_rds_cluster" "test" {
26052663
allow_major_version_upgrade = %[1]t
2606-
apply_immediately = true
2664+
apply_immediately = %[5]t
26072665
cluster_identifier = %[2]q
26082666
engine = %[3]q
26092667
engine_version = %[4]q
@@ -2620,6 +2678,7 @@ data "aws_rds_orderable_db_instance" "test" {
26202678
26212679
# Upgrading requires a healthy primary instance
26222680
resource "aws_rds_cluster_instance" "test" {
2681+
apply_immediately = %[5]t
26232682
cluster_identifier = aws_rds_cluster.test.id
26242683
engine = data.aws_rds_orderable_db_instance.test.engine
26252684
engine_version = data.aws_rds_orderable_db_instance.test.engine_version
@@ -2630,7 +2689,7 @@ resource "aws_rds_cluster_instance" "test" {
26302689
ignore_changes = [engine_version]
26312690
}
26322691
}
2633-
`, allowMajorVersionUpgrade, rName, engine, engineVersion)
2692+
`, allowMajorVersionUpgrade, rName, engine, engineVersion, applyImmediately)
26342693
}
26352694

26362695
func testAccClusterConfig_allowMajorVersionUpgradeCustomParameters(rName string, allowMajorVersionUpgrade bool, engine string, engineVersion string, applyImmediate bool) string {

internal/service/rds/engine_version_test.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,17 @@ func TestCompareActualEngineVersion(t *testing.T) {
1010
type testCase struct {
1111
configuredVersion string
1212
actualVersion string
13+
pendingVersion string
1314
expectedEngineVersion string
1415
expectedEngineVersionActual string
1516
}
1617
tests := map[string]testCase{
18+
"import": {
19+
configuredVersion: "", // no "old" value on import
20+
actualVersion: "8.1",
21+
expectedEngineVersion: "8.1",
22+
expectedEngineVersionActual: "8.1",
23+
},
1724
"point version upgrade": {
1825
configuredVersion: "8.0",
1926
actualVersion: "8.0.27",
@@ -32,6 +39,20 @@ func TestCompareActualEngineVersion(t *testing.T) {
3239
expectedEngineVersion: "9.0.0",
3340
expectedEngineVersionActual: "9.0.0",
3441
},
42+
"pending minor version upgrade": {
43+
configuredVersion: "8.1.1",
44+
actualVersion: "8.0",
45+
pendingVersion: "8.1.1",
46+
expectedEngineVersion: "8.1.1",
47+
expectedEngineVersionActual: "8.0",
48+
},
49+
"pending major version upgrade": {
50+
configuredVersion: "9.0.0",
51+
actualVersion: "8.1",
52+
pendingVersion: "9.0.0",
53+
expectedEngineVersion: "9.0.0",
54+
expectedEngineVersionActual: "8.1",
55+
},
3556
"aurora upgrade": {
3657
configuredVersion: "5.7.mysql_aurora.2.07",
3758
actualVersion: "5.7.serverless_mysql_aurora.2.08.3",
@@ -66,7 +87,7 @@ func TestCompareActualEngineVersion(t *testing.T) {
6687
r := ResourceCluster()
6788
d := r.Data(nil)
6889
d.Set("engine_version", test.configuredVersion)
69-
compareActualEngineVersion(d, test.configuredVersion, test.actualVersion)
90+
compareActualEngineVersion(d, test.configuredVersion, test.actualVersion, test.pendingVersion)
7091

7192
if want, got := test.expectedEngineVersion, d.Get("engine_version"); got != want {
7293
t.Errorf("unexpected engine_version; want: %q, got: %q", want, got)

internal/service/rds/instance.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -2339,7 +2339,11 @@ func isStorageTypeGP3BelowAllocatedStorageThreshold(d *schema.ResourceData) bool
23392339
func dbSetResourceDataEngineVersionFromInstance(d *schema.ResourceData, c *rds.DBInstance) {
23402340
oldVersion := d.Get("engine_version").(string)
23412341
newVersion := aws.StringValue(c.EngineVersion)
2342-
compareActualEngineVersion(d, oldVersion, newVersion)
2342+
var pendingVersion string
2343+
if c.PendingModifiedValues != nil && c.PendingModifiedValues.EngineVersion != nil {
2344+
pendingVersion = aws.StringValue(c.PendingModifiedValues.EngineVersion)
2345+
}
2346+
compareActualEngineVersion(d, oldVersion, newVersion, pendingVersion)
23432347
}
23442348

23452349
type dbInstanceARN struct {

internal/service/rds/verify.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,30 @@ import (
44
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
55
)
66

7-
func compareActualEngineVersion(d *schema.ResourceData, oldVersion string, newVersion string) {
8-
newVersionSubstr := newVersion
7+
// compareActualEngineVersion sets engine version related attributes
8+
//
9+
// `engine_version_actual` is always set to newVersion
10+
//
11+
// `engine_version` is set to newVersion unless:
12+
// - old and pending versions are equal (ie. the update is awaiting a
13+
// maintenance window)
14+
// - old and new versions are not exactly equal, but match after accounting
15+
// for an omitted patch value in the configuration (ie. old="1.3",
16+
// new="1.3.27" will not trigger a set)
17+
func compareActualEngineVersion(d *schema.ResourceData, oldVersion, newVersion, pendingVersion string) {
18+
d.Set("engine_version_actual", newVersion)
19+
20+
if oldVersion != "" && oldVersion == pendingVersion {
21+
return
22+
}
923

24+
newVersionSubstr := newVersion
1025
if len(newVersion) > len(oldVersion) {
1126
newVersionSubstr = string([]byte(newVersion)[0 : len(oldVersion)+1])
1227
}
13-
14-
if oldVersion != newVersion && string(append([]byte(oldVersion), []byte(".")...)) != newVersionSubstr {
15-
d.Set("engine_version", newVersion)
28+
if oldVersion != newVersion && oldVersion+"." == newVersionSubstr {
29+
return
1630
}
1731

18-
d.Set("engine_version_actual", newVersion)
32+
d.Set("engine_version", newVersion)
1933
}

0 commit comments

Comments
 (0)