Skip to content

Commit 9652a8a

Browse files
authored
Validate read replica has matching desired config as replica source instance (#139)
* feat: use full blue_green_deployment object in replica_source * feat: validate engine_version for read replica * chore: bump version to 0.6.8 Signed-off-by: Di Wang <[email protected]>
1 parent 52201e2 commit 9652a8a

7 files changed

+92
-20
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FROM quay.io/redhat-services-prod/app-sre-tenant/er-base-terraform-main/er-base-terraform-main:tf-1.6.6-py-3.12-v0.3.4-1@sha256:1579e58702182a02b55a0841254d188a6b99ff42c774279890567338c863a31b AS base
22
# keep in sync with pyproject.toml
3-
LABEL konflux.additional-tags="0.6.7"
3+
LABEL konflux.additional-tags="0.6.8"
44

55
FROM base AS builder
66
COPY --from=ghcr.io/astral-sh/uv:0.6.13@sha256:0b6dc79013b689f3bc0cbf12807cb1c901beaafe80f2ee10a1d76aa3842afb92 /uv /bin/uv

er_aws_rds/input.py

+40-9
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,6 @@ class ParameterGroup(BaseModel):
6565
parameters: list[Parameter] | None = Field(default=None)
6666

6767

68-
class ReplicaSource(BaseModel):
69-
"AppInterface ReplicaSource"
70-
71-
region: str
72-
identifier: str
73-
blue_green_deployment_enabled: bool
74-
75-
7668
class BlueGreenDeploymentTarget(BaseModel):
7769
"AppInterface BlueGreenDeployment.Target"
7870

@@ -95,6 +87,14 @@ class BlueGreenDeployment(BaseModel):
9587
target: BlueGreenDeploymentTarget | None = None
9688

9789

90+
class ReplicaSource(BaseModel):
91+
"AppInterface ReplicaSource"
92+
93+
region: str
94+
identifier: str
95+
blue_green_deployment: BlueGreenDeployment | None = None
96+
97+
9898
class DBInstanceTimeouts(BaseModel):
9999
"DBInstance timeouts"
100100

@@ -335,7 +335,25 @@ def _validate_blue_green_update(self) -> Self:
335335

336336
@model_validator(mode="after")
337337
def _validate_blue_green_deployment_for_replica(self) -> Self:
338-
if self.replica_source and self.replica_source.blue_green_deployment_enabled:
338+
"""
339+
Validate the blue_green_deployment for read replicas
340+
341+
* blue_green_deployment is not supported for read replica instance (only supported for primary instance)
342+
* If the replica_source has blue_green_deployment enabled
343+
* parameter_group must be None
344+
* deletion_protection must be False
345+
* region must match replica_source region
346+
* engine_version must match replica_source engine_version if specified in target after switchover and delete,
347+
note only engine_version is applied to all instances,
348+
instance-class is supposed to be applied to all instances but actually only applied to primary instance,
349+
so we only validate engine_version.
350+
doc: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments-creating.html#create-blue-green-settings
351+
"""
352+
if (
353+
self.replica_source
354+
and (blue_green_deployment := self.replica_source.blue_green_deployment)
355+
and blue_green_deployment.enabled
356+
):
339357
if self.parameter_group:
340358
raise ValueError(
341359
"parameter_group is not supported when replica_source has blue_green_deployment enabled"
@@ -348,6 +366,19 @@ def _validate_blue_green_deployment_for_replica(self) -> Self:
348366
raise ValueError(
349367
"Cross-region read replicas are not currently supported for Blue Green Deployments"
350368
)
369+
if (
370+
blue_green_deployment.switchover
371+
and blue_green_deployment.delete
372+
and blue_green_deployment.target
373+
and (engine_version := blue_green_deployment.target.engine_version)
374+
and engine_version != self.engine_version
375+
):
376+
not_matched = {
377+
"engine_version": engine_version,
378+
}
379+
raise ValueError(
380+
f"desired config not match replica_source blue_green_deployment.target, update: {not_matched}"
381+
)
351382
if self.is_read_replica and self.blue_green_deployment:
352383
raise ValueError(
353384
"blue_green_deployment is not supported for replica instance"

hooks/utils/blue_green_deployment_manager.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ def __init__(
5454
def run(self) -> State:
5555
"""Run Blue/Green Deployment Manager"""
5656
if (
57-
replica_source := self.app_interface_input.data.replica_source
58-
) and replica_source.blue_green_deployment_enabled:
57+
(replica_source := self.app_interface_input.data.replica_source)
58+
and replica_source.blue_green_deployment
59+
and replica_source.blue_green_deployment.enabled
60+
):
5961
self.logger.info("blue_green_deployment in replica_source enabled.")
6062
return State.REPLICA_SOURCE_ENABLED
6163

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "er-aws-rds"
3-
version = "0.6.7"
3+
version = "0.6.8"
44
description = "ERv2 module for managing AWS rds instances"
55
authors = [{ name = "AppSRE", email = "[email protected]" }]
66
license = { text = "Apache 2.0" }

tests/test_blue_green_deployment_manager.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,9 @@ def test_run_for_read_replica_has_blue_green_deployment_enabled(
11211121
"replica_source": {
11221122
"identifier": "test-rds-source",
11231123
"region": "us-east-1",
1124-
"blue_green_deployment_enabled": True,
1124+
"blue_green_deployment": {
1125+
"enabled": True,
1126+
},
11251127
},
11261128
"parameter_group": None,
11271129
}

tests/test_input_validation.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ def test_validate_blue_green_deployment_for_replica_with_parameter_group() -> No
240240
"replica_source": {
241241
"identifier": "test-rds-source",
242242
"region": "us-east-1",
243-
"blue_green_deployment_enabled": True,
243+
"blue_green_deployment": {
244+
"enabled": True,
245+
},
244246
},
245247
"parameter_group": DEFAULT_PARAMETER_GROUP,
246248
}
@@ -259,7 +261,9 @@ def test_validate_blue_green_deployment_for_replica_with_deletion_protection() -
259261
"replica_source": {
260262
"identifier": "test-rds-source",
261263
"region": "us-east-1",
262-
"blue_green_deployment_enabled": True,
264+
"blue_green_deployment": {
265+
"enabled": True,
266+
},
263267
},
264268
"parameter_group": None,
265269
"deletion_protection": True,
@@ -272,14 +276,45 @@ def test_validate_blue_green_deployment_for_replica_with_deletion_protection() -
272276
AppInterfaceInput.model_validate(mod_input)
273277

274278

279+
def test_validate_blue_green_deployment_for_replica_when_desired_config_not_match_target() -> (
280+
None
281+
):
282+
"""Test replica desired config match target"""
283+
mod_input = input_data({
284+
"data": {
285+
"replica_source": {
286+
"identifier": "test-rds-source",
287+
"region": "us-east-1",
288+
"blue_green_deployment": {
289+
"enabled": True,
290+
"switchover": True,
291+
"delete": True,
292+
"target": DEFAULT_TARGET,
293+
},
294+
},
295+
"parameter_group": None,
296+
}
297+
})
298+
expected_not_matched = {
299+
"engine_version": "15.7",
300+
}
301+
with pytest.raises(
302+
ValidationError,
303+
match=rf".*desired config not match replica_source blue_green_deployment.target, update: {expected_not_matched}.*",
304+
):
305+
AppInterfaceInput.model_validate(mod_input)
306+
307+
275308
def test_validate_blue_green_deployment_for_replica() -> None:
276309
"""Test that blue_green_deployment is not supported for replica instance"""
277310
mod_input = input_data({
278311
"data": {
279312
"replica_source": {
280313
"identifier": "test-rds-source",
281314
"region": "us-east-1",
282-
"blue_green_deployment_enabled": False,
315+
"blue_green_deployment": {
316+
"enabled": False,
317+
},
283318
},
284319
"blue_green_deployment": {
285320
"enabled": False,
@@ -302,7 +337,9 @@ def test_validate_blue_green_deployment_for_cross_region_replica() -> None:
302337
"replica_source": {
303338
"identifier": "test-rds-source",
304339
"region": "us-east-1",
305-
"blue_green_deployment_enabled": True,
340+
"blue_green_deployment": {
341+
"enabled": True,
342+
},
306343
},
307344
"region": "us-west-2",
308345
"db_subnet_group_name": "test-subnet-group",
@@ -332,6 +369,6 @@ def test_validate_blue_green_deployment_when_desired_config_not_match_target_aft
332369
})
333370
with pytest.raises(
334371
ValidationError,
335-
match=r".*desired config not match blue_green_deployment.target after delete.*",
372+
match=r".*desired config not match blue_green_deployment.target after delete, update: .*",
336373
):
337374
AppInterfaceInput.model_validate(mod_input)

uv.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)