Merge pull request #550 from 2rs2ts/boto3-cloudformation
Add XML support for cloudformation commands that lacked it
This commit is contained in:
		
						commit
						9b0be24b28
					
				@ -80,6 +80,10 @@ class FakeLaunchConfiguration(object):
 | 
			
		||||
        )
 | 
			
		||||
        return config
 | 
			
		||||
 | 
			
		||||
    def delete(self, region_name):
 | 
			
		||||
        backend = autoscaling_backends[region_name]
 | 
			
		||||
        backend.delete_launch_configuration(self.name)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def physical_resource_id(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
@ -156,7 +160,7 @@ class FakeAutoScalingGroup(object):
 | 
			
		||||
            max_size=properties.get("MaxSize"),
 | 
			
		||||
            min_size=properties.get("MinSize"),
 | 
			
		||||
            launch_config_name=launch_config_name,
 | 
			
		||||
            vpc_zone_identifier=properties.get("VPCZoneIdentifier"),
 | 
			
		||||
            vpc_zone_identifier=(','.join(properties.get("VPCZoneIdentifier", [])) or None),
 | 
			
		||||
            default_cooldown=properties.get("Cooldown"),
 | 
			
		||||
            health_check_period=properties.get("HealthCheckGracePeriod"),
 | 
			
		||||
            health_check_type=properties.get("HealthCheckType"),
 | 
			
		||||
@ -167,6 +171,10 @@ class FakeAutoScalingGroup(object):
 | 
			
		||||
        )
 | 
			
		||||
        return group
 | 
			
		||||
 | 
			
		||||
    def delete(self, region_name):
 | 
			
		||||
        backend = autoscaling_backends[region_name]
 | 
			
		||||
        backend.delete_autoscaling_group(self.name)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def physical_resource_id(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
@ -241,11 +241,11 @@ DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE = """<DescribeAutoScalingGroupsResponse xml
 | 
			
		||||
        <Tags>
 | 
			
		||||
          {% for tag in group.tags %}
 | 
			
		||||
          <member>
 | 
			
		||||
            <ResourceType>{{ tag.resource_type }}</ResourceType>
 | 
			
		||||
            <ResourceId>{{ tag.resource_id }}</ResourceId>
 | 
			
		||||
            <PropagateAtLaunch>{{ tag.propagate_at_launch }}</PropagateAtLaunch>
 | 
			
		||||
            <Key>{{ tag.key }}</Key>
 | 
			
		||||
            <Value>{{ tag.value }}</Value>
 | 
			
		||||
            <ResourceType>{{ tag.resource_type or tag.ResourceType }}</ResourceType>
 | 
			
		||||
            <ResourceId>{{ tag.resource_id or tag.ResourceId }}</ResourceId>
 | 
			
		||||
            <PropagateAtLaunch>{{ tag.propagate_at_launch or tag.PropagateAtLaunch }}</PropagateAtLaunch>
 | 
			
		||||
            <Key>{{ tag.key or tag.Key }}</Key>
 | 
			
		||||
            <Value>{{ tag.value or tag.Value }}</Value>
 | 
			
		||||
          </member>
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </Tags>
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,10 @@ class FakeStack(object):
 | 
			
		||||
        self.resource_map.update(json.loads(template))
 | 
			
		||||
        self.output_map = self._create_output_map()
 | 
			
		||||
 | 
			
		||||
    def delete(self):
 | 
			
		||||
        self.resource_map.delete()
 | 
			
		||||
        self.status = 'DELETE_COMPLETE'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CloudFormationBackend(BaseBackend):
 | 
			
		||||
 | 
			
		||||
@ -112,13 +116,14 @@ class CloudFormationBackend(BaseBackend):
 | 
			
		||||
        if name_or_stack_id in self.stacks:
 | 
			
		||||
            # Delete by stack id
 | 
			
		||||
            stack = self.stacks.pop(name_or_stack_id, None)
 | 
			
		||||
            stack.status = 'DELETE_COMPLETE'
 | 
			
		||||
            stack.delete()
 | 
			
		||||
            self.deleted_stacks[stack.stack_id] = stack
 | 
			
		||||
            return self.stacks.pop(name_or_stack_id, None)
 | 
			
		||||
        else:
 | 
			
		||||
            # Delete by stack name
 | 
			
		||||
            stack_to_delete = [stack for stack in self.stacks.values() if stack.name == name_or_stack_id][0]
 | 
			
		||||
            self.delete_stack(stack_to_delete.stack_id)
 | 
			
		||||
            for stack in list(self.stacks.values()):
 | 
			
		||||
                if stack.name == name_or_stack_id:
 | 
			
		||||
                    self.delete_stack(stack.stack_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cloudformation_backends = {}
 | 
			
		||||
 | 
			
		||||
@ -374,6 +374,11 @@ class ResourceMap(collections.Mapping):
 | 
			
		||||
                changed_resource = parse_and_update_resource(resource_name, resource_json, self, self._region_name)
 | 
			
		||||
                self._parsed_resources[resource_name] = changed_resource
 | 
			
		||||
 | 
			
		||||
    def delete(self):
 | 
			
		||||
        for resource in self.resources:
 | 
			
		||||
            parsed_resource = self._parsed_resources.pop(resource)
 | 
			
		||||
            parsed_resource.delete(self._region_name)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OutputMap(collections.Mapping):
 | 
			
		||||
    def __init__(self, resources, template):
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ from six.moves.urllib.parse import urlparse
 | 
			
		||||
from moto.core.responses import BaseResponse
 | 
			
		||||
from moto.s3 import s3_backend
 | 
			
		||||
from .models import cloudformation_backends
 | 
			
		||||
from .exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CloudFormationResponse(BaseResponse):
 | 
			
		||||
@ -47,14 +48,17 @@ class CloudFormationResponse(BaseResponse):
 | 
			
		||||
            notification_arns=stack_notification_arns,
 | 
			
		||||
            tags=tags,
 | 
			
		||||
        )
 | 
			
		||||
        stack_body = {
 | 
			
		||||
            'CreateStackResponse': {
 | 
			
		||||
                'CreateStackResult': {
 | 
			
		||||
                    'StackId': stack.stack_id,
 | 
			
		||||
        if self.request_json:
 | 
			
		||||
            return json.dumps({
 | 
			
		||||
                'CreateStackResponse': {
 | 
			
		||||
                    'CreateStackResult': {
 | 
			
		||||
                        'StackId': stack.stack_id,
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return json.dumps(stack_body)
 | 
			
		||||
            })
 | 
			
		||||
        else:
 | 
			
		||||
            template = self.response_template(CREATE_STACK_RESPONSE_TEMPLATE)
 | 
			
		||||
            return template.render(stack=stack)
 | 
			
		||||
 | 
			
		||||
    def describe_stacks(self):
 | 
			
		||||
        stack_name_or_id = None
 | 
			
		||||
@ -65,11 +69,26 @@ class CloudFormationResponse(BaseResponse):
 | 
			
		||||
        template = self.response_template(DESCRIBE_STACKS_TEMPLATE)
 | 
			
		||||
        return template.render(stacks=stacks)
 | 
			
		||||
 | 
			
		||||
    def describe_stack_resource(self):
 | 
			
		||||
        stack_name = self._get_param('StackName')
 | 
			
		||||
        stack = self.cloudformation_backend.get_stack(stack_name)
 | 
			
		||||
        logical_resource_id = self._get_param('LogicalResourceId')
 | 
			
		||||
 | 
			
		||||
        for stack_resource in stack.stack_resources:
 | 
			
		||||
            if stack_resource.logical_resource_id == logical_resource_id:
 | 
			
		||||
                resource = stack_resource
 | 
			
		||||
                break
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValidationError(logical_resource_id)   
 | 
			
		||||
        
 | 
			
		||||
        template = self.response_template(DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE)
 | 
			
		||||
        return template.render(stack=stack, resource=resource)
 | 
			
		||||
 | 
			
		||||
    def describe_stack_resources(self):
 | 
			
		||||
        stack_name = self._get_param('StackName')
 | 
			
		||||
        stack = self.cloudformation_backend.get_stack(stack_name)
 | 
			
		||||
 | 
			
		||||
        template = self.response_template(DESCRIBE_STACKS_RESOURCES_RESPONSE)
 | 
			
		||||
        template = self.response_template(DESCRIBE_STACK_RESOURCES_RESPONSE)
 | 
			
		||||
        return template.render(stack=stack)
 | 
			
		||||
 | 
			
		||||
    def list_stacks(self):
 | 
			
		||||
@ -87,18 +106,21 @@ 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)
 | 
			
		||||
 | 
			
		||||
        response = {
 | 
			
		||||
            "GetTemplateResponse": {
 | 
			
		||||
            "GetTemplateResult": {
 | 
			
		||||
                "TemplateBody": stack.template,
 | 
			
		||||
                "ResponseMetadata": {
 | 
			
		||||
                    "RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
 | 
			
		||||
        
 | 
			
		||||
        if self.request_json:
 | 
			
		||||
            return json.dumps({
 | 
			
		||||
                "GetTemplateResponse": {
 | 
			
		||||
                    "GetTemplateResult": {
 | 
			
		||||
                        "TemplateBody": stack.template,
 | 
			
		||||
                        "ResponseMetadata": {
 | 
			
		||||
                            "RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        return json.dumps(response)
 | 
			
		||||
            })
 | 
			
		||||
        else:
 | 
			
		||||
            template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
 | 
			
		||||
            return template.render(stack=stack)
 | 
			
		||||
 | 
			
		||||
    def update_stack(self):
 | 
			
		||||
        stack_name = self._get_param('StackName')
 | 
			
		||||
@ -121,59 +143,108 @@ class CloudFormationResponse(BaseResponse):
 | 
			
		||||
        name_or_stack_id = self.querystring.get('StackName')[0]
 | 
			
		||||
 | 
			
		||||
        self.cloudformation_backend.delete_stack(name_or_stack_id)
 | 
			
		||||
        return json.dumps({
 | 
			
		||||
            'DeleteStackResponse': {
 | 
			
		||||
                'DeleteStackResult': {},
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        if self.request_json:
 | 
			
		||||
            return json.dumps({
 | 
			
		||||
                'DeleteStackResponse': {
 | 
			
		||||
                    'DeleteStackResult': {},
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        else:
 | 
			
		||||
            template = self.response_template(DELETE_STACK_RESPONSE_TEMPLATE)
 | 
			
		||||
            return template.render()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResult>
 | 
			
		||||
  <Stacks>
 | 
			
		||||
    {% for stack in stacks %}
 | 
			
		||||
    <member>
 | 
			
		||||
      <StackName>{{ stack.name }}</StackName>
 | 
			
		||||
      <StackId>{{ stack.stack_id }}</StackId>
 | 
			
		||||
      <CreationTime>2010-07-27T22:28:28Z</CreationTime>
 | 
			
		||||
      <StackStatus>{{ stack.status }}</StackStatus>
 | 
			
		||||
      {% if stack.notification_arns %}
 | 
			
		||||
      <NotificationARNs>
 | 
			
		||||
        {% for notification_arn in stack.notification_arns %}
 | 
			
		||||
        <member>{{ notification_arn }}</member>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
      </NotificationARNs>
 | 
			
		||||
      {% else %}
 | 
			
		||||
      <NotificationARNs/>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
      <DisableRollback>false</DisableRollback>
 | 
			
		||||
      <Outputs>
 | 
			
		||||
      {% for output in stack.stack_outputs %}
 | 
			
		||||
        <member>
 | 
			
		||||
          <OutputKey>{{ output.key }}</OutputKey>
 | 
			
		||||
          <OutputValue>{{ output.value }}</OutputValue>
 | 
			
		||||
        </member>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
      </Outputs>
 | 
			
		||||
      <Parameters>
 | 
			
		||||
      {% for param_name, param_value in stack.stack_parameters.items() %}
 | 
			
		||||
        <member>
 | 
			
		||||
          <ParameterKey>{{ param_name }}</ParameterKey>
 | 
			
		||||
          <ParameterValue>{{ param_value }}</ParameterValue>
 | 
			
		||||
        </member>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
      </Parameters>
 | 
			
		||||
      <Tags>
 | 
			
		||||
        {% for tag_key, tag_value in stack.tags.items() %}
 | 
			
		||||
CREATE_STACK_RESPONSE_TEMPLATE = """<CreateStackResponse>
 | 
			
		||||
  <CreateStackResult>
 | 
			
		||||
    <StackId>{{ stack.stack_id }}</StackId>
 | 
			
		||||
  </CreateStackResult>
 | 
			
		||||
  <ResponseMetadata>
 | 
			
		||||
    <RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
 | 
			
		||||
    </ResponseMetadata>
 | 
			
		||||
</CreateStackResponse>
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResponse>
 | 
			
		||||
  <DescribeStacksResult>
 | 
			
		||||
    <Stacks>
 | 
			
		||||
      {% for stack in stacks %}
 | 
			
		||||
      <member>
 | 
			
		||||
        <StackName>{{ stack.name }}</StackName>
 | 
			
		||||
        <StackId>{{ stack.stack_id }}</StackId>
 | 
			
		||||
        <CreationTime>2010-07-27T22:28:28Z</CreationTime>
 | 
			
		||||
        <StackStatus>{{ stack.status }}</StackStatus>
 | 
			
		||||
        {% if stack.notification_arns %}
 | 
			
		||||
        <NotificationARNs>
 | 
			
		||||
          {% for notification_arn in stack.notification_arns %}
 | 
			
		||||
          <member>{{ notification_arn }}</member>
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </NotificationARNs>
 | 
			
		||||
        {% else %}
 | 
			
		||||
        <NotificationARNs/>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <DisableRollback>false</DisableRollback>
 | 
			
		||||
        <Outputs>
 | 
			
		||||
        {% for output in stack.stack_outputs %}
 | 
			
		||||
          <member>
 | 
			
		||||
            <Key>{{ tag_key }}</Key>
 | 
			
		||||
            <Value>{{ tag_value }}</Value>
 | 
			
		||||
            <OutputKey>{{ output.key }}</OutputKey>
 | 
			
		||||
            <OutputValue>{{ output.value }}</OutputValue>
 | 
			
		||||
          </member>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
      </Tags>
 | 
			
		||||
        </Outputs>
 | 
			
		||||
        <Parameters>
 | 
			
		||||
        {% for param_name, param_value in stack.stack_parameters.items() %}
 | 
			
		||||
          <member>
 | 
			
		||||
            <ParameterKey>{{ param_name }}</ParameterKey>
 | 
			
		||||
            <ParameterValue>{{ param_value }}</ParameterValue>
 | 
			
		||||
          </member>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        </Parameters>
 | 
			
		||||
        <Tags>
 | 
			
		||||
          {% for tag_key, tag_value in stack.tags.items() %}
 | 
			
		||||
            <member>
 | 
			
		||||
              <Key>{{ tag_key }}</Key>
 | 
			
		||||
              <Value>{{ tag_value }}</Value>
 | 
			
		||||
            </member>
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </Tags>
 | 
			
		||||
      </member>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    </Stacks>
 | 
			
		||||
  </DescribeStacksResult>
 | 
			
		||||
</DescribeStacksResponse>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
 | 
			
		||||
  <DescribeStackResourceResult>
 | 
			
		||||
    <StackResourceDetail>
 | 
			
		||||
      <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>
 | 
			
		||||
    </StackResourceDetail>
 | 
			
		||||
  </DescribeStackResourceResult>
 | 
			
		||||
</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 %}
 | 
			
		||||
  </Stacks>
 | 
			
		||||
</DescribeStacksResult>"""
 | 
			
		||||
  </StackResources>
 | 
			
		||||
</DescribeStackResourcesResult>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LIST_STACKS_RESPONSE = """<ListStacksResponse>
 | 
			
		||||
@ -193,23 +264,6 @@ LIST_STACKS_RESPONSE = """<ListStacksResponse>
 | 
			
		||||
</ListStacksResponse>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DESCRIBE_STACKS_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>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
 | 
			
		||||
  <ListStackResourcesResult>
 | 
			
		||||
    <StackResourceSummaries>
 | 
			
		||||
@ -228,3 +282,22 @@ LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
 | 
			
		||||
    <RequestId>2d06e36c-ac1d-11e0-a958-f9382b6eb86b</RequestId>
 | 
			
		||||
  </ResponseMetadata>
 | 
			
		||||
</ListStackResourcesResponse>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
 | 
			
		||||
  <GetTemplateResult>
 | 
			
		||||
    <TemplateBody>{{ stack.template }}
 | 
			
		||||
    </TemplateBody>
 | 
			
		||||
  </GetTemplateResult>
 | 
			
		||||
  <ResponseMetadata>
 | 
			
		||||
    <RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
 | 
			
		||||
  </ResponseMetadata>
 | 
			
		||||
</GetTemplateResponse>"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
 | 
			
		||||
  <ResponseMetadata>
 | 
			
		||||
    <RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
 | 
			
		||||
  </ResponseMetadata>
 | 
			
		||||
</DeleteStackResponse>
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
@ -254,6 +254,10 @@ class BaseResponse(_TemplateEnvironmentMixin):
 | 
			
		||||
            param_index += 1
 | 
			
		||||
        return results
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def request_json(self):
 | 
			
		||||
        return 'JSON' in self.querystring.get('ContentType', [])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def metadata_response(request, full_url, headers):
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
@ -12,10 +12,6 @@ class SNSResponse(BaseResponse):
 | 
			
		||||
    def backend(self):
 | 
			
		||||
        return sns_backends[self.region]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def request_json(self):
 | 
			
		||||
        return 'JSON' in self.querystring.get('ContentType', [])
 | 
			
		||||
 | 
			
		||||
    def _get_attributes(self):
 | 
			
		||||
        attributes = self._get_list_prefix('Attributes.entry')
 | 
			
		||||
        return dict(
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,232 @@
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
import boto3
 | 
			
		||||
import boto
 | 
			
		||||
import boto.s3
 | 
			
		||||
import boto.s3.key
 | 
			
		||||
from botocore.exceptions import ClientError
 | 
			
		||||
from moto import mock_cloudformation, mock_s3
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import sure  # noqa
 | 
			
		||||
# Ensure 'assert_raises' context manager support for Python 2.6
 | 
			
		||||
import tests.backport_assert_raises  # noqa
 | 
			
		||||
from nose.tools import assert_raises
 | 
			
		||||
 | 
			
		||||
dummy_template = {
 | 
			
		||||
    "AWSTemplateFormatVersion": "2010-09-09",
 | 
			
		||||
    "Description": "Stack 1",
 | 
			
		||||
    "Resources": {},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dummy_template_json = json.dumps(dummy_template)
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_boto3_create_stack():
 | 
			
		||||
    cf_conn = boto3.client('cloudformation', region_name='us-east-1')
 | 
			
		||||
    cf_conn.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cf_conn.get_template(StackName="test_stack")['TemplateBody'].should.equal(dummy_template)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_creating_stacks_across_regions():
 | 
			
		||||
    west1_cf = boto3.resource('cloudformation', region_name='us-west-1')
 | 
			
		||||
    west2_cf = boto3.resource('cloudformation', region_name='us-west-2')
 | 
			
		||||
    west1_cf.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
    west2_cf.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    list(west1_cf.stacks.all()).should.have.length_of(1)
 | 
			
		||||
    list(west2_cf.stacks.all()).should.have.length_of(1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_create_stack_with_notification_arn():
 | 
			
		||||
    cf = boto3.resource('cloudformation', region_name='us-east-1')
 | 
			
		||||
    cf.create_stack(
 | 
			
		||||
        StackName="test_stack_with_notifications",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
        NotificationARNs=['arn:aws:sns:us-east-1:123456789012:fake-queue'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    stack = list(cf.stacks.all())[0]
 | 
			
		||||
    stack.notification_arns.should.contain('arn:aws:sns:us-east-1:123456789012:fake-queue')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
@mock_s3
 | 
			
		||||
def test_create_stack_from_s3_url():
 | 
			
		||||
    s3_conn = boto.s3.connect_to_region('us-west-1')
 | 
			
		||||
    bucket = s3_conn.create_bucket("foobar")
 | 
			
		||||
    key = boto.s3.key.Key(bucket)
 | 
			
		||||
    key.key = "template-key"
 | 
			
		||||
    key.set_contents_from_string(dummy_template_json)
 | 
			
		||||
    key_url = key.generate_url(expires_in=0, query_auth=False)
 | 
			
		||||
 | 
			
		||||
    cf_conn = boto3.client('cloudformation', region_name='us-west-1')
 | 
			
		||||
    cf_conn.create_stack(
 | 
			
		||||
        StackName='stack_from_url',
 | 
			
		||||
        TemplateURL=key_url,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cf_conn.get_template(StackName="stack_from_url")['TemplateBody'].should.equal(dummy_template)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_describe_stack_by_name():
 | 
			
		||||
    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]
 | 
			
		||||
    stack['StackName'].should.equal('test_stack')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_describe_stack_by_stack_id():
 | 
			
		||||
    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]
 | 
			
		||||
    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")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_list_stacks():
 | 
			
		||||
    cf = boto3.resource('cloudformation', region_name='us-east-1')
 | 
			
		||||
    cf.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
    cf.create_stack(
 | 
			
		||||
        StackName="test_stack2",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    stacks = list(cf.stacks.all())
 | 
			
		||||
    stacks.should.have.length_of(2)
 | 
			
		||||
    stack_names = [stack.stack_name for stack in stacks]
 | 
			
		||||
    stack_names.should.contain("test_stack")
 | 
			
		||||
    stack_names.should.contain("test_stack2")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_delete_stack_from_resource():
 | 
			
		||||
    cf = boto3.resource('cloudformation', region_name='us-east-1')
 | 
			
		||||
    stack = cf.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    list(cf.stacks.all()).should.have.length_of(1)
 | 
			
		||||
    stack.delete()
 | 
			
		||||
    list(cf.stacks.all()).should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_delete_stack_by_name():
 | 
			
		||||
    cf_conn = boto3.client('cloudformation', region_name='us-east-1')
 | 
			
		||||
    cf_conn.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        TemplateBody=dummy_template_json,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    cf_conn.describe_stacks()['Stacks'].should.have.length_of(1)
 | 
			
		||||
    cf_conn.delete_stack(StackName="test_stack")
 | 
			
		||||
    cf_conn.describe_stacks()['Stacks'].should.have.length_of(0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_describe_deleted_stack():
 | 
			
		||||
    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]
 | 
			
		||||
    stack_id = stack['StackId']
 | 
			
		||||
    cf_conn.delete_stack(StackName=stack['StackId'])
 | 
			
		||||
    stack_by_id = cf_conn.describe_stacks(StackName=stack_id)['Stacks'][0]
 | 
			
		||||
    stack_by_id['StackId'].should.equal(stack['StackId'])
 | 
			
		||||
    stack_by_id['StackName'].should.equal("test_stack")
 | 
			
		||||
    stack_by_id['StackStatus'].should.equal("DELETE_COMPLETE")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_bad_describe_stack():
 | 
			
		||||
    cf_conn = boto3.client('cloudformation', region_name='us-east-1')
 | 
			
		||||
    with assert_raises(ClientError):
 | 
			
		||||
        cf_conn.describe_stacks(StackName="non_existent_stack")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation()
 | 
			
		||||
def test_cloudformation_params():
 | 
			
		||||
    dummy_template_with_params = {
 | 
			
		||||
        "AWSTemplateFormatVersion": "2010-09-09",
 | 
			
		||||
        "Description": "Stack 1",
 | 
			
		||||
        "Resources": {},
 | 
			
		||||
        "Parameters": {
 | 
			
		||||
            "APPNAME": {
 | 
			
		||||
                "Default": "app-name",
 | 
			
		||||
                "Description": "The name of the app",
 | 
			
		||||
                "Type": "String"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    dummy_template_with_params_json = json.dumps(dummy_template_with_params)
 | 
			
		||||
 | 
			
		||||
    cf = boto3.resource('cloudformation', region_name='us-east-1')
 | 
			
		||||
    stack = cf.create_stack(
 | 
			
		||||
        StackName='test_stack',
 | 
			
		||||
        TemplateBody=dummy_template_with_params_json,
 | 
			
		||||
        Parameters=[{
 | 
			
		||||
            "ParameterKey": "APPNAME",
 | 
			
		||||
            "ParameterValue": "testing123",
 | 
			
		||||
        }],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    stack.parameters.should.have.length_of(1)
 | 
			
		||||
    param = stack.parameters[0]
 | 
			
		||||
    param['ParameterKey'].should.equal('APPNAME')
 | 
			
		||||
    param['ParameterValue'].should.equal('testing123')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@mock_cloudformation
 | 
			
		||||
def test_stack_tags():
 | 
			
		||||
    tags = [
 | 
			
		||||
        {
 | 
			
		||||
            "Key": "foo",
 | 
			
		||||
            "Value": "bar"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "Key": "baz",
 | 
			
		||||
            "Value": "bleh"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
    cf = boto3.resource('cloudformation', region_name='us-east-1')
 | 
			
		||||
    stack = cf.create_stack(
 | 
			
		||||
        StackName="test_stack",
 | 
			
		||||
        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.should.equal(expected_tag_items)
 | 
			
		||||
@ -19,13 +19,12 @@ def test_cloudformation_server_get():
 | 
			
		||||
    template_body = {
 | 
			
		||||
        "Resources": {},
 | 
			
		||||
    }
 | 
			
		||||
    res = test_client.action_json("CreateStack", StackName=stack_name,
 | 
			
		||||
    create_stack_resp = test_client.action_data("CreateStack", StackName=stack_name,
 | 
			
		||||
        TemplateBody=json.dumps(template_body))
 | 
			
		||||
    stack_id = res["CreateStackResponse"]["CreateStackResult"]["StackId"]
 | 
			
		||||
    create_stack_resp.should.match(r"<CreateStackResponse>.*<CreateStackResult>.*<StackId>.*</StackId>.*</CreateStackResult>.*</CreateStackResponse>", re.DOTALL)
 | 
			
		||||
    stack_id_from_create_response = re.search("<StackId>(.*)</StackId>", create_stack_resp).groups()[0]
 | 
			
		||||
 | 
			
		||||
    data = test_client.action_data("ListStacks")
 | 
			
		||||
    list_stacks_resp = test_client.action_data("ListStacks")
 | 
			
		||||
    stack_id_from_list_response = re.search("<StackId>(.*)</StackId>", list_stacks_resp).groups()[0]
 | 
			
		||||
 | 
			
		||||
    stacks = re.search("<StackId>(.*)</StackId>", data)
 | 
			
		||||
 | 
			
		||||
    list_stack_id = stacks.groups()[0]
 | 
			
		||||
    assert stack_id == list_stack_id
 | 
			
		||||
    stack_id_from_create_response.should.equal(stack_id_from_list_response)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user