Skip to content

Commit

Permalink
Merge remote-tracking branch 'spulec/master'
Browse files Browse the repository at this point in the history
Conflicts:
	moto/s3/responses.py
  • Loading branch information
kouk committed Nov 14, 2013
2 parents 92bebbb + d5b3af2 commit 3628e40
Show file tree
Hide file tree
Showing 42 changed files with 1,173 additions and 308 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
moto.egg-info/*
dist/*
.tox
.coverage
*.pyc
.noseids
build/
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: python
python:
- 2.6
- 2.7
env:
matrix:
Expand All @@ -12,6 +13,7 @@ env:
- BOTO_VERSION=2.7
install:
- pip install boto==$BOTO_VERSION
- pip install https://github.com/gabrielfalcao/HTTPretty/tarball/8bbbdfc14326678b1aeba6a2d81af0d835a2cd6f
- pip install .
- pip install -r requirements.txt
script:
Expand Down
2 changes: 2 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ Moto is written by Steve Pulec with contributions from:
* [Dilshod Tadjibaev](https://github.com/antimora)
* [Dan Berglund](https://github.com/cheif)
* [Lincoln de Sousa](https://github.com/clarete)
* [mhock](https://github.com/mhock)
* [Ilya Sukhanov](https://github.com/IlyaSukhanov)
1 change: 1 addition & 0 deletions moto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .elb import mock_elb
from .emr import mock_emr
from .s3 import mock_s3
from .s3bucket_path import mock_s3bucket_path
from .ses import mock_ses
from .sqs import mock_sqs
from .sts import mock_sts
2 changes: 2 additions & 0 deletions moto/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from moto.elb import elb_backend
from moto.emr import emr_backend
from moto.s3 import s3_backend
from moto.s3bucket_path import s3bucket_path_backend
from moto.ses import ses_backend
from moto.sqs import sqs_backend
from moto.sts import sts_backend
Expand All @@ -15,6 +16,7 @@
'elb': elb_backend,
'emr': emr_backend,
's3': s3_backend,
's3bucket_path': s3bucket_path_backend,
'ses': ses_backend,
'sqs': sqs_backend,
'sts': sts_backend,
Expand Down
1 change: 1 addition & 0 deletions moto/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
class MockAWS(object):
def __init__(self, backend):
self.backend = backend
HTTPretty.reset()

def __call__(self, func):
return self.decorate_callable(func)
Expand Down
2 changes: 1 addition & 1 deletion moto/core/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def call_action(self):
status = new_headers.pop('status', 200)
headers.update(new_headers)
return status, headers, body
raise NotImplementedError("The {} action has not been implemented".format(action))
raise NotImplementedError("The {0} action has not been implemented".format(action))


def metadata_response(request, full_url, headers):
Expand Down
4 changes: 2 additions & 2 deletions moto/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def get_random_hex(length=8):


def get_random_message_id():
return '{}-{}-{}-{}-{}'.format(get_random_hex(8), get_random_hex(4), get_random_hex(4), get_random_hex(4), get_random_hex(12))
return '{0}-{1}-{2}-{3}-{4}'.format(get_random_hex(8), get_random_hex(4), get_random_hex(4), get_random_hex(4), get_random_hex(12))


def convert_regex_to_flask_path(url_path):
Expand Down Expand Up @@ -61,7 +61,7 @@ def __name__(self):
outer = self.callback.im_class.__name__
else:
outer = self.callback.__module__
return "{}.{}".format(outer, self.callback.__name__)
return "{0}.{1}".format(outer, self.callback.__name__)

def __call__(self, args=None, **kwargs):
headers = dict(request.headers)
Expand Down
13 changes: 10 additions & 3 deletions moto/dynamodb/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from collections import defaultdict, OrderedDict
from collections import defaultdict
import datetime
import json

try:
from collections import OrderedDict
except ImportError:
# python 2.6 or earlier, use backport
from ordereddict import OrderedDict


from moto.core import BaseBackend
from .comparisons import get_comparison_func
from .utils import unix_time
Expand Down Expand Up @@ -36,7 +43,7 @@ def __eq__(self, other):
)

def __repr__(self):
return "DynamoType: {}".format(self.to_json())
return "DynamoType: {0}".format(self.to_json())

def to_json(self):
return {self.type: self.value}
Expand All @@ -62,7 +69,7 @@ def __init__(self, hash_key, hash_key_type, range_key, range_key_type, attrs):
self.attrs[key] = DynamoType(value)

def __repr__(self):
return "Item: {}".format(self.to_json())
return "Item: {0}".format(self.to_json())

def to_json(self):
attributes = {}
Expand Down
6 changes: 2 additions & 4 deletions moto/dynamodb/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import datetime
import calendar


def unix_time(dt):
epoch = datetime.datetime.utcfromtimestamp(0)
delta = dt - epoch
return delta.total_seconds()
return calendar.timegm(dt.timetuple())
116 changes: 103 additions & 13 deletions moto/ec2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
random_subnet_id,
random_volume_id,
random_vpc_id,
random_eip_association_id,
random_eip_allocation_id,
random_ip,
)


Expand All @@ -29,24 +32,24 @@ def __init__(self, image_id, user_data):
super(Instance, self).__init__()
self.id = random_instance_id()
self.image_id = image_id
self._state = InstanceState()
self._state = InstanceState("running", 16)
self.user_data = user_data

def start(self):
self._state.name = "pending"
self._state.code = 0
self._state.name = "running"
self._state.code = 16

def stop(self):
self._state.name = "stopping"
self._state.code = 64
self._state.name = "stopped"
self._state.code = 80

def terminate(self):
self._state.name = "shutting-down"
self._state.code = 32
self._state.name = "terminated"
self._state.code = 48

def reboot(self):
self._state.name = "pending"
self._state.code = 0
self._state.name = "running"
self._state.code = 16

def get_tags(self):
tags = ec2_backend.describe_tags(self.id)
Expand Down Expand Up @@ -215,8 +218,12 @@ def create_image(self, instance_id, name, description):
self.amis[ami_id] = ami
return ami

def describe_images(self):
return self.amis.values()
def describe_images(self, ami_ids=None):
if ami_ids:
images = [image for image in self.amis.values() if image.id in ami_ids]
else:
images = self.amis.values()
return images

def deregister_image(self, ami_id):
if ami_id in self.amis:
Expand Down Expand Up @@ -280,7 +287,7 @@ def __init__(self, ip_protocol, from_port, to_port, ip_ranges, source_groups):

@property
def unique_representation(self):
return "{}-{}-{}-{}-{}".format(
return "{0}-{1}-{2}-{3}-{4}".format(
self.ip_protocol,
self.from_port,
self.to_port,
Expand Down Expand Up @@ -571,9 +578,92 @@ def cancel_spot_instance_requests(self, request_ids):
return requests


class ElasticAddress():
def __init__(self, domain):
self.public_ip = random_ip()
self.allocation_id = random_eip_allocation_id() if domain == "vpc" else None
self.domain = domain
self.instance = None
self.association_id = None


class ElasticAddressBackend(object):

def __init__(self):
self.addresses = []
super(ElasticAddressBackend, self).__init__()

def allocate_address(self, domain):
address = ElasticAddress(domain)
self.addresses.append(address)
return address

def address_by_ip(self, ips):
return [address for address in self.addresses
if address.public_ip in ips]

def address_by_allocation(self, allocation_ids):
return [address for address in self.addresses
if address.allocation_id in allocation_ids]

def address_by_association(self, association_ids):
return [address for address in self.addresses
if address.association_id in association_ids]

def associate_address(self, instance, address=None, allocation_id=None, reassociate=False):
eips = []
if address:
eips = self.address_by_ip([address])
elif allocation_id:
eips = self.address_by_allocation([allocation_id])
eip = eips[0] if len(eips) > 0 else None

if eip and eip.instance is None or reassociate:
eip.instance = instance
if eip.domain == "vpc":
eip.association_id = random_eip_association_id()
return eip
else:
return None

def describe_addresses(self):
return self.addresses

def disassociate_address(self, address=None, association_id=None):
eips = []
if address:
eips = self.address_by_ip([address])
elif association_id:
eips = self.address_by_association([association_id])

if eips:
eip = eips[0]
eip.instance = None
eip.association_id = None
return True
else:
return False

def release_address(self, address=None, allocation_id=None):
eips = []
if address:
eips = self.address_by_ip([address])
elif allocation_id:
eips = self.address_by_allocation([allocation_id])

if eips:
eip = eips[0]
self.disassociate_address(address=eip.public_ip)
eip.allocation_id = None
self.addresses.remove(eip)
return True
else:
return False


class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
VPCBackend, SubnetBackend, SpotRequestBackend):
VPCBackend, SubnetBackend, SpotRequestBackend, ElasticAddressBackend):
pass


Expand Down
12 changes: 8 additions & 4 deletions moto/ec2/responses/amis.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
from jinja2 import Template

from moto.ec2.models import ec2_backend
from moto.ec2.utils import instance_ids_from_querystring
from moto.ec2.utils import instance_ids_from_querystring, image_ids_from_querystring


class AmisResponse(object):
def create_image(self):
name = self.querystring.get('Name')[0]
description = self.querystring.get('Description')[0]
if "Description" in self.querystring:
description = self.querystring.get('Description')[0]
else:
description = ""
instance_ids = instance_ids_from_querystring(self.querystring)
instance_id = instance_ids[0]
image = ec2_backend.create_image(instance_id, name, description)
if not image:
return "There is not instance with id {}".format(instance_id), dict(status=404)
return "There is not instance with id {0}".format(instance_id), dict(status=404)
template = Template(CREATE_IMAGE_RESPONSE)
return template.render(image=image)

Expand All @@ -30,7 +33,8 @@ def describe_image_attribute(self):
raise NotImplementedError('AMIs.describe_image_attribute is not yet implemented')

def describe_images(self):
images = ec2_backend.describe_images()
ami_ids = image_ids_from_querystring(self.querystring)
images = ec2_backend.describe_images(ami_ids=ami_ids)
template = Template(DESCRIBE_IMAGES_RESPONSE)
return template.render(images=images)

Expand Down
6 changes: 3 additions & 3 deletions moto/ec2/responses/elastic_block_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ def delete_snapshot(self):
success = ec2_backend.delete_snapshot(snapshot_id)
if not success:
# Snapshot doesn't exist
return "Snapshot with id {} does not exist".format(snapshot_id), dict(status=404)
return "Snapshot with id {0} does not exist".format(snapshot_id), dict(status=404)
return DELETE_SNAPSHOT_RESPONSE

def delete_volume(self):
volume_id = self.querystring.get('VolumeId')[0]
success = ec2_backend.delete_volume(volume_id)
if not success:
# Volume doesn't exist
return "Volume with id {} does not exist".format(volume_id), dict(status=404)
return "Volume with id {0} does not exist".format(volume_id), dict(status=404)
return DELETE_VOLUME_RESPONSE

def describe_snapshot_attribute(self):
Expand Down Expand Up @@ -77,7 +77,7 @@ def detach_volume(self):
attachment = ec2_backend.detach_volume(volume_id, instance_id, device_path)
if not attachment:
# Volume wasn't attached
return "Volume {} can not be detached from {} because it is not attached".format(volume_id, instance_id), dict(status=404)
return "Volume {0} can not be detached from {1} because it is not attached".format(volume_id, instance_id), dict(status=404)
template = Template(DETATCH_VOLUME_RESPONSE)
return template.render(attachment=attachment)

Expand Down

0 comments on commit 3628e40

Please sign in to comment.