Various RDS, RDS/Cloudformation, RDS/KMS improvements. (#789)
We need to mock out deploying RDS instances with full disk encryption and detailed tagging. We also need to be able do deploy these instances with Cloudformation, and then access them with both boto and boto3. * Join RDS and RDS2 backends - this makes RDS resources created via either of the two boto RDS APIs visible to both, more closely mirroring how AWS works * Fix RDS responses that were returning JSON but should be returning XML * Add mocking of RDS Cloudformation calls * Add mocking of RDS full disk encryption with KMS * Add mocking of RDS DBParameterGroups * Fix mocking of RDS DBSecurityGroupIngress rules * Make mocking of RDS OptionGroupOptions more accurate * Fix mocking of RDS cross-region DB replication * Add RDS tag support to: * DBs * DBSubnetGroups * DBSecurityGroups Signed-off-by: Andrew Garrett <andrew.garrett@getbraintree.com>
This commit is contained in:
parent
201efd5773
commit
74bbd9c8e5
@ -14,6 +14,7 @@ from moto.elb import models as elb_models
|
||||
from moto.iam import models as iam_models
|
||||
from moto.kms import models as kms_models
|
||||
from moto.rds import models as rds_models
|
||||
from moto.rds2 import models as rds2_models
|
||||
from moto.redshift import models as redshift_models
|
||||
from moto.route53 import models as route53_models
|
||||
from moto.s3 import models as s3_models
|
||||
@ -56,6 +57,7 @@ MODEL_MAP = {
|
||||
"AWS::RDS::DBInstance": rds_models.Database,
|
||||
"AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup,
|
||||
"AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup,
|
||||
"AWS::RDS::DBParameterGroup": rds2_models.DBParameterGroup,
|
||||
"AWS::Redshift::Cluster": redshift_models.Cluster,
|
||||
"AWS::Redshift::ClusterParameterGroup": redshift_models.ParameterGroup,
|
||||
"AWS::Redshift::ClusterSubnetGroup": redshift_models.SubnetGroup,
|
||||
@ -311,6 +313,7 @@ class ResourceMap(collections.Mapping):
|
||||
if not resource_json:
|
||||
raise KeyError(resource_logical_id)
|
||||
new_resource = parse_and_create_resource(resource_logical_id, resource_json, self, self._region_name)
|
||||
if new_resource is not None:
|
||||
self._parsed_resources[resource_logical_id] = new_resource
|
||||
return new_resource
|
||||
|
||||
|
@ -10,6 +10,7 @@ from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.utils import get_random_hex
|
||||
from moto.ec2.models import ec2_backends
|
||||
from moto.rds2.models import rds2_backends
|
||||
from .exceptions import DBInstanceNotFoundError, DBSecurityGroupNotFoundError, DBSubnetGroupNotFoundError
|
||||
|
||||
|
||||
@ -26,6 +27,11 @@ class Database(object):
|
||||
if self.engine_version is None:
|
||||
self.engine_version = "5.6.21"
|
||||
self.iops = kwargs.get("iops")
|
||||
self.storage_encrypted = kwargs.get("storage_encrypted", False)
|
||||
if self.storage_encrypted:
|
||||
self.kms_key_id = kwargs.get("kms_key_id", "default_kms_key_id")
|
||||
else:
|
||||
self.kms_key_id = kwargs.get("kms_key_id")
|
||||
self.storage_type = kwargs.get("storage_type")
|
||||
self.master_username = kwargs.get('master_username')
|
||||
self.master_password = kwargs.get('master_password')
|
||||
@ -119,6 +125,7 @@ class Database(object):
|
||||
"engine": properties.get("Engine"),
|
||||
"engine_version": properties.get("EngineVersion"),
|
||||
"iops": properties.get("Iops"),
|
||||
"kms_key_id": properties.get("KmsKeyId"),
|
||||
"master_password": properties.get('MasterUserPassword'),
|
||||
"master_username": properties.get('MasterUsername'),
|
||||
"multi_az": properties.get("MultiAZ"),
|
||||
@ -126,7 +133,9 @@ class Database(object):
|
||||
"publicly_accessible": properties.get("PubliclyAccessible"),
|
||||
"region": region_name,
|
||||
"security_groups": security_groups,
|
||||
"storage_encrypted": properties.get("StorageEncrypted"),
|
||||
"storage_type": properties.get("StorageType"),
|
||||
"tags": properties.get("Tags"),
|
||||
}
|
||||
|
||||
rds_backend = rds_backends[region_name]
|
||||
@ -204,6 +213,10 @@ class Database(object):
|
||||
<PubliclyAccessible>{{ database.publicly_accessible }}</PubliclyAccessible>
|
||||
<AutoMinorVersionUpgrade>{{ database.auto_minor_version_upgrade }}</AutoMinorVersionUpgrade>
|
||||
<AllocatedStorage>{{ database.allocated_storage }}</AllocatedStorage>
|
||||
<StorageEncrypted>{{ database.storage_encrypted }}</StorageEncrypted>
|
||||
{% if database.kms_key_id %}
|
||||
<KmsKeyId>{{ database.kms_key_id }}</KmsKeyId>
|
||||
{% endif %}
|
||||
{% if database.iops %}
|
||||
<Iops>{{ database.iops }}</Iops>
|
||||
<StorageType>io1</StorageType>
|
||||
@ -220,6 +233,10 @@ class Database(object):
|
||||
</DBInstance>""")
|
||||
return template.render(database=self)
|
||||
|
||||
def delete(self, region_name):
|
||||
backend = rds_backends[region_name]
|
||||
backend.delete_database(self.db_instance_identifier)
|
||||
|
||||
|
||||
class SecurityGroup(object):
|
||||
def __init__(self, group_name, description):
|
||||
@ -267,14 +284,18 @@ class SecurityGroup(object):
|
||||
properties = cloudformation_json['Properties']
|
||||
group_name = resource_name.lower() + get_random_hex(12)
|
||||
description = properties['GroupDescription']
|
||||
security_group_ingress = properties['DBSecurityGroupIngress']
|
||||
security_group_ingress_rules = properties.get('DBSecurityGroupIngress', [])
|
||||
tags = properties.get('Tags')
|
||||
|
||||
ec2_backend = ec2_backends[region_name]
|
||||
rds_backend = rds_backends[region_name]
|
||||
security_group = rds_backend.create_security_group(
|
||||
group_name,
|
||||
description,
|
||||
tags,
|
||||
)
|
||||
|
||||
for security_group_ingress in security_group_ingress_rules:
|
||||
for ingress_type, ingress_value in security_group_ingress.items():
|
||||
if ingress_type == "CIDRIP":
|
||||
security_group.authorize_cidr(ingress_value)
|
||||
@ -286,6 +307,10 @@ class SecurityGroup(object):
|
||||
security_group.authorize_security_group(subnet)
|
||||
return security_group
|
||||
|
||||
def delete(self, region_name):
|
||||
backend = rds_backends[region_name]
|
||||
backend.delete_security_group(self.group_name)
|
||||
|
||||
|
||||
class SubnetGroup(object):
|
||||
def __init__(self, subnet_name, description, subnets):
|
||||
@ -324,6 +349,7 @@ class SubnetGroup(object):
|
||||
subnet_name = resource_name.lower() + get_random_hex(12)
|
||||
description = properties['DBSubnetGroupDescription']
|
||||
subnet_ids = properties['SubnetIds']
|
||||
tags = properties.get('Tags')
|
||||
|
||||
ec2_backend = ec2_backends[region_name]
|
||||
subnets = [ec2_backend.get_subnet(subnet_id) for subnet_id in subnet_ids]
|
||||
@ -332,102 +358,31 @@ class SubnetGroup(object):
|
||||
subnet_name,
|
||||
description,
|
||||
subnets,
|
||||
tags,
|
||||
)
|
||||
return subnet_group
|
||||
|
||||
def delete(self, region_name):
|
||||
backend = rds_backends[region_name]
|
||||
backend.delete_subnet_group(self.subnet_name)
|
||||
|
||||
|
||||
class RDSBackend(BaseBackend):
|
||||
|
||||
def __init__(self):
|
||||
self.databases = {}
|
||||
self.security_groups = {}
|
||||
self.subnet_groups = {}
|
||||
def __init__(self, region):
|
||||
self.region = region
|
||||
|
||||
def create_database(self, db_kwargs):
|
||||
database_id = db_kwargs['db_instance_identifier']
|
||||
database = Database(**db_kwargs)
|
||||
self.databases[database_id] = database
|
||||
return database
|
||||
def __getattr__(self, attr):
|
||||
return self.rds2_backend().__getattribute__(attr)
|
||||
|
||||
def create_database_replica(self, db_kwargs):
|
||||
database_id = db_kwargs['db_instance_identifier']
|
||||
source_database_id = db_kwargs['source_db_identifier']
|
||||
primary = self.describe_databases(source_database_id)[0]
|
||||
replica = copy.deepcopy(primary)
|
||||
replica.update(db_kwargs)
|
||||
replica.set_as_replica()
|
||||
self.databases[database_id] = replica
|
||||
primary.add_replica(replica)
|
||||
return replica
|
||||
def reset(self):
|
||||
# preserve region
|
||||
region = self.region
|
||||
self.rds2_backend().reset()
|
||||
self.__dict__ = {}
|
||||
self.__init__(region)
|
||||
|
||||
def describe_databases(self, db_instance_identifier=None):
|
||||
if db_instance_identifier:
|
||||
if db_instance_identifier in self.databases:
|
||||
return [self.databases[db_instance_identifier]]
|
||||
else:
|
||||
raise DBInstanceNotFoundError(db_instance_identifier)
|
||||
return self.databases.values()
|
||||
def rds2_backend(self):
|
||||
return rds2_backends[self.region]
|
||||
|
||||
def modify_database(self, db_instance_identifier, db_kwargs):
|
||||
database = self.describe_databases(db_instance_identifier)[0]
|
||||
database.update(db_kwargs)
|
||||
return database
|
||||
|
||||
def delete_database(self, db_instance_identifier):
|
||||
if db_instance_identifier in self.databases:
|
||||
database = self.databases.pop(db_instance_identifier)
|
||||
if database.is_replica:
|
||||
primary = self.describe_databases(database.source_db_identifier)[0]
|
||||
primary.remove_replica(database)
|
||||
database.status = 'deleting'
|
||||
return database
|
||||
else:
|
||||
raise DBInstanceNotFoundError(db_instance_identifier)
|
||||
|
||||
def create_security_group(self, group_name, description):
|
||||
security_group = SecurityGroup(group_name, description)
|
||||
self.security_groups[group_name] = security_group
|
||||
return security_group
|
||||
|
||||
def describe_security_groups(self, security_group_name):
|
||||
if security_group_name:
|
||||
if security_group_name in self.security_groups:
|
||||
return [self.security_groups[security_group_name]]
|
||||
else:
|
||||
raise DBSecurityGroupNotFoundError(security_group_name)
|
||||
return self.security_groups.values()
|
||||
|
||||
def delete_security_group(self, security_group_name):
|
||||
if security_group_name in self.security_groups:
|
||||
return self.security_groups.pop(security_group_name)
|
||||
else:
|
||||
raise DBSecurityGroupNotFoundError(security_group_name)
|
||||
|
||||
def authorize_security_group(self, security_group_name, cidr_ip):
|
||||
security_group = self.describe_security_groups(security_group_name)[0]
|
||||
security_group.authorize_cidr(cidr_ip)
|
||||
return security_group
|
||||
|
||||
def create_subnet_group(self, subnet_name, description, subnets):
|
||||
subnet_group = SubnetGroup(subnet_name, description, subnets)
|
||||
self.subnet_groups[subnet_name] = subnet_group
|
||||
return subnet_group
|
||||
|
||||
def describe_subnet_groups(self, subnet_group_name):
|
||||
if subnet_group_name:
|
||||
if subnet_group_name in self.subnet_groups:
|
||||
return [self.subnet_groups[subnet_group_name]]
|
||||
else:
|
||||
raise DBSubnetGroupNotFoundError(subnet_group_name)
|
||||
return self.subnet_groups.values()
|
||||
|
||||
def delete_subnet_group(self, subnet_name):
|
||||
if subnet_name in self.subnet_groups:
|
||||
return self.subnet_groups.pop(subnet_name)
|
||||
else:
|
||||
raise DBSubnetGroupNotFoundError(subnet_name)
|
||||
|
||||
|
||||
rds_backends = {}
|
||||
for region in boto.rds.regions():
|
||||
rds_backends[region.name] = RDSBackend()
|
||||
rds_backends = dict((region.name, RDSBackend(region.name)) for region in boto.rds.regions())
|
||||
|
@ -12,7 +12,7 @@ class RDSResponse(BaseResponse):
|
||||
return rds_backends[self.region]
|
||||
|
||||
def _get_db_kwargs(self):
|
||||
return {
|
||||
args = {
|
||||
"auto_minor_version_upgrade": self._get_param('AutoMinorVersionUpgrade'),
|
||||
"allocated_storage": self._get_int_param('AllocatedStorage'),
|
||||
"availability_zone": self._get_param("AvailabilityZone"),
|
||||
@ -25,6 +25,7 @@ class RDSResponse(BaseResponse):
|
||||
"engine": self._get_param("Engine"),
|
||||
"engine_version": self._get_param("EngineVersion"),
|
||||
"iops": self._get_int_param("Iops"),
|
||||
"kms_key_id": self._get_param("KmsKeyId"),
|
||||
"master_password": self._get_param('MasterUserPassword'),
|
||||
"master_username": self._get_param('MasterUsername'),
|
||||
"multi_az": self._get_bool_param("MultiAZ"),
|
||||
@ -35,9 +36,13 @@ class RDSResponse(BaseResponse):
|
||||
"publicly_accessible": self._get_param("PubliclyAccessible"),
|
||||
"region": self.region,
|
||||
"security_groups": self._get_multi_param('DBSecurityGroups.member'),
|
||||
"storage_encrypted": self._get_param("StorageEncrypted"),
|
||||
"storage_type": self._get_param("StorageType"),
|
||||
# VpcSecurityGroupIds.member.N
|
||||
"tags": list(),
|
||||
}
|
||||
args['tags'] = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
return args
|
||||
|
||||
def _get_db_replica_kwargs(self):
|
||||
return {
|
||||
@ -54,6 +59,17 @@ class RDSResponse(BaseResponse):
|
||||
"storage_type": self._get_param("StorageType"),
|
||||
}
|
||||
|
||||
def unpack_complex_list_params(self, label, names):
|
||||
unpacked_list = list()
|
||||
count = 1
|
||||
while self._get_param('{0}.{1}.{2}'.format(label, count, names[0])):
|
||||
param = dict()
|
||||
for i in range(len(names)):
|
||||
param[names[i]] = self._get_param('{0}.{1}.{2}'.format(label, count, names[i]))
|
||||
unpacked_list.append(param)
|
||||
count += 1
|
||||
return unpacked_list
|
||||
|
||||
def create_dbinstance(self):
|
||||
db_kwargs = self._get_db_kwargs()
|
||||
|
||||
@ -90,7 +106,8 @@ class RDSResponse(BaseResponse):
|
||||
def create_dbsecurity_group(self):
|
||||
group_name = self._get_param('DBSecurityGroupName')
|
||||
description = self._get_param('DBSecurityGroupDescription')
|
||||
security_group = self.backend.create_security_group(group_name, description)
|
||||
tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
security_group = self.backend.create_security_group(group_name, description, tags)
|
||||
template = self.response_template(CREATE_SECURITY_GROUP_TEMPLATE)
|
||||
return template.render(security_group=security_group)
|
||||
|
||||
@ -118,7 +135,8 @@ class RDSResponse(BaseResponse):
|
||||
description = self._get_param('DBSubnetGroupDescription')
|
||||
subnet_ids = self._get_multi_param('SubnetIds.member')
|
||||
subnets = [ec2_backends[self.region].get_subnet(subnet_id) for subnet_id in subnet_ids]
|
||||
subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets)
|
||||
tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets, tags)
|
||||
template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE)
|
||||
return template.render(subnet_group=subnet_group)
|
||||
|
||||
|
@ -1,20 +1,22 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
from jinja2 import Template
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
|
||||
class RDSClientError(BadRequest):
|
||||
def __init__(self, code, message):
|
||||
super(RDSClientError, self).__init__()
|
||||
self.description = json.dumps({
|
||||
"Error": {
|
||||
"Code": code,
|
||||
"Message": message,
|
||||
'Type': 'Sender',
|
||||
},
|
||||
'RequestId': '6876f774-7273-11e4-85dc-39e55ca848d1',
|
||||
})
|
||||
template = Template("""
|
||||
<RDSClientError>
|
||||
<Error>
|
||||
<Code>{{ code }}</Code>
|
||||
<Message>{{ message }}</Message>
|
||||
<Type>Sender</Type>
|
||||
</Error>
|
||||
<RequestId>6876f774-7273-11e4-85dc-39e55ca848d1</RequestId>
|
||||
</RDSClientError>""")
|
||||
self.description = template.render(code=code, message=message)
|
||||
|
||||
|
||||
class DBInstanceNotFoundError(RDSClientError):
|
||||
@ -37,3 +39,8 @@ class DBSubnetGroupNotFoundError(RDSClientError):
|
||||
'DBSubnetGroupNotFound',
|
||||
"Subnet Group {0} not found.".format(subnet_group_name))
|
||||
|
||||
class DBParameterGroupNotFoundError(RDSClientError):
|
||||
def __init__(self, db_parameter_group_name):
|
||||
super(DBParameterGroupNotFoundError, self).__init__(
|
||||
'DBParameterGroupNotFound',
|
||||
'DB Parameter Group {0} not found.'.format(db_parameter_group_name))
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,8 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
from collections import defaultdict
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.models import ec2_backends
|
||||
from .models import rds2_backends
|
||||
from .exceptions import DBParameterGroupNotFoundError
|
||||
import json
|
||||
import re
|
||||
|
||||
@ -22,11 +24,12 @@ class RDS2Response(BaseResponse):
|
||||
"db_instance_class": self._get_param('DBInstanceClass'),
|
||||
"db_instance_identifier": self._get_param('DBInstanceIdentifier'),
|
||||
"db_name": self._get_param("DBName"),
|
||||
# DBParameterGroupName
|
||||
"db_parameter_group_name": self._get_param("DBParameterGroupName"),
|
||||
"db_subnet_group_name": self._get_param("DBSubnetGroupName"),
|
||||
"engine": self._get_param("Engine"),
|
||||
"engine_version": self._get_param("EngineVersion"),
|
||||
"iops": self._get_int_param("Iops"),
|
||||
"kms_key_id": self._get_param("KmsKeyId"),
|
||||
"master_user_password": self._get_param('MasterUserPassword'),
|
||||
"master_username": self._get_param('MasterUsername'),
|
||||
"multi_az": self._get_bool_param("MultiAZ"),
|
||||
@ -36,12 +39,13 @@ class RDS2Response(BaseResponse):
|
||||
# PreferredMaintenanceWindow
|
||||
"publicly_accessible": self._get_param("PubliclyAccessible"),
|
||||
"region": self.region,
|
||||
"security_groups": self._get_multi_param('DBSecurityGroups.member'),
|
||||
"security_groups": self._get_multi_param('DBSecurityGroups.DBSecurityGroupName'),
|
||||
"storage_encrypted": self._get_param("StorageEncrypted"),
|
||||
"storage_type": self._get_param("StorageType"),
|
||||
# VpcSecurityGroupIds.member.N
|
||||
"tags": list()
|
||||
"tags": list(),
|
||||
}
|
||||
args['tags'] = self.unpack_complex_list_params('Tags.member', ('Key', 'Value'))
|
||||
args['tags'] = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
return args
|
||||
|
||||
def _get_db_replica_kwargs(self):
|
||||
@ -67,6 +71,14 @@ class RDS2Response(BaseResponse):
|
||||
'name': self._get_param('OptionGroupName')
|
||||
}
|
||||
|
||||
def _get_db_parameter_group_kwargs(self):
|
||||
return {
|
||||
'description': self._get_param('Description'),
|
||||
'family': self._get_param('DBParameterGroupFamily'),
|
||||
'name': self._get_param('DBParameterGroupName'),
|
||||
'tags': self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')),
|
||||
}
|
||||
|
||||
def unpack_complex_list_params(self, label, names):
|
||||
unpacked_list = list()
|
||||
count = 1
|
||||
@ -150,7 +162,7 @@ class RDS2Response(BaseResponse):
|
||||
|
||||
def add_tags_to_resource(self):
|
||||
arn = self._get_param('ResourceName')
|
||||
tags = self.unpack_complex_list_params('Tags.member', ('Key', 'Value'))
|
||||
tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
tags = self.backend.add_tags_to_resource(arn, tags)
|
||||
template = self.response_template(ADD_TAGS_TO_RESOURCE_TEMPLATE)
|
||||
return template.render(tags=tags)
|
||||
@ -168,7 +180,8 @@ class RDS2Response(BaseResponse):
|
||||
def create_db_security_group(self):
|
||||
group_name = self._get_param('DBSecurityGroupName')
|
||||
description = self._get_param('DBSecurityGroupDescription')
|
||||
security_group = self.backend.create_security_group(group_name, description)
|
||||
tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
security_group = self.backend.create_security_group(group_name, description, tags)
|
||||
template = self.response_template(CREATE_SECURITY_GROUP_TEMPLATE)
|
||||
return template.render(security_group=security_group)
|
||||
|
||||
@ -206,9 +219,10 @@ class RDS2Response(BaseResponse):
|
||||
def create_db_subnet_group(self):
|
||||
subnet_name = self._get_param('DBSubnetGroupName')
|
||||
description = self._get_param('DBSubnetGroupDescription')
|
||||
subnet_ids = self._get_multi_param('SubnetIds.member')
|
||||
subnet_ids = self._get_multi_param('SubnetIds.SubnetIdentifier')
|
||||
tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value'))
|
||||
subnets = [ec2_backends[self.region].get_subnet(subnet_id) for subnet_id in subnet_ids]
|
||||
subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets)
|
||||
subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets, tags)
|
||||
template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE)
|
||||
return template.render(subnet_group=subnet_group)
|
||||
|
||||
@ -283,225 +297,326 @@ class RDS2Response(BaseResponse):
|
||||
template = self.response_template(MODIFY_OPTION_GROUP_TEMPLATE)
|
||||
return template.render(option_group=option_group)
|
||||
|
||||
def create_dbparameter_group(self):
|
||||
return self.create_db_parameter_group()
|
||||
|
||||
CREATE_DATABASE_TEMPLATE = """{
|
||||
"CreateDBInstanceResponse": {
|
||||
"CreateDBInstanceResult": {
|
||||
"DBInstance": {{ database.to_json() }}
|
||||
},
|
||||
"ResponseMetadata": { "RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4" }
|
||||
}
|
||||
}"""
|
||||
def create_db_parameter_group(self):
|
||||
kwargs = self._get_db_parameter_group_kwargs()
|
||||
db_parameter_group = self.backend.create_db_parameter_group(kwargs)
|
||||
template = self.response_template(CREATE_DB_PARAMETER_GROUP_TEMPLATE)
|
||||
return template.render(db_parameter_group=db_parameter_group)
|
||||
|
||||
CREATE_DATABASE_REPLICA_TEMPLATE = """{"CreateDBInstanceReadReplicaResponse": {
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "5e60c46d-a844-11e4-bb68-17f36418e58f"
|
||||
},
|
||||
"CreateDBInstanceReadReplicaResult": {
|
||||
"DBInstance": {{ database.to_json() }}
|
||||
}
|
||||
}}"""
|
||||
def describe_dbparameter_groups(self):
|
||||
return self.describe_db_parameter_groups()
|
||||
|
||||
DESCRIBE_DATABASES_TEMPLATE = """{
|
||||
"DescribeDBInstancesResponse": {
|
||||
"DescribeDBInstancesResult": {
|
||||
"DBInstances": [
|
||||
def describe_db_parameter_groups(self):
|
||||
kwargs = self._get_db_parameter_group_kwargs()
|
||||
kwargs['max_records'] = self._get_param('MaxRecords')
|
||||
kwargs['marker'] = self._get_param('Marker')
|
||||
db_parameter_groups = self.backend.describe_db_parameter_groups(kwargs)
|
||||
template = self.response_template(DESCRIBE_DB_PARAMETER_GROUPS_TEMPLATE)
|
||||
return template.render(db_parameter_groups=db_parameter_groups)
|
||||
|
||||
def modify_dbparameter_group(self):
|
||||
return self.modify_db_parameter_group()
|
||||
|
||||
def modify_db_parameter_group(self):
|
||||
db_parameter_group_name = self._get_param('DBParameterGroupName')
|
||||
db_parameter_group_parameters = self._get_db_parameter_group_paramters()
|
||||
db_parameter_group = self.backend.modify_db_parameter_group(db_parameter_group_name,
|
||||
db_parameter_group_parameters)
|
||||
template = self.response_template(MODIFY_DB_PARAMETER_GROUP_TEMPLATE)
|
||||
return template.render(db_parameter_group=db_parameter_group)
|
||||
|
||||
def _get_db_parameter_group_paramters(self):
|
||||
parameter_group_parameters = defaultdict(dict)
|
||||
for param_name, value in self.querystring.items():
|
||||
if not param_name.startswith('Parameters.Parameter'):
|
||||
continue
|
||||
|
||||
split_param_name = param_name.split('.')
|
||||
param_id = split_param_name[2]
|
||||
param_setting = split_param_name[3]
|
||||
|
||||
parameter_group_parameters[param_id][param_setting] = value[0]
|
||||
|
||||
return parameter_group_parameters.values()
|
||||
|
||||
def describe_dbparameters(self):
|
||||
return self.describe_db_parameters()
|
||||
|
||||
def describe_db_parameters(self):
|
||||
db_parameter_group_name = self._get_param('DBParameterGroupName')
|
||||
db_parameter_groups = self.backend.describe_db_parameter_groups({'name': db_parameter_group_name})
|
||||
if not db_parameter_groups:
|
||||
raise DBParameterGroupNotFoundError(db_parameter_group_name)
|
||||
|
||||
template = self.response_template(DESCRIBE_DB_PARAMETERS_TEMPLATE)
|
||||
return template.render(db_parameter_group=db_parameter_groups[0])
|
||||
|
||||
def delete_dbparameter_group(self):
|
||||
return self.delete_db_parameter_group()
|
||||
|
||||
def delete_db_parameter_group(self):
|
||||
kwargs = self._get_db_parameter_group_kwargs()
|
||||
db_parameter_group = self.backend.delete_db_parameter_group(kwargs['name'])
|
||||
template = self.response_template(DELETE_DB_PARAMETER_GROUP_TEMPLATE)
|
||||
return template.render(db_parameter_group=db_parameter_group)
|
||||
|
||||
|
||||
CREATE_DATABASE_TEMPLATE = """<CreateDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateDBInstanceResult>
|
||||
{{ database.to_xml() }}
|
||||
</CreateDBInstanceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>523e3218-afc7-11c3-90f5-f90431260ab4</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateDBInstanceResponse>"""
|
||||
|
||||
CREATE_DATABASE_REPLICA_TEMPLATE = """<CreateDBInstanceReadReplicaResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateDBInstanceReadReplicaResult>
|
||||
{{ database.to_xml() }}
|
||||
</CreateDBInstanceReadReplicaResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>5e60c46d-a844-11e4-bb68-17f36418e58f</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateDBInstanceReadReplicaResponse>"""
|
||||
|
||||
DESCRIBE_DATABASES_TEMPLATE = """<DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeDBInstancesResult>
|
||||
<DBInstances>
|
||||
{%- for database in databases -%}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{{ database.to_json() }}
|
||||
{{ database.to_xml() }}
|
||||
{%- endfor -%}
|
||||
]
|
||||
},
|
||||
"ResponseMetadata": { "RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4" }
|
||||
}
|
||||
}"""
|
||||
</DBInstances>
|
||||
</DescribeDBInstancesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>523e3218-afc7-11c3-90f5-f90431260ab4</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeDBInstancesResponse>"""
|
||||
|
||||
MODIFY_DATABASE_TEMPLATE = """{"ModifyDBInstanceResponse": {
|
||||
"ModifyDBInstanceResult": {
|
||||
"DBInstance": {{ database.to_json() }},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "bb58476c-a1a8-11e4-99cf-55e92d4bbada"
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
MODIFY_DATABASE_TEMPLATE = """<ModifyDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ModifyDBInstanceResult>
|
||||
{{ database.to_xml() }}
|
||||
</ModifyDBInstanceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>bb58476c-a1a8-11e4-99cf-55e92d4bbada</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ModifyDBInstanceResponse>"""
|
||||
|
||||
REBOOT_DATABASE_TEMPLATE = """{"RebootDBInstanceResponse": {
|
||||
"RebootDBInstanceResult": {
|
||||
"DBInstance": {{ database.to_json() }},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "d55711cb-a1ab-11e4-99cf-55e92d4bbada"
|
||||
}
|
||||
}
|
||||
}
|
||||
}"""
|
||||
REBOOT_DATABASE_TEMPLATE = """<RebootDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<RebootDBInstanceResult>
|
||||
{{ database.to_xml() }}
|
||||
</RebootDBInstanceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>d55711cb-a1ab-11e4-99cf-55e92d4bbada</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RebootDBInstanceResponse>"""
|
||||
|
||||
|
||||
DELETE_DATABASE_TEMPLATE = """{ "DeleteDBInstanceResponse": {
|
||||
"DeleteDBInstanceResult": {
|
||||
"DBInstance": {{ database.to_json() }}
|
||||
},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
DELETE_DATABASE_TEMPLATE = """<DeleteDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DeleteDBInstanceResult>
|
||||
{{ database.to_xml() }}
|
||||
</DeleteDBInstanceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>7369556f-b70d-11c3-faca-6ba18376ea1b</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteDBInstanceResponse>"""
|
||||
|
||||
CREATE_SECURITY_GROUP_TEMPLATE = """{"CreateDBSecurityGroupResponse": {
|
||||
"CreateDBSecurityGroupResult": {
|
||||
"DBSecurityGroup":
|
||||
{{ security_group.to_json() }},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "462165d0-a77a-11e4-a5fa-75b30c556f97"
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
CREATE_SECURITY_GROUP_TEMPLATE = """<CreateDBSecurityGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateDBSecurityGroupResult>
|
||||
{{ security_group.to_xml() }}
|
||||
</CreateDBSecurityGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>462165d0-a77a-11e4-a5fa-75b30c556f97</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateDBSecurityGroupResponse>"""
|
||||
|
||||
DESCRIBE_SECURITY_GROUPS_TEMPLATE = """{
|
||||
"DescribeDBSecurityGroupsResponse": {
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "5df2014e-a779-11e4-bdb0-594def064d0c"
|
||||
},
|
||||
"DescribeDBSecurityGroupsResult": {
|
||||
"Marker": "null",
|
||||
"DBSecurityGroups": [
|
||||
DESCRIBE_SECURITY_GROUPS_TEMPLATE = """<DescribeDBSecurityGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeDBSecurityGroupsResult>
|
||||
<DBSecurityGroups>
|
||||
{% for security_group in security_groups %}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{{ security_group.to_json() }}
|
||||
{{ security_group.to_xml() }}
|
||||
{% endfor %}
|
||||
]
|
||||
}
|
||||
}
|
||||
}"""
|
||||
</DBSecurityGroups>
|
||||
</DescribeDBSecurityGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>5df2014e-a779-11e4-bdb0-594def064d0c</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeDBSecurityGroupsResponse>"""
|
||||
|
||||
DELETE_SECURITY_GROUP_TEMPLATE = """{"DeleteDBSecurityGroupResponse": {
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "97e846bd-a77d-11e4-ac58-91351c0f3426"
|
||||
}
|
||||
}}"""
|
||||
DELETE_SECURITY_GROUP_TEMPLATE = """<DeleteDBSecurityGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>97e846bd-a77d-11e4-ac58-91351c0f3426</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteDBSecurityGroupResponse>"""
|
||||
|
||||
AUTHORIZE_SECURITY_GROUP_TEMPLATE = """{
|
||||
"AuthorizeDBSecurityGroupIngressResponse": {
|
||||
"AuthorizeDBSecurityGroupIngressResult": {
|
||||
"DBSecurityGroup": {{ security_group.to_json() }}
|
||||
},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "75d32fd5-a77e-11e4-8892-b10432f7a87d"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
AUTHORIZE_SECURITY_GROUP_TEMPLATE = """<AuthorizeDBSecurityGroupIngressResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<AuthorizeDBSecurityGroupIngressResult>
|
||||
{{ security_group.to_xml() }}
|
||||
</AuthorizeDBSecurityGroupIngressResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>75d32fd5-a77e-11e4-8892-b10432f7a87d</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AuthorizeDBSecurityGroupIngressResponse>"""
|
||||
|
||||
CREATE_SUBNET_GROUP_TEMPLATE = """{
|
||||
"CreateDBSubnetGroupResponse": {
|
||||
"CreateDBSubnetGroupResult":
|
||||
{ {{ subnet_group.to_json() }} },
|
||||
"ResponseMetadata": { "RequestId": "3a401b3f-bb9e-11d3-f4c6-37db295f7674" }
|
||||
}
|
||||
}"""
|
||||
CREATE_SUBNET_GROUP_TEMPLATE = """<CreateDBSubnetGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateDBSubnetGroupResult>
|
||||
{{ subnet_group.to_xml() }}
|
||||
</CreateDBSubnetGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>3a401b3f-bb9e-11d3-f4c6-37db295f7674</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateDBSubnetGroupResponse>"""
|
||||
|
||||
DESCRIBE_SUBNET_GROUPS_TEMPLATE = """{
|
||||
"DescribeDBSubnetGroupsResponse": {
|
||||
"DescribeDBSubnetGroupsResult": {
|
||||
"DBSubnetGroups": [
|
||||
DESCRIBE_SUBNET_GROUPS_TEMPLATE = """<DescribeDBSubnetGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeDBSubnetGroupsResult>
|
||||
<DBSubnetGroups>
|
||||
{% for subnet_group in subnet_groups %}
|
||||
{ {{ subnet_group.to_json() }} }{%- if not loop.last -%},{%- endif -%}
|
||||
{{ subnet_group.to_xml() }}
|
||||
{% endfor %}
|
||||
],
|
||||
"Marker": null
|
||||
},
|
||||
"ResponseMetadata": { "RequestId": "b783db3b-b98c-11d3-fbc7-5c0aad74da7c" }
|
||||
}
|
||||
}"""
|
||||
</DBSubnetGroups>
|
||||
</DescribeDBSubnetGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>b783db3b-b98c-11d3-fbc7-5c0aad74da7c</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeDBSubnetGroupsResponse>"""
|
||||
|
||||
DELETE_SUBNET_GROUP_TEMPLATE = """<DeleteDBSubnetGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>13785dd5-a7fc-11e4-bb9c-7f371d0859b0</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteDBSubnetGroupResponse>"""
|
||||
|
||||
DELETE_SUBNET_GROUP_TEMPLATE = """{"DeleteDBSubnetGroupResponse": {"ResponseMetadata": {"RequestId": "13785dd5-a7fc-11e4-bb9c-7f371d0859b0"}}}"""
|
||||
CREATE_OPTION_GROUP_TEMPLATE = """<CreateOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateOptionGroupResult>
|
||||
{{ option_group.to_xml() }}
|
||||
</CreateOptionGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>1e38dad4-9f50-11e4-87ea-a31c60ed2e36</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateOptionGroupResponse>"""
|
||||
|
||||
CREATE_OPTION_GROUP_TEMPLATE = """{
|
||||
"CreateOptionGroupResponse": {
|
||||
"CreateOptionGroupResult": {
|
||||
"OptionGroup": {{ option_group.to_json() }}
|
||||
},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "1e38dad4-9f50-11e4-87ea-a31c60ed2e36"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
DELETE_OPTION_GROUP_TEMPLATE = """<DeleteOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>e2590367-9fa2-11e4-99cf-55e92d41c60e</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteOptionGroupResponse>"""
|
||||
|
||||
DELETE_OPTION_GROUP_TEMPLATE = \
|
||||
"""{"DeleteOptionGroupResponse": {"ResponseMetadata": {"RequestId": "e2590367-9fa2-11e4-99cf-55e92d41c60e"}}}"""
|
||||
|
||||
DESCRIBE_OPTION_GROUP_TEMPLATE = \
|
||||
"""{"DescribeOptionGroupsResponse": {
|
||||
"DescribeOptionGroupsResult": {
|
||||
"Marker": null,
|
||||
"OptionGroupsList": [
|
||||
DESCRIBE_OPTION_GROUP_TEMPLATE = """<DescribeOptionGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeOptionGroupsResult>
|
||||
<OptionGroupsList>
|
||||
{%- for option_group in option_groups -%}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{{ option_group.to_json() }}
|
||||
{{ option_group.to_xml() }}
|
||||
{%- endfor -%}
|
||||
]},
|
||||
"ResponseMetadata": {"RequestId": "4caf445d-9fbc-11e4-87ea-a31c60ed2e36"}
|
||||
}}"""
|
||||
</OptionGroupsList>
|
||||
</DescribeOptionGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4caf445d-9fbc-11e4-87ea-a31c60ed2e36</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeOptionGroupsResponse>"""
|
||||
|
||||
DESCRIBE_OPTION_GROUP_OPTIONS_TEMPLATE = \
|
||||
"""{"DescribeOptionGroupOptionsResponse": {
|
||||
"DescribeOptionGroupOptionsResult": {
|
||||
"Marker": null,
|
||||
"OptionGroupOptions": [
|
||||
DESCRIBE_OPTION_GROUP_OPTIONS_TEMPLATE = """<DescribeOptionGroupOptionsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeOptionGroupOptionsResult>
|
||||
<OptionGroupOptions>
|
||||
{%- for option_group_option in option_group_options -%}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{{ option_group_option.to_json() }}
|
||||
{{ option_group_option.to_xml() }}
|
||||
{%- endfor -%}
|
||||
]},
|
||||
"ResponseMetadata": {"RequestId": "457f7bb8-9fbf-11e4-9084-5754f80d5144"}
|
||||
}}"""
|
||||
</OptionGroupOptions>
|
||||
</DescribeOptionGroupOptionsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>457f7bb8-9fbf-11e4-9084-5754f80d5144</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeOptionGroupOptionsResponse>"""
|
||||
|
||||
MODIFY_OPTION_GROUP_TEMPLATE = \
|
||||
"""{"ModifyOptionGroupResponse": {
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "ce9284a5-a0de-11e4-b984-a11a53e1f328"
|
||||
},
|
||||
"ModifyOptionGroupResult":
|
||||
{{ option_group.to_json() }}
|
||||
}
|
||||
}"""
|
||||
MODIFY_OPTION_GROUP_TEMPLATE = """<ModifyOptionGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ModifyOptionGroupResult>
|
||||
{{ option_group.to_xml() }}
|
||||
</ModifyOptionGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>ce9284a5-a0de-11e4-b984-a11a53e1f328</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ModifyOptionGroupResponse>"""
|
||||
|
||||
LIST_TAGS_FOR_RESOURCE_TEMPLATE = \
|
||||
"""{"ListTagsForResourceResponse":
|
||||
{"ListTagsForResourceResult":
|
||||
{"TagList": [
|
||||
CREATE_DB_PARAMETER_GROUP_TEMPLATE = """<CreateDBParameterGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<CreateDBParameterGroupResult>
|
||||
{{ db_parameter_group.to_xml() }}
|
||||
</CreateDBParameterGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>7805c127-af22-11c3-96ac-6999cc5f7e72</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateDBParameterGroupResponse>"""
|
||||
|
||||
DESCRIBE_DB_PARAMETER_GROUPS_TEMPLATE = """<DescribeDBParameterGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeDBParameterGroupsResult>
|
||||
<DBParameterGroups>
|
||||
{%- for db_parameter_group in db_parameter_groups -%}
|
||||
{{ db_parameter_group.to_xml() }}
|
||||
{%- endfor -%}
|
||||
</DBParameterGroups>
|
||||
</DescribeDBParameterGroupsResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>b75d527a-b98c-11d3-f272-7cd6cce12cc5</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeDBParameterGroupsResponse>"""
|
||||
|
||||
MODIFY_DB_PARAMETER_GROUP_TEMPLATE = """<ModifyDBParameterGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ModifyDBParameterGroupResult>
|
||||
<DBParameterGroupName>{{ db_parameter_group.name }}</DBParameterGroupName>
|
||||
</ModifyDBParameterGroupResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>12d7435e-bba0-11d3-fe11-33d33a9bb7e3</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ModifyDBParameterGroupResponse>"""
|
||||
|
||||
DELETE_DB_PARAMETER_GROUP_TEMPLATE = """<DeleteDBParameterGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>cad6c267-ba25-11d3-fe11-33d33a9bb7e3</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DeleteDBParameterGroupResponse>"""
|
||||
|
||||
DESCRIBE_DB_PARAMETERS_TEMPLATE = """<DescribeDBParametersResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
|
||||
<DescribeDBParametersResult>
|
||||
<Parameters>
|
||||
{%- for db_parameter_name, db_parameter in db_parameter_group.parameters.items() -%}
|
||||
<Parameter>
|
||||
{%- for parameter_name, parameter_value in db_parameter.items() -%}
|
||||
<{{ parameter_name }}>{{ parameter_value }}</{{ parameter_name }}>
|
||||
{%- endfor -%}
|
||||
</Parameter>
|
||||
{%- endfor -%}
|
||||
</Parameters>
|
||||
</DescribeDBParametersResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8c40488f-b9ff-11d3-a15e-7ac49293f4fa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</DescribeDBParametersResponse>
|
||||
"""
|
||||
|
||||
LIST_TAGS_FOR_RESOURCE_TEMPLATE = """<ListTagsForResourceResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
|
||||
<ListTagsForResourceResult>
|
||||
<TagList>
|
||||
{%- for tag in tags -%}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{
|
||||
"Key": "{{ tag['Key'] }}",
|
||||
"Value": "{{ tag['Value'] }}"
|
||||
}
|
||||
<Tag>
|
||||
<Key>{{ tag['Key'] }}</Key>
|
||||
<Value>{{ tag['Value'] }}</Value>
|
||||
</Tag>
|
||||
{%- endfor -%}
|
||||
]},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "8c21ba39-a598-11e4-b688-194eaf8658fa"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
</TagList>
|
||||
</ListTagsForResourceResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>8c21ba39-a598-11e4-b688-194eaf8658fa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListTagsForResourceResponse>"""
|
||||
|
||||
ADD_TAGS_TO_RESOURCE_TEMPLATE = \
|
||||
"""{"ListTagsForResourceResponse": {
|
||||
"ListTagsForResourceResult": {
|
||||
"TagList": [
|
||||
{%- for tag in tags -%}
|
||||
{%- if loop.index != 1 -%},{%- endif -%}
|
||||
{
|
||||
"Key": "{{ tag['Key'] }}",
|
||||
"Value": "{{ tag['Value'] }}"
|
||||
}
|
||||
{%- endfor -%}
|
||||
]},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "b194d9ca-a664-11e4-b688-194eaf8658fa"
|
||||
}
|
||||
}
|
||||
}"""
|
||||
ADD_TAGS_TO_RESOURCE_TEMPLATE = """<AddTagsToResourceResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>b194d9ca-a664-11e4-b688-194eaf8658fa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AddTagsToResourceResponse>"""
|
||||
|
||||
REMOVE_TAGS_FROM_RESOURCE_TEMPLATE = \
|
||||
"""{"RemoveTagsFromResourceResponse": {"ResponseMetadata": {"RequestId": "c6499a01-a664-11e4-8069-fb454b71a80e"}}}
|
||||
"""
|
||||
REMOVE_TAGS_FROM_RESOURCE_TEMPLATE = """<RemoveTagsFromResourceResponse xmlns="http://rds.amazonaws.com/doc/2014-10-31/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>b194d9ca-a664-11e4-b688-194eaf8658fa</RequestId>
|
||||
</ResponseMetadata>
|
||||
</RemoveTagsFromResourceResponse>"""
|
||||
|
@ -0,0 +1,201 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
template = {
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
|
||||
"Description" : "AWS CloudFormation Sample Template RDS_MySQL_With_Read_Replica: Sample template showing how to create a highly-available, RDS DBInstance with a read replica. **WARNING** This template creates an Amazon Relational Database Service database instance and Amazon CloudWatch alarms. You will be billed for the AWS resources used if you create a stack from this template.",
|
||||
|
||||
"Parameters": {
|
||||
"DBName": {
|
||||
"Default": "MyDatabase",
|
||||
"Description" : "The database name",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "64",
|
||||
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
|
||||
},
|
||||
|
||||
"DBInstanceIdentifier": {
|
||||
"Type": "String"
|
||||
},
|
||||
|
||||
"DBUser": {
|
||||
"NoEcho": "true",
|
||||
"Description" : "The database admin account username",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "16",
|
||||
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
|
||||
"ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
|
||||
},
|
||||
|
||||
"DBPassword": {
|
||||
"NoEcho": "true",
|
||||
"Description" : "The database admin account password",
|
||||
"Type": "String",
|
||||
"MinLength": "1",
|
||||
"MaxLength": "41",
|
||||
"AllowedPattern" : "[a-zA-Z0-9]+",
|
||||
"ConstraintDescription" : "must contain only alphanumeric characters."
|
||||
},
|
||||
|
||||
"DBAllocatedStorage": {
|
||||
"Default": "5",
|
||||
"Description" : "The size of the database (Gb)",
|
||||
"Type": "Number",
|
||||
"MinValue": "5",
|
||||
"MaxValue": "1024",
|
||||
"ConstraintDescription" : "must be between 5 and 1024Gb."
|
||||
},
|
||||
|
||||
"DBInstanceClass": {
|
||||
"Description" : "The database instance type",
|
||||
"Type": "String",
|
||||
"Default": "db.m1.small",
|
||||
"AllowedValues" : [ "db.t1.micro", "db.m1.small", "db.m1.medium", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.m3.medium", "db.m3.large", "db.m3.xlarge", "db.m3.2xlarge", "db.r3.large", "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge", "db.r3.8xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.cr1.8xlarge"]
|
||||
,
|
||||
"ConstraintDescription" : "must select a valid database instance type."
|
||||
},
|
||||
|
||||
"EC2SecurityGroup": {
|
||||
"Description" : "The EC2 security group that contains instances that need access to the database",
|
||||
"Default": "default",
|
||||
"Type": "String",
|
||||
"AllowedPattern" : "[a-zA-Z0-9\\-]+",
|
||||
"ConstraintDescription" : "must be a valid security group name."
|
||||
},
|
||||
|
||||
"MultiAZ" : {
|
||||
"Description" : "Multi-AZ master database",
|
||||
"Type" : "String",
|
||||
"Default" : "false",
|
||||
"AllowedValues" : [ "true", "false" ],
|
||||
"ConstraintDescription" : "must be true or false."
|
||||
}
|
||||
},
|
||||
|
||||
"Conditions" : {
|
||||
"Is-EC2-VPC" : { "Fn::Or" : [ {"Fn::Equals" : [{"Ref" : "AWS::Region"}, "eu-central-1" ]},
|
||||
{"Fn::Equals" : [{"Ref" : "AWS::Region"}, "cn-north-1" ]}]},
|
||||
"Is-EC2-Classic" : { "Fn::Not" : [{ "Condition" : "Is-EC2-VPC"}]}
|
||||
},
|
||||
|
||||
"Resources" : {
|
||||
"DBParameterGroup": {
|
||||
"Type": "AWS::RDS::DBParameterGroup",
|
||||
"Properties" : {
|
||||
"Description": "DB Parameter Goup",
|
||||
"Family" : "MySQL5.1",
|
||||
"Parameters": {
|
||||
"BACKLOG_QUEUE_LIMIT": "2048"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"DBEC2SecurityGroup": {
|
||||
"Type": "AWS::EC2::SecurityGroup",
|
||||
"Condition" : "Is-EC2-VPC",
|
||||
"Properties" : {
|
||||
"GroupDescription": "Open database for access",
|
||||
"SecurityGroupIngress" : [{
|
||||
"IpProtocol" : "tcp",
|
||||
"FromPort" : "3306",
|
||||
"ToPort" : "3306",
|
||||
"SourceSecurityGroupName" : { "Ref" : "EC2SecurityGroup" }
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
"DBSecurityGroup": {
|
||||
"Type": "AWS::RDS::DBSecurityGroup",
|
||||
"Condition" : "Is-EC2-Classic",
|
||||
"Properties": {
|
||||
"DBSecurityGroupIngress": [{
|
||||
"EC2SecurityGroupName": { "Ref": "EC2SecurityGroup" }
|
||||
}],
|
||||
"GroupDescription": "database access"
|
||||
}
|
||||
},
|
||||
|
||||
"my_vpc": {
|
||||
"Type" : "AWS::EC2::VPC",
|
||||
"Properties" : {
|
||||
"CidrBlock" : "10.0.0.0/16",
|
||||
}
|
||||
},
|
||||
|
||||
"EC2Subnet": {
|
||||
"Type" : "AWS::EC2::Subnet",
|
||||
"Condition" : "Is-EC2-VPC",
|
||||
"Properties" : {
|
||||
"AvailabilityZone" : "eu-central-1a",
|
||||
"CidrBlock" : "10.0.1.0/24",
|
||||
"VpcId" : { "Ref" : "my_vpc" }
|
||||
}
|
||||
},
|
||||
|
||||
"DBSubnet": {
|
||||
"Type": "AWS::RDS::DBSubnetGroup",
|
||||
"Condition" : "Is-EC2-VPC",
|
||||
"Properties": {
|
||||
"DBSubnetGroupDescription": "my db subnet group",
|
||||
"SubnetIds" : [ { "Ref": "EC2Subnet" } ],
|
||||
}
|
||||
},
|
||||
|
||||
"MasterDB" : {
|
||||
"Type" : "AWS::RDS::DBInstance",
|
||||
"Properties" : {
|
||||
"DBInstanceIdentifier": { "Ref": "DBInstanceIdentifier" },
|
||||
"DBName" : { "Ref" : "DBName" },
|
||||
"AllocatedStorage" : { "Ref" : "DBAllocatedStorage" },
|
||||
"DBInstanceClass" : { "Ref" : "DBInstanceClass" },
|
||||
"Engine" : "MySQL",
|
||||
"DBSubnetGroupName": {"Fn::If": ["Is-EC2-VPC", { "Ref": "DBSubnet" }, { "Ref": "AWS::NoValue" }]},
|
||||
"MasterUsername" : { "Ref" : "DBUser" },
|
||||
"MasterUserPassword" : { "Ref" : "DBPassword" },
|
||||
"MultiAZ" : { "Ref" : "MultiAZ" },
|
||||
"Tags" : [{ "Key" : "Name", "Value" : "Master Database" }],
|
||||
"VPCSecurityGroups": { "Fn::If" : [ "Is-EC2-VPC", [ { "Fn::GetAtt": [ "DBEC2SecurityGroup", "GroupId" ] } ], { "Ref" : "AWS::NoValue"}]},
|
||||
"DBSecurityGroups": { "Fn::If" : [ "Is-EC2-Classic", [ { "Ref": "DBSecurityGroup" } ], { "Ref" : "AWS::NoValue"}]}
|
||||
},
|
||||
"DeletionPolicy" : "Snapshot"
|
||||
},
|
||||
|
||||
"ReplicaDB" : {
|
||||
"Type" : "AWS::RDS::DBInstance",
|
||||
"Properties" : {
|
||||
"SourceDBInstanceIdentifier" : { "Ref" : "MasterDB" },
|
||||
"DBInstanceClass" : { "Ref" : "DBInstanceClass" },
|
||||
"Tags" : [{ "Key" : "Name", "Value" : "Read Replica Database" }]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"Outputs" : {
|
||||
"EC2Platform" : {
|
||||
"Description" : "Platform in which this stack is deployed",
|
||||
"Value" : { "Fn::If" : [ "Is-EC2-VPC", "EC2-VPC", "EC2-Classic" ]}
|
||||
},
|
||||
|
||||
"MasterJDBCConnectionString": {
|
||||
"Description" : "JDBC connection string for the master database",
|
||||
"Value" : { "Fn::Join": [ "", [ "jdbc:mysql://",
|
||||
{ "Fn::GetAtt": [ "MasterDB", "Endpoint.Address" ] },
|
||||
":",
|
||||
{ "Fn::GetAtt": [ "MasterDB", "Endpoint.Port" ] },
|
||||
"/",
|
||||
{ "Ref": "DBName" }]]}
|
||||
},
|
||||
"ReplicaJDBCConnectionString": {
|
||||
"Description" : "JDBC connection string for the replica database",
|
||||
"Value" : { "Fn::Join": [ "", [ "jdbc:mysql://",
|
||||
{ "Fn::GetAtt": [ "ReplicaDB", "Endpoint.Address" ] },
|
||||
":",
|
||||
{ "Fn::GetAtt": [ "ReplicaDB", "Endpoint.Port" ] },
|
||||
"/",
|
||||
{ "Ref": "DBName" }]]}
|
||||
}
|
||||
}
|
||||
}
|
@ -82,7 +82,6 @@ template = {
|
||||
},
|
||||
|
||||
"Resources" : {
|
||||
|
||||
"DBEC2SecurityGroup": {
|
||||
"Type": "AWS::EC2::SecurityGroup",
|
||||
"Condition" : "Is-EC2-VPC",
|
||||
@ -101,9 +100,9 @@ template = {
|
||||
"Type": "AWS::RDS::DBSecurityGroup",
|
||||
"Condition" : "Is-EC2-Classic",
|
||||
"Properties": {
|
||||
"DBSecurityGroupIngress": {
|
||||
"DBSecurityGroupIngress": [{
|
||||
"EC2SecurityGroupName": { "Ref": "EC2SecurityGroup" }
|
||||
},
|
||||
}],
|
||||
"GroupDescription": "database access"
|
||||
}
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ from moto import (
|
||||
mock_kms,
|
||||
mock_lambda,
|
||||
mock_rds,
|
||||
mock_rds2,
|
||||
mock_redshift,
|
||||
mock_route53,
|
||||
mock_sns,
|
||||
@ -36,6 +37,7 @@ from moto import (
|
||||
from .fixtures import (
|
||||
ec2_classic_eip,
|
||||
fn_join,
|
||||
rds_mysql_with_db_parameter_group,
|
||||
rds_mysql_with_read_replica,
|
||||
redshift,
|
||||
route53_ec2_instance_with_public_ip,
|
||||
@ -693,6 +695,44 @@ def test_vpc_single_instance_in_subnet():
|
||||
eip_resource = [resource for resource in resources if resource.resource_type == 'AWS::EC2::EIP'][0]
|
||||
eip_resource.physical_resource_id.should.equal(eip.allocation_id)
|
||||
|
||||
@mock_cloudformation()
|
||||
@mock_ec2()
|
||||
@mock_rds2()
|
||||
def test_rds_db_parameter_groups():
|
||||
ec2_conn = boto.ec2.connect_to_region("us-west-1")
|
||||
ec2_conn.create_security_group('application', 'Our Application Group')
|
||||
|
||||
template_json = json.dumps(rds_mysql_with_db_parameter_group.template)
|
||||
conn = boto.cloudformation.connect_to_region("us-west-1")
|
||||
conn.create_stack(
|
||||
"test_stack",
|
||||
template_body=template_json,
|
||||
parameters=[
|
||||
("DBInstanceIdentifier", "master_db"),
|
||||
("DBName", "my_db"),
|
||||
("DBUser", "my_user"),
|
||||
("DBPassword", "my_password"),
|
||||
("DBAllocatedStorage", "20"),
|
||||
("DBInstanceClass", "db.m1.medium"),
|
||||
("EC2SecurityGroup", "application"),
|
||||
("MultiAZ", "true"),
|
||||
],
|
||||
)
|
||||
|
||||
rds_conn = boto3.client('rds', region_name="us-west-1")
|
||||
|
||||
db_parameter_groups = rds_conn.describe_db_parameter_groups()
|
||||
len(db_parameter_groups['DBParameterGroups']).should.equal(1)
|
||||
db_parameter_group_name = db_parameter_groups['DBParameterGroups'][0]['DBParameterGroupName']
|
||||
|
||||
found_cloudformation_set_parameter = False
|
||||
for db_parameter in rds_conn.describe_db_parameters(DBParameterGroupName=db_parameter_group_name)['Parameters']:
|
||||
if db_parameter['ParameterName'] == 'BACKLOG_QUEUE_LIMIT' and db_parameter['ParameterValue'] == '2048':
|
||||
found_cloudformation_set_parameter = True
|
||||
|
||||
found_cloudformation_set_parameter.should.equal(True)
|
||||
|
||||
|
||||
|
||||
@mock_cloudformation()
|
||||
@mock_ec2()
|
||||
|
@ -238,6 +238,32 @@ def test_create_database_replica():
|
||||
primary = conn.get_all_dbinstances("db-master-1")[0]
|
||||
list(primary.read_replica_dbinstance_identifiers).should.have.length_of(0)
|
||||
|
||||
@disable_on_py3()
|
||||
@mock_rds
|
||||
def test_create_cross_region_database_replica():
|
||||
west_1_conn = boto.rds.connect_to_region("us-west-1")
|
||||
west_2_conn = boto.rds.connect_to_region("us-west-2")
|
||||
|
||||
primary = west_1_conn.create_dbinstance("db-master-1", 10, 'db.m1.small', 'root', 'hunter2')
|
||||
|
||||
primary_arn = "arn:aws:rds:us-west-1:1234567890:db:db-master-1"
|
||||
replica = west_2_conn.create_dbinstance_read_replica(
|
||||
"replica",
|
||||
primary_arn,
|
||||
"db.m1.small",
|
||||
)
|
||||
|
||||
primary = west_1_conn.get_all_dbinstances("db-master-1")[0]
|
||||
primary.read_replica_dbinstance_identifiers[0].should.equal("replica")
|
||||
|
||||
replica = west_2_conn.get_all_dbinstances("replica")[0]
|
||||
replica.instance_class.should.equal("db.m1.small")
|
||||
|
||||
west_2_conn.delete_dbinstance("replica")
|
||||
|
||||
primary = west_1_conn.get_all_dbinstances("db-master-1")[0]
|
||||
list(primary.read_replica_dbinstance_identifiers).should.have.length_of(0)
|
||||
|
||||
|
||||
@disable_on_py3()
|
||||
@mock_rds
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user