RDS: Support PreferredMaintenanceWindow for DBInstance (#5839)
This commit is contained in:
parent
afeebd8993
commit
e47a2fd120
@ -35,7 +35,13 @@ from .exceptions import (
|
||||
SubscriptionNotFoundError,
|
||||
SubscriptionAlreadyExistError,
|
||||
)
|
||||
from .utils import FilterDef, apply_filter, merge_filters, validate_filters
|
||||
from .utils import (
|
||||
FilterDef,
|
||||
apply_filter,
|
||||
merge_filters,
|
||||
validate_filters,
|
||||
valid_preferred_maintenance_window,
|
||||
)
|
||||
|
||||
|
||||
class Cluster:
|
||||
@ -446,9 +452,15 @@ class Database(CloudFormationModel):
|
||||
self.db_subnet_group = None
|
||||
self.security_groups = kwargs.get("security_groups", [])
|
||||
self.vpc_security_group_ids = kwargs.get("vpc_security_group_ids", [])
|
||||
self.preferred_maintenance_window = kwargs.get(
|
||||
"preferred_maintenance_window", "wed:06:38-wed:07:08"
|
||||
self.preferred_maintenance_window = kwargs.get("preferred_maintenance_window")
|
||||
self.preferred_backup_window = kwargs.get("preferred_backup_window")
|
||||
msg = valid_preferred_maintenance_window(
|
||||
self.preferred_maintenance_window,
|
||||
self.preferred_backup_window,
|
||||
)
|
||||
if msg:
|
||||
raise RDSClientError("InvalidParameterValue", msg)
|
||||
|
||||
self.db_parameter_group_name = kwargs.get("db_parameter_group_name")
|
||||
if (
|
||||
self.db_parameter_group_name
|
||||
@ -458,9 +470,6 @@ class Database(CloudFormationModel):
|
||||
):
|
||||
raise DBParameterGroupNotFoundError(self.db_parameter_group_name)
|
||||
|
||||
self.preferred_backup_window = kwargs.get(
|
||||
"preferred_backup_window", "13:14-13:44"
|
||||
)
|
||||
self.license_model = kwargs.get("license_model", "general-public-license")
|
||||
self.option_group_name = kwargs.get("option_group_name", None)
|
||||
self.option_group_supplied = self.option_group_name is not None
|
||||
@ -554,8 +563,8 @@ class Database(CloudFormationModel):
|
||||
<DBInstanceIdentifier>{{ database.db_instance_identifier }}</DBInstanceIdentifier>
|
||||
<DbiResourceId>{{ database.dbi_resource_id }}</DbiResourceId>
|
||||
<InstanceCreateTime>{{ database.instance_create_time }}</InstanceCreateTime>
|
||||
<PreferredBackupWindow>03:50-04:20</PreferredBackupWindow>
|
||||
<PreferredMaintenanceWindow>wed:06:38-wed:07:08</PreferredMaintenanceWindow>
|
||||
<PreferredBackupWindow>{{ database.preferred_backup_window }}</PreferredBackupWindow>
|
||||
<PreferredMaintenanceWindow>{{ database.preferred_maintenance_window }}</PreferredMaintenanceWindow>
|
||||
<ReadReplicaDBInstanceIdentifiers>
|
||||
{% for replica_id in database.replicas %}
|
||||
<ReadReplicaDBInstanceIdentifier>{{ replica_id }}</ReadReplicaDBInstanceIdentifier>
|
||||
@ -771,6 +780,12 @@ class Database(CloudFormationModel):
|
||||
"db_instance_class": properties.get("DBInstanceClass"),
|
||||
"db_instance_identifier": resource_name,
|
||||
"db_name": properties.get("DBName"),
|
||||
"preferred_backup_window": properties.get(
|
||||
"PreferredBackupWindow", "13:14-13:44"
|
||||
),
|
||||
"preferred_maintenance_window": properties.get(
|
||||
"PreferredMaintenanceWindow", "wed:06:38-wed:07:08"
|
||||
).lower(),
|
||||
"db_subnet_group_name": db_subnet_group_name,
|
||||
"engine": properties.get("Engine"),
|
||||
"engine_version": properties.get("EngineVersion"),
|
||||
@ -1448,6 +1463,13 @@ class RDSBackend(BaseBackend):
|
||||
"db_instance_identifier"
|
||||
] = db_kwargs.pop("new_db_instance_identifier")
|
||||
self.databases[db_instance_identifier] = database
|
||||
preferred_backup_window = db_kwargs.get("preferred_backup_window")
|
||||
preferred_maintenance_window = db_kwargs.get("preferred_maintenance_window")
|
||||
msg = valid_preferred_maintenance_window(
|
||||
preferred_maintenance_window, preferred_backup_window
|
||||
)
|
||||
if msg:
|
||||
raise RDSClientError("InvalidParameterValue", msg)
|
||||
database.update(db_kwargs)
|
||||
return database
|
||||
|
||||
|
@ -44,8 +44,12 @@ class RDSResponse(BaseResponse):
|
||||
"multi_az": self._get_bool_param("MultiAZ"),
|
||||
"option_group_name": self._get_param("OptionGroupName"),
|
||||
"port": self._get_param("Port"),
|
||||
# PreferredBackupWindow
|
||||
# PreferredMaintenanceWindow
|
||||
"preferred_backup_window": self._get_param(
|
||||
"PreferredBackupWindow", "13:14-13:44"
|
||||
),
|
||||
"preferred_maintenance_window": self._get_param(
|
||||
"PreferredMaintenanceWindow", "wed:06:38-wed:07:08"
|
||||
).lower(),
|
||||
"publicly_accessible": self._get_param("PubliclyAccessible"),
|
||||
"account_id": self.current_account,
|
||||
"region": self.region,
|
||||
|
@ -3,7 +3,10 @@ from collections import namedtuple
|
||||
from botocore.utils import merge_dicts
|
||||
|
||||
from collections import OrderedDict
|
||||
import datetime
|
||||
import re
|
||||
|
||||
SECONDS_IN_ONE_DAY = 24 * 60 * 60
|
||||
FilterDef = namedtuple(
|
||||
"FilterDef",
|
||||
[
|
||||
@ -133,3 +136,155 @@ def apply_filter(resources, filters, filter_defs):
|
||||
if matches_filter:
|
||||
resources_filtered[identifier] = obj
|
||||
return resources_filtered
|
||||
|
||||
|
||||
def get_start_date_end_date(base_date, window):
|
||||
"""Gets the start date and end date given DDD:HH24:MM-DDD:HH24:MM.
|
||||
|
||||
:param base_date:
|
||||
type datetime
|
||||
:param window:
|
||||
DDD:HH24:MM-DDD:HH24:MM
|
||||
:returns:
|
||||
Start and End Date in datetime format
|
||||
:rtype:
|
||||
tuple
|
||||
"""
|
||||
days = {"mon": 1, "tue": 2, "wed": 3, "thu": 4, "fri": 5, "sat": 6, "sun": 7}
|
||||
start = datetime.datetime.strptime(
|
||||
base_date + " " + window[4:9], "%d/%m/%y %H:%M"
|
||||
) + datetime.timedelta(days=days[window[0:3]])
|
||||
end = datetime.datetime.strptime(
|
||||
base_date + " " + window[14::], "%d/%m/%y %H:%M"
|
||||
) + datetime.timedelta(days=days[window[10:13]])
|
||||
return start, end
|
||||
|
||||
|
||||
def get_start_date_end_date_from_time(base_date, window):
|
||||
"""Gets the start date and end date given HH24:MM-HH24:MM.
|
||||
|
||||
:param base_date:
|
||||
type datetime
|
||||
:param window:
|
||||
HH24:MM-HH24:MM
|
||||
:returns:
|
||||
Start and End Date in datetime format
|
||||
along with flag for spills over a day
|
||||
This is useful when determine time overlaps
|
||||
:rtype:
|
||||
tuple
|
||||
"""
|
||||
times = window.split("-")
|
||||
spillover = False
|
||||
start = datetime.datetime.strptime(base_date + " " + times[0], "%d/%m/%y %H:%M")
|
||||
end = datetime.datetime.strptime(base_date + " " + times[1], "%d/%m/%y %H:%M")
|
||||
if end < start:
|
||||
end += datetime.timedelta(days=1)
|
||||
spillover = True
|
||||
return start, end, spillover
|
||||
|
||||
|
||||
def get_overlap_between_two_date_ranges(
|
||||
start_time_1, end_time_1, start_time_2, end_time_2
|
||||
):
|
||||
"""Determines overlap between 2 date ranges.
|
||||
|
||||
:param start_time_1:
|
||||
type datetime
|
||||
:param start_time_2:
|
||||
type datetime
|
||||
:param end_time_1:
|
||||
type datetime
|
||||
:param end_time_2:
|
||||
type datetime
|
||||
:returns:
|
||||
overlap in seconds
|
||||
:rtype:
|
||||
int
|
||||
"""
|
||||
latest_start = max(start_time_1, start_time_2)
|
||||
earliest_end = min(end_time_1, end_time_2)
|
||||
delta = earliest_end - latest_start
|
||||
overlap = (delta.days * SECONDS_IN_ONE_DAY) + delta.seconds
|
||||
return overlap
|
||||
|
||||
|
||||
def valid_preferred_maintenance_window(maintenance_window, backup_window):
|
||||
"""Determines validity of preferred_maintenance_window
|
||||
|
||||
:param maintenance_windown:
|
||||
type DDD:HH24:MM-DDD:HH24:MM
|
||||
:param backup_window:
|
||||
type HH24:MM-HH24:MM
|
||||
:returns:
|
||||
message
|
||||
:rtype:
|
||||
str
|
||||
"""
|
||||
MINUTES_30 = 1800
|
||||
HOURS_24 = 86400
|
||||
base_date = datetime.datetime.now().strftime("%d/%m/%y")
|
||||
try:
|
||||
p = re.compile(
|
||||
"([a-z]{3}):([0-9]{2}):([0-9]{2})-([a-z]{3}):([0-9]{2}):([0-9]{2})"
|
||||
)
|
||||
if len(maintenance_window) != 19 or re.search(p, maintenance_window) is None:
|
||||
return f"Invalid maintenance window format: {maintenance_window}. Should be specified as a range ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). Example: Sun:23:45-Mon:00:15"
|
||||
if backup_window:
|
||||
(
|
||||
backup_window_start,
|
||||
backup_window_end,
|
||||
backup_spill,
|
||||
) = get_start_date_end_date_from_time(base_date, backup_window)
|
||||
(
|
||||
maintenance_window_start,
|
||||
maintenance_window_end,
|
||||
maintenance_spill,
|
||||
) = get_start_date_end_date_from_time(
|
||||
base_date, maintenance_window[4:10] + maintenance_window[14::]
|
||||
)
|
||||
if (
|
||||
get_overlap_between_two_date_ranges(
|
||||
backup_window_start,
|
||||
backup_window_end,
|
||||
maintenance_window_start,
|
||||
maintenance_window_end,
|
||||
)
|
||||
>= 0
|
||||
):
|
||||
return "The backup window and maintenance window must not overlap."
|
||||
|
||||
# Due to spill overs, adjust the windows
|
||||
elif maintenance_spill:
|
||||
backup_window_start += datetime.timedelta(days=1)
|
||||
backup_window_end += datetime.timedelta(days=1)
|
||||
elif backup_spill:
|
||||
maintenance_window_start += datetime.timedelta(days=1)
|
||||
maintenance_window_end += datetime.timedelta(days=1)
|
||||
|
||||
# If spills, rerun overlap test with adjusted windows
|
||||
if maintenance_spill or backup_spill:
|
||||
if (
|
||||
get_overlap_between_two_date_ranges(
|
||||
backup_window_start,
|
||||
backup_window_end,
|
||||
maintenance_window_start,
|
||||
maintenance_window_end,
|
||||
)
|
||||
>= 0
|
||||
):
|
||||
return "The backup window and maintenance window must not overlap."
|
||||
|
||||
maintenance_window_start, maintenance_window_end = get_start_date_end_date(
|
||||
base_date, maintenance_window
|
||||
)
|
||||
delta = maintenance_window_end - maintenance_window_start
|
||||
delta_seconds = delta.seconds + (delta.days * SECONDS_IN_ONE_DAY)
|
||||
if delta_seconds >= MINUTES_30 and delta_seconds <= HOURS_24:
|
||||
return
|
||||
elif delta_seconds >= 0 and delta_seconds <= MINUTES_30:
|
||||
return "The maintenance window must be at least 30 minutes."
|
||||
else:
|
||||
return "Maintenance window must be less than 24 hours."
|
||||
except Exception:
|
||||
return f"Invalid day:hour:minute value: {maintenance_window}"
|
||||
|
@ -75,6 +75,179 @@ def test_create_database_no_allocated_storage():
|
||||
db_instance["Engine"].should.equal("postgres")
|
||||
db_instance["StorageType"].should.equal("gp2")
|
||||
db_instance["AllocatedStorage"].should.equal(20)
|
||||
db_instance["PreferredMaintenanceWindow"].should.equal("wed:06:38-wed:07:08")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_invalid_preferred_maintenance_window_more_24_hours():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="mon:16:00-tue:17:00",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal("Maintenance window must be less than 24 hours.")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_invalid_preferred_maintenance_window_less_30_mins():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="mon:16:00-mon:16:05",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal("The maintenance window must be at least 30 minutes.")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_invalid_preferred_maintenance_window_value():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="sim:16:00-mon:16:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain("Invalid day:hour:minute")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_invalid_preferred_maintenance_window_format():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="mon:16tue:17:00",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"Should be specified as a range ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). Example: Sun:23:45-Mon:00:15"
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_preferred_backup_window_overlap_no_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="wed:18:00-wed:22:00",
|
||||
PreferredBackupWindow="20:00-20:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_preferred_backup_window_overlap_maintenance_window_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="wed:18:00-thu:01:00",
|
||||
PreferredBackupWindow="00:00-00:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_preferred_backup_window_overlap_backup_window_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="thu:00:00-thu:14:00",
|
||||
PreferredBackupWindow="23:50-00:20",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_preferred_backup_window_overlap_both_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="wed:18:00-thu:01:00",
|
||||
PreferredBackupWindow="23:50-00:20",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_valid_preferred_maintenance_window_format():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
database = conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="sun:16:00-sun:16:30",
|
||||
)
|
||||
db_instance = database["DBInstance"]
|
||||
db_instance["DBInstanceClass"].should.equal("db.m1.small")
|
||||
db_instance["PreferredMaintenanceWindow"].should.equal("sun:16:00-sun:16:30")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_create_database_valid_preferred_maintenance_window_uppercase_format():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
database = conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
Engine="postgres",
|
||||
DBName="staging-postgres",
|
||||
DBInstanceClass="db.m1.small",
|
||||
PreferredMaintenanceWindow="MON:16:00-TUE:01:30",
|
||||
)
|
||||
db_instance = database["DBInstance"]
|
||||
db_instance["DBInstanceClass"].should.equal("db.m1.small")
|
||||
db_instance["PreferredMaintenanceWindow"].should.equal("mon:16:00-tue:01:30")
|
||||
|
||||
|
||||
@mock_rds
|
||||
@ -397,6 +570,9 @@ def test_modify_db_instance():
|
||||
)
|
||||
instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
|
||||
instances["DBInstances"][0]["AllocatedStorage"].should.equal(20)
|
||||
instances["DBInstances"][0]["PreferredMaintenanceWindow"].should.equal(
|
||||
"wed:06:38-wed:07:08"
|
||||
)
|
||||
instances["DBInstances"][0]["VpcSecurityGroups"][0][
|
||||
"VpcSecurityGroupId"
|
||||
].should.equal("sg-123456")
|
||||
@ -423,6 +599,252 @@ def test_modify_db_instance_not_existent_db_parameter_group_name():
|
||||
).should.throw(ClientError)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_valid_preferred_maintenance_window():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-sun:16:30",
|
||||
)
|
||||
instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
|
||||
instances["DBInstances"][0]["PreferredMaintenanceWindow"].should.equal(
|
||||
"sun:16:00-sun:16:30"
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_valid_preferred_maintenance_window_uppercase():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="SUN:16:00-SUN:16:30",
|
||||
)
|
||||
instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
|
||||
instances["DBInstances"][0]["PreferredMaintenanceWindow"].should.equal(
|
||||
"sun:16:00-sun:16:30"
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_invalid_preferred_maintenance_window_more_than_24_hours():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-sat:16:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal("Maintenance window must be less than 24 hours.")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_invalid_preferred_maintenance_window_less_than_30_mins():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-sun:16:10",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal("The maintenance window must be at least 30 minutes.")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_invalid_preferred_maintenance_window_value():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sin:16:00-sun:16:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain("Invalid day:hour:minute value")
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_invalid_preferred_maintenance_window_format():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00sun:16:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.contain(
|
||||
"Should be specified as a range ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). Example: Sun:23:45-Mon:00:15"
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_maintenance_backup_window_no_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-sun:16:30",
|
||||
PreferredBackupWindow="15:50-16:20",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_maintenance_backup_window_maintenance_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-mon:15:00",
|
||||
PreferredBackupWindow="00:00-00:30",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_maintenance_backup_window_backup_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="mon:00:00-mon:15:00",
|
||||
PreferredBackupWindow="23:50-00:20",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_modify_db_instance_maintenance_backup_window_both_spill():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
conn.create_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
AllocatedStorage=10,
|
||||
DBInstanceClass="postgres",
|
||||
Engine="db.m1.small",
|
||||
MasterUsername="root",
|
||||
MasterUserPassword="hunter2",
|
||||
Port=1234,
|
||||
DBSecurityGroups=["my_sg"],
|
||||
)
|
||||
with pytest.raises(ClientError) as ex:
|
||||
conn.modify_db_instance(
|
||||
DBInstanceIdentifier="db-master-1",
|
||||
PreferredMaintenanceWindow="sun:16:00-mon:15:00",
|
||||
PreferredBackupWindow="23:20-00:20",
|
||||
)
|
||||
err = ex.value.response["Error"]
|
||||
err["Code"].should.equal("InvalidParameterValue")
|
||||
err["Message"].should.equal(
|
||||
"The backup window and maintenance window must not overlap."
|
||||
)
|
||||
|
||||
|
||||
@mock_rds
|
||||
def test_rename_db_instance():
|
||||
conn = boto3.client("rds", region_name="us-west-2")
|
||||
|
Loading…
Reference in New Issue
Block a user