Merged upstream master
This commit is contained in:
commit
e64d1c1790
14 changed files with 167 additions and 29 deletions
|
|
@ -275,7 +275,7 @@ GET_METRIC_STATISTICS_TEMPLATE = """<GetMetricStatisticsResponse xmlns="http://m
|
|||
<Label>{{ label }}</Label>
|
||||
<Datapoints>
|
||||
{% for datapoint in datapoints %}
|
||||
<Datapoint>
|
||||
<member>
|
||||
{% if datapoint.sum is not none %}
|
||||
<Sum>{{ datapoint.sum }}</Sum>
|
||||
{% endif %}
|
||||
|
|
@ -302,7 +302,7 @@ GET_METRIC_STATISTICS_TEMPLATE = """<GetMetricStatisticsResponse xmlns="http://m
|
|||
|
||||
<Timestamp>{{ datapoint.timestamp }}</Timestamp>
|
||||
<Unit>{{ datapoint.unit }}</Unit>
|
||||
</Datapoint>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Datapoints>
|
||||
</GetMetricStatisticsResult>
|
||||
|
|
|
|||
|
|
@ -685,6 +685,7 @@ class IAMBackend(BaseBackend):
|
|||
for p, d in role.policies.items():
|
||||
if p == policy_name:
|
||||
return p, d
|
||||
raise IAMNotFoundException("Policy Document {0} not attached to role {1}".format(policy_name, role_name))
|
||||
|
||||
def list_role_policies(self, role_name):
|
||||
role = self.get_role(role_name)
|
||||
|
|
@ -777,7 +778,6 @@ class IAMBackend(BaseBackend):
|
|||
policy = self.get_policy(policy_arn)
|
||||
if not policy:
|
||||
raise IAMNotFoundException("Policy not found")
|
||||
|
||||
if len(policy.versions) >= 5:
|
||||
raise IAMLimitExceededException("A managed policy can have up to 5 versions. Before you create a new version, you must delete an existing version.")
|
||||
set_as_default = (set_as_default == "true") # convert it to python bool
|
||||
|
|
|
|||
|
|
@ -1349,6 +1349,7 @@ LIST_GROUPS_FOR_USER_TEMPLATE = """<ListGroupsForUserResponse>
|
|||
<GroupName>{{ group.name }}</GroupName>
|
||||
<GroupId>{{ group.id }}</GroupId>
|
||||
<Arn>{{ group.arn }}</Arn>
|
||||
<CreateDate>{{ group.created_iso_8601 }}</CreateDate>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Groups>
|
||||
|
|
@ -1652,6 +1653,7 @@ LIST_GROUPS_FOR_USER_TEMPLATE = """<ListGroupsForUserResponse>
|
|||
<GroupName>{{ group.name }}</GroupName>
|
||||
<GroupId>{{ group.id }}</GroupId>
|
||||
<Arn>{{ group.arn }}</Arn>
|
||||
<CreateDate>{{ group.created_iso_8601 }}</CreateDate>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Groups>
|
||||
|
|
|
|||
|
|
@ -268,10 +268,26 @@ class fakesock(object):
|
|||
_sent_data = []
|
||||
|
||||
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
|
||||
protocol=0):
|
||||
self.truesock = (old_socket(family, type, protocol)
|
||||
if httpretty.allow_net_connect
|
||||
else None)
|
||||
proto=0, fileno=None, _sock=None):
|
||||
"""
|
||||
Matches both the Python 2 API:
|
||||
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
|
||||
https://github.com/python/cpython/blob/2.7/Lib/socket.py
|
||||
|
||||
and the Python 3 API:
|
||||
def __init__(self, family=-1, type=-1, proto=-1, fileno=None):
|
||||
https://github.com/python/cpython/blob/3.5/Lib/socket.py
|
||||
"""
|
||||
if httpretty.allow_net_connect:
|
||||
if PY3:
|
||||
self.truesock = old_socket(family, type, proto, fileno)
|
||||
else:
|
||||
# If Python 2, if parameters are passed as arguments, instead of kwargs,
|
||||
# the 4th argument `_sock` will be interpreted as the `fileno`.
|
||||
# Check if _sock is none, and if so, pass fileno.
|
||||
self.truesock = old_socket(family, type, proto, fileno or _sock)
|
||||
else:
|
||||
self.truesock = None
|
||||
self._closed = True
|
||||
self.fd = FakeSockFile()
|
||||
self.fd.socket = self
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class RecordSet(BaseModel):
|
|||
properties["HostedZoneId"])
|
||||
|
||||
try:
|
||||
hosted_zone.delete_rrset_by_name(resource_name)
|
||||
hosted_zone.delete_rrset({'Name': resource_name})
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ class RecordSet(BaseModel):
|
|||
self.hosted_zone_name)
|
||||
if not hosted_zone:
|
||||
hosted_zone = route53_backend.get_hosted_zone(self.hosted_zone_id)
|
||||
hosted_zone.delete_rrset_by_name(self.name)
|
||||
hosted_zone.delete_rrset({'Name': self.name, 'Type': self.type_})
|
||||
|
||||
|
||||
def reverse_domain_name(domain_name):
|
||||
|
|
@ -196,9 +196,13 @@ class FakeZone(BaseModel):
|
|||
self.rrsets.append(new_rrset)
|
||||
return new_rrset
|
||||
|
||||
def delete_rrset_by_name(self, name):
|
||||
def delete_rrset(self, rrset):
|
||||
self.rrsets = [
|
||||
record_set for record_set in self.rrsets if record_set.name != name]
|
||||
record_set
|
||||
for record_set in self.rrsets
|
||||
if record_set.name != rrset['Name'] or
|
||||
(rrset.get('Type') is not None and record_set.type_ != rrset['Type'])
|
||||
]
|
||||
|
||||
def delete_rrset_by_id(self, set_identifier):
|
||||
self.rrsets = [
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class Route53(BaseResponse):
|
|||
the_zone.delete_rrset_by_id(
|
||||
record_set["SetIdentifier"])
|
||||
else:
|
||||
the_zone.delete_rrset_by_name(record_set["Name"])
|
||||
the_zone.delete_rrset(record_set)
|
||||
|
||||
return 200, headers, CHANGE_RRSET_RESPONSE
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from boto3 import Session
|
|||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds, camelcase_to_underscores
|
||||
from moto.sqs import sqs_backends
|
||||
from moto.awslambda import lambda_backends
|
||||
|
||||
|
|
@ -243,11 +243,14 @@ class SNSBackend(BaseBackend):
|
|||
def update_sms_attributes(self, attrs):
|
||||
self.sms_attributes.update(attrs)
|
||||
|
||||
def create_topic(self, name):
|
||||
def create_topic(self, name, attributes=None):
|
||||
fails_constraints = not re.match(r'^[a-zA-Z0-9_-]{1,256}$', name)
|
||||
if fails_constraints:
|
||||
raise InvalidParameterValue("Topic names must be made up of only uppercase and lowercase ASCII letters, numbers, underscores, and hyphens, and must be between 1 and 256 characters long.")
|
||||
candidate_topic = Topic(name, self)
|
||||
if attributes:
|
||||
for attribute in attributes:
|
||||
setattr(candidate_topic, camelcase_to_underscores(attribute), attributes[attribute])
|
||||
if candidate_topic.arn in self.topics:
|
||||
return self.topics[candidate_topic.arn]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ class SNSResponse(BaseResponse):
|
|||
|
||||
def create_topic(self):
|
||||
name = self._get_param('Name')
|
||||
topic = self.backend.create_topic(name)
|
||||
attributes = self._get_attributes()
|
||||
topic = self.backend.create_topic(name, attributes)
|
||||
|
||||
if self.request_json:
|
||||
return json.dumps({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue