Fix merge conflicts and add EC2 Instance delete. Closes #576.

This commit is contained in:
Steve Pulec 2016-04-28 09:21:54 -04:00
commit a600deb96a
12 changed files with 461 additions and 50 deletions

View file

@ -14,7 +14,7 @@ class ValidationError(BadRequest):
super(ValidationError, self).__init__()
self.description = template.render(
code="ValidationError",
messgae="Stack:{0} does not exist".format(name_or_id),
message="Stack:{0} does not exist".format(name_or_id),
)
@ -24,7 +24,7 @@ class MissingParameterError(BadRequest):
super(MissingParameterError, self).__init__()
self.description = template.render(
code="Missing Parameter",
messgae="Missing parameter {0}".format(parameter_name),
message="Missing parameter {0}".format(parameter_name),
)

View file

@ -51,11 +51,11 @@ class FakeStack(object):
self.template = template
self.resource_map.update(json.loads(template))
self.output_map = self._create_output_map()
self.status = 'UPDATE_COMPLETE'
self.status = "UPDATE_COMPLETE"
def delete(self):
self.resource_map.delete()
self.status = 'DELETE_COMPLETE'
self.status = "DELETE_COMPLETE"
class CloudFormationBackend(BaseBackend):

View file

@ -115,7 +115,7 @@ def clean_json(resource_json, resources_map):
return result
if 'Fn::GetAtt' in resource_json:
resource = resources_map[resource_json['Fn::GetAtt'][0]]
resource = resources_map.get(resource_json['Fn::GetAtt'][0])
if resource is None:
return resource_json
try:
@ -208,15 +208,22 @@ def parse_and_create_resource(logical_id, resource_json, resources_map, region_n
def parse_and_update_resource(logical_id, resource_json, resources_map, region_name):
resource_class, resource_json, resource_name = parse_resource(logical_id, resource_json, resources_map)
resource = resource_class.update_from_cloudformation_json(resource_name, resource_json, region_name)
return resource
resource_class, new_resource_json, new_resource_name = parse_resource(logical_id, resource_json, resources_map)
original_resource = resources_map[logical_id]
new_resource = resource_class.update_from_cloudformation_json(
original_resource=original_resource,
new_resource_name=new_resource_name,
cloudformation_json=new_resource_json,
region_name=region_name
)
new_resource.type = resource_json['Type']
new_resource.logical_resource_id = logical_id
return new_resource
def parse_and_delete_resource(logical_id, resource_json, resources_map, region_name):
resource_class, resource_json, resource_name = parse_resource(logical_id, resource_json, resources_map)
resource_class.delete_from_cloudformation_json(resource_name, resource_json, region_name)
return None
def parse_condition(condition, resources_map, condition_map):
@ -289,6 +296,8 @@ class ResourceMap(collections.Mapping):
return self._parsed_resources[resource_logical_id]
else:
resource_json = self._resource_json_map.get(resource_logical_id)
if not resource_json:
raise KeyError(resource_logical_id)
new_resource = parse_and_create_resource(resource_logical_id, resource_json, self, self._region_name)
self._parsed_resources[resource_logical_id] = new_resource
return new_resource
@ -369,18 +378,40 @@ class ResourceMap(collections.Mapping):
parse_and_delete_resource(resource_name, resource_json, self, self._region_name)
self._parsed_resources.pop(resource_name)
for resource_name in new_template:
if resource_name in old_template and new_template[resource_name] != old_template[resource_name]:
resources_to_update = set(name for name in new_template if name in old_template and new_template[name] != old_template[name])
tries = 1
while resources_to_update and tries < 5:
for resource_name in resources_to_update.copy():
resource_json = new_template[resource_name]
changed_resource = parse_and_update_resource(resource_name, resource_json, self, self._region_name)
self._parsed_resources[resource_name] = changed_resource
try:
changed_resource = parse_and_update_resource(resource_name, resource_json, self, self._region_name)
except Exception as e:
# skip over dependency violations, and try again in a second pass
last_exception = e
else:
self._parsed_resources[resource_name] = changed_resource
resources_to_update.remove(resource_name)
tries += 1
if tries == 5:
raise last_exception
def delete(self):
for resource in self.resources:
parsed_resource = self._parsed_resources.pop(resource)
if parsed_resource and hasattr(parsed_resource, 'delete'):
parsed_resource.delete(self._region_name)
remaining_resources = set(self.resources)
tries = 1
while remaining_resources and tries < 5:
for resource in remaining_resources.copy():
parsed_resource = self._parsed_resources.get(resource)
if parsed_resource:
try:
parsed_resource.delete(self._region_name)
except Exception as e:
# skip over dependency violations, and try again in a second pass
last_exception = e
else:
remaining_resources.remove(resource)
tries += 1
if tries == 5:
raise last_exception
class OutputMap(collections.Mapping):

View file

@ -79,8 +79,8 @@ class CloudFormationResponse(BaseResponse):
resource = stack_resource
break
else:
raise ValidationError(logical_resource_id)
raise ValidationError(logical_resource_id)
template = self.response_template(DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE)
return template.render(stack=stack, resource=resource)
@ -106,7 +106,7 @@ class CloudFormationResponse(BaseResponse):
def get_template(self):
name_or_stack_id = self.querystring.get('StackName')[0]
stack = self.cloudformation_backend.get_stack(name_or_stack_id)
if self.request_json:
return json.dumps({
"GetTemplateResponse": {
@ -124,21 +124,24 @@ class CloudFormationResponse(BaseResponse):
def update_stack(self):
stack_name = self._get_param('StackName')
stack_body = self._get_param('TemplateBody')
if self._get_param('UsePreviousTemplate') == "true":
stack_body = self.cloudformation_backend.get_stack(stack_name).template
else:
stack_body = self._get_param('TemplateBody')
stack = self.cloudformation_backend.update_stack(
name=stack_name,
template=stack_body,
)
if self.request_json:
return json.dumps({
stack_body = {
'UpdateStackResponse': {
'UpdateStackResult': {
'StackId': stack.name,
}
}
})
}
return json.dumps(stack_body)
else:
template = self.response_template(UPDATE_STACK_RESPONSE_TEMPLATE)
return template.render(stack=stack)
@ -310,6 +313,16 @@ GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
</GetTemplateResponse>"""
UPDATE_STACK_RESPONSE_TEMPLATE = """<UpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
<UpdateStackResult>
<StackId>{{ stack.stack_id }}</StackId>
</UpdateStackResult>
<ResponseMetadata>
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
</ResponseMetadata>
</UpdateStackResponse>
"""
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
<ResponseMetadata>
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>