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/*
|
dist/*
|
||||||
.tox
|
.tox
|
||||||
.coverage
|
.coverage
|
||||||
|
cover/
|
||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
.noseids
|
.noseids
|
||||||
build/
|
build/
|
||||||
.idea/
|
.idea/
|
||||||
*.swp
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
3
Makefile
3
Makefile
@ -6,7 +6,8 @@ init:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
rm -f .coverage
|
rm -f .coverage
|
||||||
@nosetests -sv --with-coverage ./tests/
|
rm -rf cover
|
||||||
|
@nosetests -sv --with-coverage --cover-html ./tests/
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
python setup.py sdist bdist_wheel upload
|
python setup.py sdist bdist_wheel upload
|
||||||
|
@ -379,7 +379,8 @@ class ResourceMap(collections.Mapping):
|
|||||||
def delete(self):
|
def delete(self):
|
||||||
for resource in self.resources:
|
for resource in self.resources:
|
||||||
parsed_resource = self._parsed_resources.pop(resource)
|
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):
|
class OutputMap(collections.Mapping):
|
||||||
|
@ -230,21 +230,23 @@ DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
|||||||
</DescribeStackResourceResponse>"""
|
</DescribeStackResourceResponse>"""
|
||||||
|
|
||||||
|
|
||||||
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResult>
|
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
||||||
<StackResources>
|
<DescribeStackResourcesResult>
|
||||||
{% for resource in stack.stack_resources %}
|
<StackResources>
|
||||||
<member>
|
{% for resource in stack.stack_resources %}
|
||||||
<StackId>{{ stack.stack_id }}</StackId>
|
<member>
|
||||||
<StackName>{{ stack.name }}</StackName>
|
<StackId>{{ stack.stack_id }}</StackId>
|
||||||
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
<StackName>{{ stack.name }}</StackName>
|
||||||
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
||||||
<ResourceType>{{ resource.type }}</ResourceType>
|
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
||||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
<ResourceType>{{ resource.type }}</ResourceType>
|
||||||
<ResourceStatus>{{ stack.status }}</ResourceStatus>
|
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||||
</member>
|
<ResourceStatus>{{ stack.status }}</ResourceStatus>
|
||||||
{% endfor %}
|
</member>
|
||||||
</StackResources>
|
{% endfor %}
|
||||||
</DescribeStackResourcesResult>"""
|
</StackResources>
|
||||||
|
</DescribeStackResourcesResult>
|
||||||
|
</DescribeStackResourcesResponse>"""
|
||||||
|
|
||||||
|
|
||||||
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
||||||
|
@ -16,7 +16,9 @@ def parse_key_name(path):
|
|||||||
|
|
||||||
|
|
||||||
def is_delete_keys(request, path, bucket_name):
|
def is_delete_keys(request, path, bucket_name):
|
||||||
return path == u'/' + bucket_name + u'/?delete' or (
|
return (
|
||||||
path == u'/' + bucket_name and
|
path == u'/' + bucket_name + u'/?delete' or
|
||||||
getattr(request, "query_string", "") == "delete"
|
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 = {
|
dummy_template = {
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
"AWSTemplateFormatVersion": "2010-09-09",
|
||||||
"Description": "Stack 1",
|
"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)
|
dummy_template_json = json.dumps(dummy_template)
|
||||||
|
|
||||||
|
|
||||||
@mock_cloudformation
|
@mock_cloudformation
|
||||||
def test_boto3_create_stack():
|
def test_boto3_create_stack():
|
||||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
||||||
@ -29,7 +49,8 @@ def test_boto3_create_stack():
|
|||||||
TemplateBody=dummy_template_json,
|
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
|
@mock_cloudformation
|
||||||
@ -59,7 +80,8 @@ def test_create_stack_with_notification_arn():
|
|||||||
)
|
)
|
||||||
|
|
||||||
stack = list(cf.stacks.all())[0]
|
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
|
@mock_cloudformation
|
||||||
@ -78,9 +100,27 @@ def test_create_stack_from_s3_url():
|
|||||||
TemplateURL=key_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
|
@mock_cloudformation
|
||||||
def test_describe_stack_by_name():
|
def test_describe_stack_by_name():
|
||||||
cf_conn = boto3.client('cloudformation', region_name='us-east-1')
|
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 = 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['StackId'].should.equal(stack['StackId'])
|
||||||
stack_by_id['StackName'].should.equal("test_stack")
|
stack_by_id['StackName'].should.equal("test_stack")
|
||||||
@ -198,9 +239,9 @@ def test_cloudformation_params():
|
|||||||
StackName='test_stack',
|
StackName='test_stack',
|
||||||
TemplateBody=dummy_template_with_params_json,
|
TemplateBody=dummy_template_with_params_json,
|
||||||
Parameters=[{
|
Parameters=[{
|
||||||
"ParameterKey": "APPNAME",
|
"ParameterKey": "APPNAME",
|
||||||
"ParameterValue": "testing123",
|
"ParameterValue": "testing123",
|
||||||
}],
|
}],
|
||||||
)
|
)
|
||||||
|
|
||||||
stack.parameters.should.have.length_of(1)
|
stack.parameters.should.have.length_of(1)
|
||||||
@ -227,6 +268,8 @@ def test_stack_tags():
|
|||||||
TemplateBody=dummy_template_json,
|
TemplateBody=dummy_template_json,
|
||||||
Tags=tags,
|
Tags=tags,
|
||||||
)
|
)
|
||||||
observed_tag_items = set(item for items in [tag.items() for tag in stack.tags] for item in items)
|
observed_tag_items = set(
|
||||||
expected_tag_items = set(item for items in [tag.items() for tag in tags] for item in items)
|
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)
|
observed_tag_items.should.equal(expected_tag_items)
|
||||||
|
Loading…
Reference in New Issue
Block a user