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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user