Fix merge conflicts and add EC2 Instance delete. Closes #576.
This commit is contained in:
parent
0b24c6be57
commit
a600deb96a
12 changed files with 461 additions and 50 deletions
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue