Fix multiple bugs encountered with boto3
* Fix path detection for deleting keys in S3 bucket * Fix stack deletion ensure delete method exists on object * Previous tests were using a stack with no resources * Fix DESCRIBE_STACK_RESOURCES_RESPONSE, * Previously untested code path
This commit is contained in:
parent
1ece813131
commit
b152c00642
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,9 +2,11 @@ moto.egg-info/*
|
||||
dist/*
|
||||
.tox
|
||||
.coverage
|
||||
cover/
|
||||
*.pyc
|
||||
*~
|
||||
.noseids
|
||||
build/
|
||||
.idea/
|
||||
*.swp
|
||||
.DS_Store
|
||||
|
3
Makefile
3
Makefile
@ -6,7 +6,8 @@ init:
|
||||
|
||||
test:
|
||||
rm -f .coverage
|
||||
@nosetests -sv --with-coverage ./tests/
|
||||
rm -rf cover
|
||||
@nosetests -sv --with-coverage --cover-html ./tests/
|
||||
|
||||
publish:
|
||||
python setup.py sdist bdist_wheel upload
|
||||
|
@ -379,7 +379,8 @@ class ResourceMap(collections.Mapping):
|
||||
def delete(self):
|
||||
for resource in self.resources:
|
||||
parsed_resource = self._parsed_resources.pop(resource)
|
||||
parsed_resource.delete(self._region_name)
|
||||
if parsed_resource and hasattr(parsed_resource, 'delete'):
|
||||
parsed_resource.delete(self._region_name)
|
||||
|
||||
|
||||
class OutputMap(collections.Mapping):
|
||||
|
@ -230,21 +230,23 @@ DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
||||
</DescribeStackResourceResponse>"""
|
||||
|
||||
|
||||
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResult>
|
||||
<StackResources>
|
||||
{% for resource in stack.stack_resources %}
|
||||
<member>
|
||||
<StackId>{{ stack.stack_id }}</StackId>
|
||||
<StackName>{{ stack.name }}</StackName>
|
||||
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
||||
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
||||
<ResourceType>{{ resource.type }}</ResourceType>
|
||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||
<ResourceStatus>{{ stack.status }}</ResourceStatus>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</StackResources>
|
||||
</DescribeStackResourcesResult>"""
|
||||
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
||||
<DescribeStackResourcesResult>
|
||||
<StackResources>
|
||||
{% for resource in stack.stack_resources %}
|
||||
<member>
|
||||
<StackId>{{ stack.stack_id }}</StackId>
|
||||
<StackName>{{ stack.name }}</StackName>
|
||||
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
||||
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
||||
<ResourceType>{{ resource.type }}</ResourceType>
|
||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||
<ResourceStatus>{{ stack.status }}</ResourceStatus>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</StackResources>
|
||||
</DescribeStackResourcesResult>
|
||||
</DescribeStackResourcesResponse>"""
|
||||
|
||||
|
||||
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
||||
|
@ -16,7 +16,9 @@ def parse_key_name(path):
|
||||
|
||||
|
||||
def is_delete_keys(request, path, bucket_name):
|
||||
return path == u'/' + bucket_name + u'/?delete' or (
|
||||
path == u'/' + bucket_name and
|
||||
getattr(request, "query_string", "") == "delete"
|
||||
return (
|
||||
path == u'/' + bucket_name + u'/?delete' or
|
||||
path == u'/' + bucket_name + u'?delete' or
|
||||
(path == u'/' + bucket_name and
|
||||
getattr(request, "query_string", "") == "delete")
|
||||
)
|
||||
|
@ -16,11 +16,31 @@ from nose.tools import assert_raises
|
||||
dummy_template = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "Stack 1",
|
||||
"Resources": {},
|
||||
"Resources": {
|
||||
"EC2Instance1": {
|
||||
"Type": "AWS::EC2::Instance",
|
||||
"Properties": {
|
||||
"ImageId": "ami-d3adb33f",
|
||||
"KeyName": "dummy",
|
||||
"InstanceType": "t2.micro",
|
||||
"Tags": [
|
||||
{
|
||||
"Key": "Description",
|
||||
"Value": "Test tag"
|
||||
},
|
||||
{
|
||||
"Key": "Name",
|
||||
"Value": "Name tag for tests"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dummy_template_json = json.dumps(dummy_template)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_boto3_create_stack():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
@ -29,7 +49,8 @@ def test_boto3_create_stack():
|
||||
TemplateBody=dummy_template_json,
|
||||
)
|
||||
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(dummy_template)
|
||||
cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(
|
||||
dummy_template)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@ -59,7 +80,8 @@ def test_create_stack_with_notification_arn():
|
||||
)
|
||||
|
||||
stack = list(cf.stacks.all())[0]
|
||||
stack.notification_arns.should.contain('arn:aws:sns:us-east-1:123456789012:fake-queue')
|
||||
stack.notification_arns.should.contain(
|
||||
'arn:aws:sns:us-east-1:123456789012:fake-queue')
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@ -78,9 +100,27 @@ def test_create_stack_from_s3_url():
|
||||
TemplateURL=key_url,
|
||||
)
|
||||
|
||||
cf_conn.get_template(StackName="stack_from_url")['TemplateBody'].should.equal(dummy_template)
|
||||
cf_conn.get_template(StackName="stack_from_url")[
|
||||
'TemplateBody'].should.equal(dummy_template)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_describe_stack_resources():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
cf_conn.create_stack(
|
||||
StackName="test_stack",
|
||||
TemplateBody=dummy_template_json,
|
||||
)
|
||||
|
||||
stack = cf_conn.describe_stacks(StackName="test_stack")['Stacks'][0]
|
||||
|
||||
response = cf_conn.describe_stack_resources(StackName=stack['StackName'])
|
||||
resource = response['StackResources'][0]
|
||||
resource['LogicalResourceId'].should.equal('EC2Instance1')
|
||||
resource['ResourceStatus'].should.equal('CREATE_COMPLETE')
|
||||
resource['ResourceType'].should.equal('AWS::EC2::Instance')
|
||||
resource['StackId'].should.equal(stack['StackId'])
|
||||
|
||||
@mock_cloudformation
|
||||
def test_describe_stack_by_name():
|
||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||
@ -102,7 +142,8 @@ def test_describe_stack_by_stack_id():
|
||||
)
|
||||
|
||||
stack = cf_conn.describe_stacks(StackName="test_stack")['Stacks'][0]
|
||||
stack_by_id = cf_conn.describe_stacks(StackName=stack['StackId'])['Stacks'][0]
|
||||
stack_by_id = cf_conn.describe_stacks(StackName=stack['StackId'])['Stacks'][
|
||||
0]
|
||||
|
||||
stack_by_id['StackId'].should.equal(stack['StackId'])
|
||||
stack_by_id['StackName'].should.equal("test_stack")
|
||||
@ -198,9 +239,9 @@ def test_cloudformation_params():
|
||||
StackName='test_stack',
|
||||
TemplateBody=dummy_template_with_params_json,
|
||||
Parameters=[{
|
||||
"ParameterKey": "APPNAME",
|
||||
"ParameterValue": "testing123",
|
||||
}],
|
||||
"ParameterKey": "APPNAME",
|
||||
"ParameterValue": "testing123",
|
||||
}],
|
||||
)
|
||||
|
||||
stack.parameters.should.have.length_of(1)
|
||||
@ -227,6 +268,8 @@ def test_stack_tags():
|
||||
TemplateBody=dummy_template_json,
|
||||
Tags=tags,
|
||||
)
|
||||
observed_tag_items = set(item for items in [tag.items() for tag in stack.tags] for item in items)
|
||||
expected_tag_items = set(item for items in [tag.items() for tag in tags] for item in items)
|
||||
observed_tag_items = set(
|
||||
item for items in [tag.items() for tag in stack.tags] for item in items)
|
||||
expected_tag_items = set(
|
||||
item for items in [tag.items() for tag in tags] for item in items)
|
||||
observed_tag_items.should.equal(expected_tag_items)
|
||||
|
Loading…
Reference in New Issue
Block a user