basic ec2 and s3 working
This commit is contained in:
parent
6a060dfd7e
commit
77d6df6531
11
Makefile
Normal file
11
Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
|
||||||
|
init:
|
||||||
|
python setup.py develop
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
test:
|
||||||
|
nosetests ./tests/
|
||||||
|
|
||||||
|
travis:
|
||||||
|
nosetests ./tests/
|
@ -9,7 +9,6 @@ Imagine you have the following code that you want to test:
|
|||||||
```python
|
```python
|
||||||
import boto
|
import boto
|
||||||
from boto.s3.key import Key
|
from boto.s3.key import Key
|
||||||
conn = boto.connect_s3()
|
|
||||||
|
|
||||||
class MyModel(object):
|
class MyModel(object):
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
@ -17,6 +16,7 @@ class MyModel(object):
|
|||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
conn = boto.connect_s3()
|
||||||
bucket = conn.get_bucket('mybucket')
|
bucket = conn.get_bucket('mybucket')
|
||||||
k = Key(bucket)
|
k = Key(bucket)
|
||||||
k.key = self.name
|
k.key = self.name
|
||||||
|
2
moto/__init__.py
Normal file
2
moto/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .ec2 import mock_ec2
|
||||||
|
from .s3 import mock_s3
|
BIN
moto/__init__.pyc
Normal file
BIN
moto/__init__.pyc
Normal file
Binary file not shown.
1
moto/core/__init__.py
Normal file
1
moto/core/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .models import BaseBackend
|
BIN
moto/core/__init__.pyc
Normal file
BIN
moto/core/__init__.pyc
Normal file
Binary file not shown.
40
moto/core/models.py
Normal file
40
moto/core/models.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import functools
|
||||||
|
import re
|
||||||
|
|
||||||
|
from httpretty import HTTPretty
|
||||||
|
|
||||||
|
|
||||||
|
class BaseBackend(object):
|
||||||
|
base_url = None
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self = self.__class__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def urls(self):
|
||||||
|
backend_module = self.__class__.__module__
|
||||||
|
backend_urls_module_name = backend_module.replace("models", "urls")
|
||||||
|
backend_urls_module = __import__(backend_urls_module_name, fromlist=['urls'])
|
||||||
|
urls = backend_urls_module.urls
|
||||||
|
return urls
|
||||||
|
|
||||||
|
def decorator(self, func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kw):
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
HTTPretty.reset()
|
||||||
|
HTTPretty.enable()
|
||||||
|
|
||||||
|
for method in HTTPretty.METHODS:
|
||||||
|
for key, value in self.urls.iteritems():
|
||||||
|
HTTPretty.register_uri(
|
||||||
|
method=method,
|
||||||
|
uri=re.compile(self.base_url + key),
|
||||||
|
body=value,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
return func(*args, **kw)
|
||||||
|
finally:
|
||||||
|
HTTPretty.disable()
|
||||||
|
return wrapper
|
BIN
moto/core/models.pyc
Normal file
BIN
moto/core/models.pyc
Normal file
Binary file not shown.
2
moto/ec2/__init__.py
Normal file
2
moto/ec2/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .models import ec2_backend
|
||||||
|
mock_ec2 = ec2_backend.decorator
|
BIN
moto/ec2/__init__.pyc
Normal file
BIN
moto/ec2/__init__.pyc
Normal file
Binary file not shown.
44
moto/ec2/models.py
Normal file
44
moto/ec2/models.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from boto.ec2.instance import Instance, InstanceState, Reservation
|
||||||
|
|
||||||
|
from moto.core import BaseBackend
|
||||||
|
from .utils import random_instance_id, random_reservation_id
|
||||||
|
|
||||||
|
|
||||||
|
class MockEC2(BaseBackend):
|
||||||
|
base_url = "https://ec2.us-east-1.amazonaws.com"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reservations = {}
|
||||||
|
|
||||||
|
def add_instance(self):
|
||||||
|
new_instance = Instance()
|
||||||
|
new_instance.id = random_instance_id()
|
||||||
|
new_instance._state = InstanceState(0, "pending")
|
||||||
|
|
||||||
|
new_reservation = Reservation()
|
||||||
|
new_reservation.id = random_reservation_id()
|
||||||
|
new_reservation.instances = [new_instance]
|
||||||
|
self.reservations[new_reservation.id] = new_reservation
|
||||||
|
return new_reservation
|
||||||
|
|
||||||
|
def terminate_instances(self, instance_ids):
|
||||||
|
terminated_instances = []
|
||||||
|
for instance in self.all_instances():
|
||||||
|
if instance.id in instance_ids:
|
||||||
|
instance._state = InstanceState(32, 'shutting-down')
|
||||||
|
terminated_instances.append(instance)
|
||||||
|
|
||||||
|
return terminated_instances
|
||||||
|
|
||||||
|
def all_instances(self):
|
||||||
|
instances = []
|
||||||
|
for reservation in self.all_reservations():
|
||||||
|
for instance in reservation.instances:
|
||||||
|
instances.append(instance)
|
||||||
|
return instances
|
||||||
|
|
||||||
|
def all_reservations(self):
|
||||||
|
return self.reservations.values()
|
||||||
|
|
||||||
|
|
||||||
|
ec2_backend = MockEC2()
|
BIN
moto/ec2/models.pyc
Normal file
BIN
moto/ec2/models.pyc
Normal file
Binary file not shown.
224
moto/ec2/responses.py
Normal file
224
moto/ec2/responses.py
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
from urlparse import parse_qs
|
||||||
|
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
from .models import ec2_backend
|
||||||
|
|
||||||
|
|
||||||
|
def instances(uri, body, headers):
|
||||||
|
querystring = parse_qs(body)
|
||||||
|
action = querystring['Action'][0]
|
||||||
|
|
||||||
|
if action == 'DescribeInstances':
|
||||||
|
template = Template(EC2_DESCRIBE_INSTANCES)
|
||||||
|
return template.render(reservations=ec2_backend.all_reservations())
|
||||||
|
elif action == 'RunInstances':
|
||||||
|
new_reservation = ec2_backend.add_instance()
|
||||||
|
template = Template(EC2_RUN_INSTANCES)
|
||||||
|
return template.render(reservation=new_reservation)
|
||||||
|
elif action == 'TerminateInstances':
|
||||||
|
instance_ids = querystring.get('InstanceId.1')[0]
|
||||||
|
instances = ec2_backend.terminate_instances(instance_ids)
|
||||||
|
template = Template(EC2_TERMINATE_INSTANCES)
|
||||||
|
return template.render(instances=instances)
|
||||||
|
else:
|
||||||
|
raise ValueError("Not implemented", action)
|
||||||
|
|
||||||
|
|
||||||
|
EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<reservationId>{{ reservation.id }}</reservationId>
|
||||||
|
<ownerId>111122223333</ownerId>
|
||||||
|
<groupSet>
|
||||||
|
<item>
|
||||||
|
<groupId>sg-245f6a01</groupId>
|
||||||
|
<groupName>default</groupName>
|
||||||
|
</item>
|
||||||
|
</groupSet>
|
||||||
|
<instancesSet>
|
||||||
|
{% for instance in reservation.instances %}
|
||||||
|
<item>
|
||||||
|
<instanceId>{{ instance.id }}</instanceId>
|
||||||
|
<imageId>ami-60a54009</imageId>
|
||||||
|
<instanceState>
|
||||||
|
<code>0</code>
|
||||||
|
<name>pending</name>
|
||||||
|
</instanceState>
|
||||||
|
<privateDnsName/>
|
||||||
|
<dnsName/>
|
||||||
|
<reason/>
|
||||||
|
<amiLaunchIndex>0</amiLaunchIndex>
|
||||||
|
<instanceType>m1.small</instanceType>
|
||||||
|
<launchTime>2007-08-07T11:51:50.000Z</launchTime>
|
||||||
|
<placement>
|
||||||
|
<availabilityZone>us-east-1b</availabilityZone>
|
||||||
|
<groupName/>
|
||||||
|
<tenancy>default</tenancy>
|
||||||
|
</placement>
|
||||||
|
<monitoring>
|
||||||
|
<state>enabled</state>
|
||||||
|
</monitoring>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
<item>
|
||||||
|
<groupId>sg-245f6a01</groupId>
|
||||||
|
<groupName>default</groupName>
|
||||||
|
</item>
|
||||||
|
</groupSet>
|
||||||
|
<virtualizationType>paravirtual</virtualizationType>
|
||||||
|
<clientToken/>
|
||||||
|
<hypervisor>xen</hypervisor>
|
||||||
|
<ebsOptimized>false</ebsOptimized>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</instancesSet>
|
||||||
|
</RunInstancesResponse>"""
|
||||||
|
|
||||||
|
EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazonaws.com/doc/2012-12-01/'>
|
||||||
|
<requestId>fdcdcab1-ae5c-489e-9c33-4637c5dda355</requestId>
|
||||||
|
<reservationSet>
|
||||||
|
{% for reservation in reservations %}
|
||||||
|
<item>
|
||||||
|
<reservationId>{{ reservation.id }}</reservationId>
|
||||||
|
<ownerId>111122223333</ownerId>
|
||||||
|
<groupSet>
|
||||||
|
<item>
|
||||||
|
<groupId>sg-1a2b3c4d</groupId>
|
||||||
|
<groupName>my-security-group</groupName>
|
||||||
|
</item>
|
||||||
|
</groupSet>
|
||||||
|
<instancesSet>
|
||||||
|
{% for instance in reservation.instances %}
|
||||||
|
<item>
|
||||||
|
<instanceId>{{ instance.id }}</instanceId>
|
||||||
|
<imageId>ami-1a2b3c4d</imageId>
|
||||||
|
<instanceState>
|
||||||
|
<code>16</code>
|
||||||
|
<name>{{ instance.state }}</name>
|
||||||
|
</instanceState>
|
||||||
|
<privateDnsName/>
|
||||||
|
<dnsName/>
|
||||||
|
<reason/>
|
||||||
|
<keyName>gsg-keypair</keyName>
|
||||||
|
<amiLaunchIndex>0</amiLaunchIndex>
|
||||||
|
<productCodes/>
|
||||||
|
<instanceType>c1.medium</instanceType>
|
||||||
|
<launchTime>YYYY-MM-DDTHH:MM:SS+0000</launchTime>
|
||||||
|
<placement>
|
||||||
|
<availabilityZone>us-west-2a</availabilityZone>
|
||||||
|
<groupName/>
|
||||||
|
<tenancy>default</tenancy>
|
||||||
|
</placement>
|
||||||
|
<platform>windows</platform>
|
||||||
|
<monitoring>
|
||||||
|
<state>disabled</state>
|
||||||
|
</monitoring>
|
||||||
|
<subnetId>subnet-1a2b3c4d</subnetId>
|
||||||
|
<vpcId>vpc-1a2b3c4d</vpcId>
|
||||||
|
<privateIpAddress>10.0.0.12</privateIpAddress>
|
||||||
|
<ipAddress>46.51.219.63</ipAddress>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
<item>
|
||||||
|
<groupId>sg-1a2b3c4d</groupId>
|
||||||
|
<groupName>my-security-group</groupName>
|
||||||
|
</item>
|
||||||
|
</groupSet>
|
||||||
|
<architecture>x86_64</architecture>
|
||||||
|
<rootDeviceType>ebs</rootDeviceType>
|
||||||
|
<rootDeviceName>/dev/sda1</rootDeviceName>
|
||||||
|
<blockDeviceMapping>
|
||||||
|
<item>
|
||||||
|
<deviceName>/dev/sda1</deviceName>
|
||||||
|
<ebs>
|
||||||
|
<volumeId>vol-1a2b3c4d</volumeId>
|
||||||
|
<status>attached</status>
|
||||||
|
<attachTime>YYYY-MM-DDTHH:MM:SS.SSSZ</attachTime>
|
||||||
|
<deleteOnTermination>true</deleteOnTermination>
|
||||||
|
</ebs>
|
||||||
|
</item>
|
||||||
|
</blockDeviceMapping>
|
||||||
|
<virtualizationType>hvm</virtualizationType>
|
||||||
|
<clientToken>ABCDE1234567890123</clientToken>
|
||||||
|
<tagSet>
|
||||||
|
<item>
|
||||||
|
<key>Name</key>
|
||||||
|
<value>Windows Instance</value>
|
||||||
|
</item>
|
||||||
|
</tagSet>
|
||||||
|
<hypervisor>xen</hypervisor>
|
||||||
|
<networkInterfaceSet>
|
||||||
|
<item>
|
||||||
|
<networkInterfaceId>eni-1a2b3c4d</networkInterfaceId>
|
||||||
|
<subnetId>subnet-1a2b3c4d</subnetId>
|
||||||
|
<vpcId>vpc-1a2b3c4d</vpcId>
|
||||||
|
<description>Primary network interface</description>
|
||||||
|
<ownerId>111122223333</ownerId>
|
||||||
|
<status>in-use</status>
|
||||||
|
<privateIpAddress>10.0.0.12</privateIpAddress>
|
||||||
|
<macAddress>1b:2b:3c:4d:5e:6f</macAddress>
|
||||||
|
<sourceDestCheck>true</sourceDestCheck>
|
||||||
|
<groupSet>
|
||||||
|
<item>
|
||||||
|
<groupId>sg-1a2b3c4d</groupId>
|
||||||
|
<groupName>my-security-group</groupName>
|
||||||
|
</item>
|
||||||
|
</groupSet>
|
||||||
|
<attachment>
|
||||||
|
<attachmentId>eni-attach-1a2b3c4d</attachmentId>
|
||||||
|
<deviceIndex>0</deviceIndex>
|
||||||
|
<status>attached</status>
|
||||||
|
<attachTime>YYYY-MM-DDTHH:MM:SS+0000</attachTime>
|
||||||
|
<deleteOnTermination>true</deleteOnTermination>
|
||||||
|
</attachment>
|
||||||
|
<association>
|
||||||
|
<publicIp>46.51.219.63</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
<privateIpAddressesSet>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>10.0.0.12</privateIpAddress>
|
||||||
|
<primary>true</primary>
|
||||||
|
<association>
|
||||||
|
<publicIp>46.51.219.63</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<privateIpAddress>10.0.0.14</privateIpAddress>
|
||||||
|
<primary>false</primary>
|
||||||
|
<association>
|
||||||
|
<publicIp>46.51.221.177</publicIp>
|
||||||
|
<ipOwnerId>111122223333</ipOwnerId>
|
||||||
|
</association>
|
||||||
|
</item>
|
||||||
|
</privateIpAddressesSet>
|
||||||
|
</item>
|
||||||
|
</networkInterfaceSet>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</instancesSet>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</reservationSet>
|
||||||
|
</DescribeInstancesResponse>"""
|
||||||
|
|
||||||
|
EC2_TERMINATE_INSTANCES = """
|
||||||
|
<TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2012-12-01/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<instancesSet>
|
||||||
|
{% for instance in instances %}
|
||||||
|
<item>
|
||||||
|
<instanceId>{{ instance.id }}</instanceId>
|
||||||
|
<currentState>
|
||||||
|
<code>32</code>
|
||||||
|
<name>shutting-down</name>
|
||||||
|
</currentState>
|
||||||
|
<previousState>
|
||||||
|
<code>16</code>
|
||||||
|
<name>running</name>
|
||||||
|
</previousState>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</instancesSet>
|
||||||
|
</TerminateInstancesResponse>"""
|
BIN
moto/ec2/responses.pyc
Normal file
BIN
moto/ec2/responses.pyc
Normal file
Binary file not shown.
5
moto/ec2/urls.py
Normal file
5
moto/ec2/urls.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .responses import instances
|
||||||
|
|
||||||
|
urls = {
|
||||||
|
'/': instances,
|
||||||
|
}
|
BIN
moto/ec2/urls.pyc
Normal file
BIN
moto/ec2/urls.pyc
Normal file
Binary file not shown.
15
moto/ec2/utils.py
Normal file
15
moto/ec2/utils.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
def random_id(prefix=''):
|
||||||
|
size = 8
|
||||||
|
chars = range(10) + ['a', 'b', 'c', 'd', 'e', 'f']
|
||||||
|
|
||||||
|
instance_tag = ''.join(unicode(random.choice(chars)) for x in range(size))
|
||||||
|
return '{}-{}'.format(prefix, instance_tag)
|
||||||
|
|
||||||
|
def random_instance_id():
|
||||||
|
return random_id(prefix='i')
|
||||||
|
|
||||||
|
def random_reservation_id():
|
||||||
|
return random_id(prefix='r')
|
BIN
moto/ec2/utils.pyc
Normal file
BIN
moto/ec2/utils.pyc
Normal file
Binary file not shown.
2
moto/s3/__init__.py
Normal file
2
moto/s3/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .models import s3_backend
|
||||||
|
mock_s3 = s3_backend.decorator
|
BIN
moto/s3/__init__.pyc
Normal file
BIN
moto/s3/__init__.pyc
Normal file
Binary file not shown.
57
moto/s3/models.py
Normal file
57
moto/s3/models.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# from boto.s3.bucket import Bucket
|
||||||
|
# from boto.s3.key import Key
|
||||||
|
import md5
|
||||||
|
|
||||||
|
from moto.core import BaseBackend
|
||||||
|
|
||||||
|
|
||||||
|
class FakeKey(object):
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def etag(self):
|
||||||
|
value_md5 = md5.new()
|
||||||
|
value_md5.update(self.value)
|
||||||
|
return '"{0}"'.format(value_md5.hexdigest())
|
||||||
|
|
||||||
|
class FakeBucket(object):
|
||||||
|
def __init__(self, name):
|
||||||
|
self.name = name
|
||||||
|
self.keys = []
|
||||||
|
|
||||||
|
|
||||||
|
class MockS3(BaseBackend):
|
||||||
|
base_url = "https://(.+).s3.amazonaws.com"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.buckets = {}
|
||||||
|
|
||||||
|
def create_bucket(self, bucket_name):
|
||||||
|
new_bucket = FakeBucket(name=bucket_name)
|
||||||
|
self.buckets[bucket_name] = new_bucket
|
||||||
|
return new_bucket
|
||||||
|
|
||||||
|
def get_bucket(self, bucket_name):
|
||||||
|
return self.buckets.get(bucket_name)
|
||||||
|
|
||||||
|
def set_key(self, bucket_name, key_name, value):
|
||||||
|
bucket = self.buckets[bucket_name]
|
||||||
|
new_key = FakeKey(name=key_name, value=value)
|
||||||
|
bucket.keys.append(new_key)
|
||||||
|
|
||||||
|
return new_key
|
||||||
|
|
||||||
|
def get_key(self, bucket_name, key_name):
|
||||||
|
bucket = self.buckets[bucket_name]
|
||||||
|
found_key = None
|
||||||
|
for key in bucket.keys:
|
||||||
|
if key.name == key_name:
|
||||||
|
found_key = key
|
||||||
|
break
|
||||||
|
|
||||||
|
return found_key
|
||||||
|
|
||||||
|
|
||||||
|
s3_backend = MockS3()
|
BIN
moto/s3/models.pyc
Normal file
BIN
moto/s3/models.pyc
Normal file
Binary file not shown.
72
moto/s3/responses.py
Normal file
72
moto/s3/responses.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
from .models import s3_backend
|
||||||
|
|
||||||
|
def bucket_response(uri, body, headers):
|
||||||
|
hostname = uri.hostname
|
||||||
|
bucket_name = hostname.replace(".s3.amazonaws.com", "")
|
||||||
|
|
||||||
|
if uri.method == 'GET':
|
||||||
|
bucket = s3_backend.get_bucket(bucket_name)
|
||||||
|
if bucket:
|
||||||
|
template = Template(S3_BUCKET_GET_RESPONSE)
|
||||||
|
return template.render(bucket=bucket)
|
||||||
|
else:
|
||||||
|
return "", dict(status=404)
|
||||||
|
else:
|
||||||
|
new_bucket = s3_backend.create_bucket(bucket_name)
|
||||||
|
template = Template(S3_BUCKET_CREATE_RESPONSE)
|
||||||
|
return template.render(bucket=new_bucket)
|
||||||
|
|
||||||
|
|
||||||
|
def key_response(uri_info, body, headers):
|
||||||
|
|
||||||
|
key_name = uri_info.path.lstrip('/')
|
||||||
|
hostname = uri_info.hostname
|
||||||
|
bucket_name = hostname.replace(".s3.amazonaws.com", "")
|
||||||
|
|
||||||
|
if uri_info.method == 'GET':
|
||||||
|
key = s3_backend.get_key(bucket_name, key_name)
|
||||||
|
if key:
|
||||||
|
return key.value
|
||||||
|
else:
|
||||||
|
return "", dict(status=404)
|
||||||
|
|
||||||
|
if uri_info.method == 'PUT':
|
||||||
|
if body:
|
||||||
|
new_key = s3_backend.set_key(bucket_name, key_name, body)
|
||||||
|
return S3_OBJECT_RESPONSE, dict(etag=new_key.etag)
|
||||||
|
key = s3_backend.get_key(bucket_name, key_name)
|
||||||
|
if key:
|
||||||
|
return "", dict(etag=key.etag)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
elif uri_info.method == 'HEAD':
|
||||||
|
key = s3_backend.get_key(bucket_name, key_name)
|
||||||
|
return S3_OBJECT_RESPONSE, dict(etag=key.etag)
|
||||||
|
else:
|
||||||
|
import pdb;pdb.set_trace()
|
||||||
|
|
||||||
|
|
||||||
|
S3_BUCKET_GET_RESPONSE = """<ListBucket xmlns="http://doc.s3.amazonaws.com/2006-03-01">\
|
||||||
|
<Bucket>{{ bucket.name }}</Bucket>\
|
||||||
|
<Prefix>notes/</Prefix>\
|
||||||
|
<Delimiter>/</Delimiter>\
|
||||||
|
<MaxKeys>1000</MaxKeys>\
|
||||||
|
<AWSAccessKeyId>AKIAIOSFODNN7EXAMPLE</AWSAccessKeyId>\
|
||||||
|
<Timestamp>2006-03-01T12:00:00.183Z</Timestamp>\
|
||||||
|
<Signature>Iuyz3d3P0aTou39dzbqaEXAMPLE=</Signature>\
|
||||||
|
</ListBucket>"""
|
||||||
|
|
||||||
|
S3_BUCKET_CREATE_RESPONSE = """<CreateBucketResponse xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||||
|
<CreateBucketResponse>
|
||||||
|
<Bucket>{{ bucket.name }}</Bucket>
|
||||||
|
</CreateBucketResponse>
|
||||||
|
</CreateBucketResponse>"""
|
||||||
|
|
||||||
|
S3_OBJECT_RESPONSE = """<PutObjectResponse xmlns="http://s3.amazonaws.com/doc/2006-03-01">
|
||||||
|
<PutObjectResponse>
|
||||||
|
<ETag>"asdlfkdalsjfsalfkjsadlfjsdjkk"</ETag>
|
||||||
|
<LastModified>2006-03-01T12:00:00.183Z</LastModified>
|
||||||
|
</PutObjectResponse>
|
||||||
|
</PutObjectResponse>"""
|
BIN
moto/s3/responses.pyc
Normal file
BIN
moto/s3/responses.pyc
Normal file
Binary file not shown.
6
moto/s3/urls.py
Normal file
6
moto/s3/urls.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from .responses import bucket_response, key_response
|
||||||
|
|
||||||
|
urls = {
|
||||||
|
'/$': bucket_response,
|
||||||
|
'/(.+)': key_response,
|
||||||
|
}
|
BIN
moto/s3/urls.pyc
Normal file
BIN
moto/s3/urls.pyc
Normal file
Binary file not shown.
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
boto
|
||||||
|
httpretty
|
||||||
|
Jinja2
|
||||||
|
mock
|
||||||
|
nose
|
||||||
|
sure
|
14
setup.py
Normal file
14
setup.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='moto',
|
||||||
|
version='0.0.1',
|
||||||
|
description='Moto is a library that allows your python tests to easily mock out the boto library',
|
||||||
|
author='Steve Pulec',
|
||||||
|
author_email='spulec@gmail',
|
||||||
|
url='https://github.com/spulec/moto',
|
||||||
|
packages=find_packages()
|
||||||
|
)
|
BIN
tests/__init__.pyc
Normal file
BIN
tests/__init__.pyc
Normal file
Binary file not shown.
29
tests/test_ec2/test_ec2.py
Normal file
29
tests/test_ec2/test_ec2.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import boto
|
||||||
|
from boto.ec2.instance import Reservation
|
||||||
|
from sure import expect
|
||||||
|
|
||||||
|
from moto import mock_ec2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_instance_launch_and_terminate():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
reservation = conn.run_instances('<ami-image-id>')
|
||||||
|
reservation.should.be.a(Reservation)
|
||||||
|
reservation.instances.should.have.length_of(1)
|
||||||
|
instance = reservation.instances[0]
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances()
|
||||||
|
reservations.should.have.length_of(1)
|
||||||
|
reservations[0].id.should.equal(reservation.id)
|
||||||
|
instances = reservations[0].instances
|
||||||
|
instances.should.have.length_of(1)
|
||||||
|
instances[0].id.should.equal(instance.id)
|
||||||
|
instances[0].state.should.equal('pending')
|
||||||
|
|
||||||
|
conn.terminate_instances(instances[0].id)
|
||||||
|
|
||||||
|
reservations = conn.get_all_instances()
|
||||||
|
instance = reservations[0].instances[0]
|
||||||
|
instance.state.should.equal('shutting-down')
|
BIN
tests/test_ec2/test_ec2.pyc
Normal file
BIN
tests/test_ec2/test_ec2.pyc
Normal file
Binary file not shown.
30
tests/test_s3/test_s3.py
Normal file
30
tests/test_s3/test_s3.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import boto
|
||||||
|
from boto.s3.key import Key
|
||||||
|
|
||||||
|
from moto import mock_s3
|
||||||
|
|
||||||
|
|
||||||
|
class MyModel(object):
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
conn = boto.connect_s3('the_key', 'the_secret')
|
||||||
|
bucket = conn.get_bucket('mybucket')
|
||||||
|
k = Key(bucket)
|
||||||
|
k.key = self.name
|
||||||
|
k.set_contents_from_string(self.value)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_my_model_save():
|
||||||
|
# Create Bucket so that test can run
|
||||||
|
conn = boto.connect_s3('the_key', 'the_secret')
|
||||||
|
conn.create_bucket('mybucket')
|
||||||
|
####################################
|
||||||
|
|
||||||
|
model_instance = MyModel('steve', 'is awesome')
|
||||||
|
model_instance.save()
|
||||||
|
|
||||||
|
assert conn.get_bucket('mybucket').get_key('steve').get_contents_as_string() == 'is awesome'
|
BIN
tests/test_s3/test_s3.pyc
Normal file
BIN
tests/test_s3/test_s3.pyc
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user