Merge pull request #180 from DreadPirateShawn/AMIsModifyAndDescribe
AMIs: Added ModifyImageAttribute and DescribeImages filtering.
This commit is contained in:
commit
cfda83dcba
@ -119,6 +119,14 @@ class InvalidAMIAttributeItemValueError(EC2ClientError):
|
|||||||
.format(value, attribute))
|
.format(value, attribute))
|
||||||
|
|
||||||
|
|
||||||
|
class MalformedAMIIdError(EC2ClientError):
|
||||||
|
def __init__(self, ami_id):
|
||||||
|
super(MalformedAMIIdError, self).__init__(
|
||||||
|
"InvalidAMIID.Malformed",
|
||||||
|
"Invalid id: \"{0}\" (expecting \"ami-...\")"
|
||||||
|
.format(ami_id))
|
||||||
|
|
||||||
|
|
||||||
class InvalidSnapshotIdError(EC2ClientError):
|
class InvalidSnapshotIdError(EC2ClientError):
|
||||||
def __init__(self, snapshot_id):
|
def __init__(self, snapshot_id):
|
||||||
super(InvalidSnapshotIdError, self).__init__(
|
super(InvalidSnapshotIdError, self).__init__(
|
||||||
|
@ -29,6 +29,7 @@ from .exceptions import (
|
|||||||
InvalidSecurityGroupNotFoundError,
|
InvalidSecurityGroupNotFoundError,
|
||||||
InvalidPermissionNotFoundError,
|
InvalidPermissionNotFoundError,
|
||||||
InvalidInstanceIdError,
|
InvalidInstanceIdError,
|
||||||
|
MalformedAMIIdError,
|
||||||
InvalidAMIIdError,
|
InvalidAMIIdError,
|
||||||
InvalidAMIAttributeItemValueError,
|
InvalidAMIAttributeItemValueError,
|
||||||
InvalidSnapshotIdError,
|
InvalidSnapshotIdError,
|
||||||
@ -90,6 +91,14 @@ class Instance(BotoInstance, TaggedEC2Instance):
|
|||||||
self.block_device_mapping = BlockDeviceMapping()
|
self.block_device_mapping = BlockDeviceMapping()
|
||||||
self.block_device_mapping['/dev/sda1'] = BlockDeviceType()
|
self.block_device_mapping['/dev/sda1'] = BlockDeviceType()
|
||||||
|
|
||||||
|
amis = ec2_backend.describe_images(filters={'image-id': image_id})
|
||||||
|
ami = amis[0] if amis else None
|
||||||
|
|
||||||
|
self.platform = ami.platform if ami else None
|
||||||
|
self.virtualization_type = ami.virtualization_type if ami else 'paravirtual'
|
||||||
|
self.architecture = ami.architecture if ami else 'x86_64'
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||||
properties = cloudformation_json['Properties']
|
properties = cloudformation_json['Properties']
|
||||||
@ -328,13 +337,34 @@ class TagBackend(object):
|
|||||||
class Ami(TaggedEC2Instance):
|
class Ami(TaggedEC2Instance):
|
||||||
def __init__(self, ami_id, instance, name, description):
|
def __init__(self, ami_id, instance, name, description):
|
||||||
self.id = ami_id
|
self.id = ami_id
|
||||||
|
self.state = "available"
|
||||||
|
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.instance_id = instance.id
|
self.instance_id = instance.id
|
||||||
|
self.virtualization_type = instance.virtualization_type
|
||||||
|
self.architecture = instance.architecture
|
||||||
|
self.kernel_id = instance.kernel
|
||||||
|
self.platform = instance.platform
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.launch_permission_groups = set()
|
||||||
|
|
||||||
self.virtualization_type = instance.virtualization_type
|
# AWS auto-creates these, we should reflect the same.
|
||||||
self.kernel_id = instance.kernel
|
volume = ec2_backend.create_volume(15, "us-east-1a")
|
||||||
|
self.ebs_snapshot = ec2_backend.create_snapshot(volume.id, "Auto-created snapshot for AMI %s" % self.id)
|
||||||
|
|
||||||
|
def get_filter_value(self, filter_name):
|
||||||
|
if filter_name == 'virtualization-type':
|
||||||
|
return self.virtualization_type
|
||||||
|
elif filter_name == 'kernel-id':
|
||||||
|
return self.kernel_id
|
||||||
|
elif filter_name in ['architecture', 'platform']:
|
||||||
|
return getattr(self,filter_name)
|
||||||
|
elif filter_name == 'image-id':
|
||||||
|
return self.id
|
||||||
|
else:
|
||||||
|
ec2_backend.raise_not_implemented_error("The filter '{0}' for DescribeImages".format(filter_name))
|
||||||
|
|
||||||
|
|
||||||
class AmiBackend(object):
|
class AmiBackend(object):
|
||||||
@ -350,11 +380,19 @@ class AmiBackend(object):
|
|||||||
self.amis[ami_id] = ami
|
self.amis[ami_id] = ami
|
||||||
return ami
|
return ami
|
||||||
|
|
||||||
def describe_images(self, ami_ids=()):
|
def describe_images(self, ami_ids=(), filters=None):
|
||||||
|
if filters:
|
||||||
|
images = self.amis.values()
|
||||||
|
for (_filter, _filter_value) in filters.iteritems():
|
||||||
|
images = [ ami for ami in images if ami.get_filter_value(_filter) in _filter_value ]
|
||||||
|
return images
|
||||||
|
else:
|
||||||
images = []
|
images = []
|
||||||
for ami_id in ami_ids:
|
for ami_id in ami_ids:
|
||||||
if ami_id in self.amis:
|
if ami_id in self.amis:
|
||||||
images.append(self.amis[ami_id])
|
images.append(self.amis[ami_id])
|
||||||
|
elif not ami_id.startswith("ami-"):
|
||||||
|
raise MalformedAMIIdError(ami_id)
|
||||||
else:
|
else:
|
||||||
raise InvalidAMIIdError(ami_id)
|
raise InvalidAMIIdError(ami_id)
|
||||||
return images or self.amis.values()
|
return images or self.amis.values()
|
||||||
@ -365,6 +403,30 @@ class AmiBackend(object):
|
|||||||
return True
|
return True
|
||||||
raise InvalidAMIIdError(ami_id)
|
raise InvalidAMIIdError(ami_id)
|
||||||
|
|
||||||
|
def get_launch_permission_groups(self, ami_id):
|
||||||
|
ami = self.describe_images(ami_ids=[ami_id])[0]
|
||||||
|
return ami.launch_permission_groups
|
||||||
|
|
||||||
|
def add_launch_permission(self, ami_id, user_id=None, group=None):
|
||||||
|
if user_id:
|
||||||
|
ec2_backend.raise_not_implemented_error("The UserId parameter for ModifyImageAttribute")
|
||||||
|
|
||||||
|
if group != 'all':
|
||||||
|
raise InvalidAMIAttributeItemValueError("UserGroup", group)
|
||||||
|
ami = self.describe_images(ami_ids=[ami_id])[0]
|
||||||
|
ami.launch_permission_groups.add(group)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remove_launch_permission(self, ami_id, user_id=None, group=None):
|
||||||
|
if user_id:
|
||||||
|
ec2_backend.raise_not_implemented_error("The UserId parameter for ModifyImageAttribute")
|
||||||
|
|
||||||
|
if group != 'all':
|
||||||
|
raise InvalidAMIAttributeItemValueError("UserGroup", group)
|
||||||
|
ami = self.describe_images(ami_ids=[ami_id])[0]
|
||||||
|
ami.launch_permission_groups.discard(group)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Region(object):
|
class Region(object):
|
||||||
def __init__(self, name, endpoint):
|
def __init__(self, name, endpoint):
|
||||||
|
@ -3,7 +3,7 @@ from jinja2 import Template
|
|||||||
|
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from moto.ec2.models import ec2_backend
|
from moto.ec2.models import ec2_backend
|
||||||
from moto.ec2.utils import instance_ids_from_querystring, image_ids_from_querystring
|
from moto.ec2.utils import instance_ids_from_querystring, image_ids_from_querystring, filters_from_querystring
|
||||||
|
|
||||||
|
|
||||||
class AmisResponse(BaseResponse):
|
class AmisResponse(BaseResponse):
|
||||||
@ -25,17 +25,29 @@ class AmisResponse(BaseResponse):
|
|||||||
template = Template(DEREGISTER_IMAGE_RESPONSE)
|
template = Template(DEREGISTER_IMAGE_RESPONSE)
|
||||||
return template.render(success=str(success).lower())
|
return template.render(success=str(success).lower())
|
||||||
|
|
||||||
def describe_image_attribute(self):
|
|
||||||
raise NotImplementedError('AMIs.describe_image_attribute is not yet implemented')
|
|
||||||
|
|
||||||
def describe_images(self):
|
def describe_images(self):
|
||||||
ami_ids = image_ids_from_querystring(self.querystring)
|
ami_ids = image_ids_from_querystring(self.querystring)
|
||||||
images = ec2_backend.describe_images(ami_ids=ami_ids)
|
filters = filters_from_querystring(self.querystring)
|
||||||
|
images = ec2_backend.describe_images(ami_ids=ami_ids, filters=filters)
|
||||||
template = Template(DESCRIBE_IMAGES_RESPONSE)
|
template = Template(DESCRIBE_IMAGES_RESPONSE)
|
||||||
return template.render(images=images)
|
return template.render(images=images)
|
||||||
|
|
||||||
|
def describe_image_attribute(self):
|
||||||
|
ami_id = self.querystring.get('ImageId')[0]
|
||||||
|
groups = ec2_backend.get_launch_permission_groups(ami_id)
|
||||||
|
template = Template(DESCRIBE_IMAGE_ATTRIBUTES_RESPONSE)
|
||||||
|
return template.render(ami_id=ami_id, groups=groups)
|
||||||
|
|
||||||
def modify_image_attribute(self):
|
def modify_image_attribute(self):
|
||||||
raise NotImplementedError('AMIs.modify_image_attribute is not yet implemented')
|
ami_id = self.querystring.get('ImageId')[0]
|
||||||
|
operation_type = self.querystring.get('OperationType')[0]
|
||||||
|
group = self.querystring.get('UserGroup.1', [None])[0]
|
||||||
|
user_id = self.querystring.get('UserId.1', [None])[0]
|
||||||
|
if (operation_type == 'add'):
|
||||||
|
ec2_backend.add_launch_permission(ami_id, user_id=user_id, group=group)
|
||||||
|
elif (operation_type == 'remove'):
|
||||||
|
ec2_backend.remove_launch_permission(ami_id, user_id=user_id, group=group)
|
||||||
|
return MODIFY_IMAGE_ATTRIBUTE_RESPONSE
|
||||||
|
|
||||||
def register_image(self):
|
def register_image(self):
|
||||||
raise NotImplementedError('AMIs.register_image is not yet implemented')
|
raise NotImplementedError('AMIs.register_image is not yet implemented')
|
||||||
@ -57,15 +69,18 @@ DESCRIBE_IMAGES_RESPONSE = """<DescribeImagesResponse xmlns="http://ec2.amazonaw
|
|||||||
<item>
|
<item>
|
||||||
<imageId>{{ image.id }}</imageId>
|
<imageId>{{ image.id }}</imageId>
|
||||||
<imageLocation>amazon/getting-started</imageLocation>
|
<imageLocation>amazon/getting-started</imageLocation>
|
||||||
<imageState>available</imageState>
|
<imageState>{{ image.state }}</imageState>
|
||||||
<imageOwnerId>111122223333</imageOwnerId>
|
<imageOwnerId>111122223333</imageOwnerId>
|
||||||
<isPublic>true</isPublic>
|
<isPublic>true</isPublic>
|
||||||
<architecture>i386</architecture>
|
<architecture>{{ image.architecture }}</architecture>
|
||||||
<imageType>machine</imageType>
|
<imageType>machine</imageType>
|
||||||
<kernelId>{{ image.kernel_id }}</kernelId>
|
<kernelId>{{ image.kernel_id }}</kernelId>
|
||||||
<ramdiskId>ari-1a2b3c4d</ramdiskId>
|
<ramdiskId>ari-1a2b3c4d</ramdiskId>
|
||||||
<imageOwnerAlias>amazon</imageOwnerAlias>
|
<imageOwnerAlias>amazon</imageOwnerAlias>
|
||||||
<name>{{ image.name }}</name>
|
<name>{{ image.name }}</name>
|
||||||
|
{% if image.platform %}
|
||||||
|
<platform>{{ image.platform }}</platform>
|
||||||
|
{% endif %}
|
||||||
<description>{{ image.description }}</description>
|
<description>{{ image.description }}</description>
|
||||||
<rootDeviceType>ebs</rootDeviceType>
|
<rootDeviceType>ebs</rootDeviceType>
|
||||||
<rootDeviceName>/dev/sda</rootDeviceName>
|
<rootDeviceName>/dev/sda</rootDeviceName>
|
||||||
@ -73,7 +88,7 @@ DESCRIBE_IMAGES_RESPONSE = """<DescribeImagesResponse xmlns="http://ec2.amazonaw
|
|||||||
<item>
|
<item>
|
||||||
<deviceName>/dev/sda1</deviceName>
|
<deviceName>/dev/sda1</deviceName>
|
||||||
<ebs>
|
<ebs>
|
||||||
<snapshotId>snap-1a2b3c4d</snapshotId>
|
<snapshotId>{{ image.ebs_snapshot.id }}</snapshotId>
|
||||||
<volumeSize>15</volumeSize>
|
<volumeSize>15</volumeSize>
|
||||||
<deleteOnTermination>false</deleteOnTermination>
|
<deleteOnTermination>false</deleteOnTermination>
|
||||||
<volumeType>standard</volumeType>
|
<volumeType>standard</volumeType>
|
||||||
@ -109,3 +124,27 @@ DEREGISTER_IMAGE_RESPONSE = """<DeregisterImageResponse xmlns="http://ec2.amazon
|
|||||||
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
<return>{{ success }}</return>
|
<return>{{ success }}</return>
|
||||||
</DeregisterImageResponse>"""
|
</DeregisterImageResponse>"""
|
||||||
|
|
||||||
|
DESCRIBE_IMAGE_ATTRIBUTES_RESPONSE = """
|
||||||
|
<DescribeImageAttributeResponse xmlns="http://ec2.amazonaws.com/doc/2013-08-15/">
|
||||||
|
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
|
||||||
|
<imageId>{{ ami_id }}</imageId>
|
||||||
|
{% if not groups %}
|
||||||
|
<launchPermission/>
|
||||||
|
{% endif %}
|
||||||
|
{% if groups %}
|
||||||
|
<launchPermission>
|
||||||
|
{% for group in groups %}
|
||||||
|
<item>
|
||||||
|
<group>{{ group }}</group>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</launchPermission>
|
||||||
|
{% endif %}
|
||||||
|
</DescribeImageAttributeResponse>"""
|
||||||
|
|
||||||
|
MODIFY_IMAGE_ATTRIBUTE_RESPONSE = """
|
||||||
|
<ModifyImageAttributeResponse xmlns="http://ec2.amazonaws.com/doc/2013-08-15/">
|
||||||
|
<return>true</return>
|
||||||
|
</ModifyImageAttributeResponse>
|
||||||
|
"""
|
||||||
|
@ -199,7 +199,12 @@ EC2_RUN_INSTANCES = """<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc
|
|||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</groupSet>
|
</groupSet>
|
||||||
<virtualizationType>paravirtual</virtualizationType>
|
{% if instance.platform %}
|
||||||
|
<platform>{{ instance.platform }}</platform>
|
||||||
|
{% endif %}
|
||||||
|
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
|
||||||
|
<architecture>{{ instance.architecture }}</architecture>
|
||||||
|
<kernelId>{{ instance.kernel }}</kernelId>
|
||||||
<clientToken/>
|
<clientToken/>
|
||||||
<hypervisor>xen</hypervisor>
|
<hypervisor>xen</hypervisor>
|
||||||
<ebsOptimized>false</ebsOptimized>
|
<ebsOptimized>false</ebsOptimized>
|
||||||
@ -238,7 +243,9 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
<groupName/>
|
<groupName/>
|
||||||
<tenancy>default</tenancy>
|
<tenancy>default</tenancy>
|
||||||
</placement>
|
</placement>
|
||||||
<platform>windows</platform>
|
{% if instance.platform %}
|
||||||
|
<platform>{{ instance.platform }}</platform>
|
||||||
|
{% endif %}
|
||||||
<monitoring>
|
<monitoring>
|
||||||
<state>disabled</state>
|
<state>disabled</state>
|
||||||
</monitoring>
|
</monitoring>
|
||||||
@ -255,11 +262,12 @@ EC2_DESCRIBE_INSTANCES = """<DescribeInstancesResponse xmlns='http://ec2.amazona
|
|||||||
</item>
|
</item>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</groupSet>
|
</groupSet>
|
||||||
<architecture>x86_64</architecture>
|
<architecture>{{ instance.architecture }}</architecture>
|
||||||
|
<kernelId>{{ instance.kernel }}</kernelId>
|
||||||
<rootDeviceType>ebs</rootDeviceType>
|
<rootDeviceType>ebs</rootDeviceType>
|
||||||
<rootDeviceName>/dev/sda1</rootDeviceName>
|
<rootDeviceName>/dev/sda1</rootDeviceName>
|
||||||
<blockDeviceMapping />
|
<blockDeviceMapping />
|
||||||
<virtualizationType>hvm</virtualizationType>
|
<virtualizationType>{{ instance.virtualization_type }}</virtualizationType>
|
||||||
<clientToken>ABCDE1234567890123</clientToken>
|
<clientToken>ABCDE1234567890123</clientToken>
|
||||||
<tagSet>
|
<tagSet>
|
||||||
{% for tag in instance.get_tags() %}
|
{% for tag in instance.get_tags() %}
|
||||||
|
@ -16,16 +16,36 @@ def test_ami_create_and_delete():
|
|||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
reservation = conn.run_instances('ami-1234abcd')
|
reservation = conn.run_instances('ami-1234abcd')
|
||||||
instance = reservation.instances[0]
|
instance = reservation.instances[0]
|
||||||
image = conn.create_image(instance.id, "test-ami", "this is a test ami")
|
image_id = conn.create_image(instance.id, "test-ami", "this is a test ami")
|
||||||
|
|
||||||
all_images = conn.get_all_images()
|
all_images = conn.get_all_images()
|
||||||
all_images[0].id.should.equal(image)
|
image = all_images[0]
|
||||||
|
|
||||||
success = conn.deregister_image(image)
|
image.id.should.equal(image_id)
|
||||||
|
image.virtualization_type.should.equal(instance.virtualization_type)
|
||||||
|
image.architecture.should.equal(instance.architecture)
|
||||||
|
image.kernel_id.should.equal(instance.kernel)
|
||||||
|
image.platform.should.equal(instance.platform)
|
||||||
|
|
||||||
|
# Validate auto-created volume and snapshot
|
||||||
|
volumes = conn.get_all_volumes()
|
||||||
|
volumes.should.have.length_of(1)
|
||||||
|
volume = volumes[0]
|
||||||
|
|
||||||
|
snapshots = conn.get_all_snapshots()
|
||||||
|
snapshots.should.have.length_of(1)
|
||||||
|
snapshot = snapshots[0]
|
||||||
|
|
||||||
|
image.block_device_mapping.current_value.snapshot_id.should.equal(snapshot.id)
|
||||||
|
snapshot.description.should.equal("Auto-created snapshot for AMI {0}".format(image.id))
|
||||||
|
snapshot.volume_id.should.equal(volume.id)
|
||||||
|
|
||||||
|
# Deregister
|
||||||
|
success = conn.deregister_image(image_id)
|
||||||
success.should.be.true
|
success.should.be.true
|
||||||
|
|
||||||
with assert_raises(EC2ResponseError) as cm:
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
conn.deregister_image(image)
|
conn.deregister_image(image_id)
|
||||||
cm.exception.code.should.equal('InvalidAMIID.NotFound')
|
cm.exception.code.should.equal('InvalidAMIID.NotFound')
|
||||||
cm.exception.status.should.equal(400)
|
cm.exception.status.should.equal(400)
|
||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
@ -75,6 +95,44 @@ def test_ami_pulls_attributes_from_instance():
|
|||||||
image.kernel_id.should.equal('test-kernel')
|
image.kernel_id.should.equal('test-kernel')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_ami_filters():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
|
reservationA = conn.run_instances('ami-1234abcd')
|
||||||
|
instanceA = reservationA.instances[0]
|
||||||
|
instanceA.modify_attribute("architecture", "i386")
|
||||||
|
instanceA.modify_attribute("kernel", "k-1234abcd")
|
||||||
|
instanceA.modify_attribute("platform", "windows")
|
||||||
|
instanceA.modify_attribute("virtualization_type", "hvm")
|
||||||
|
imageA_id = conn.create_image(instanceA.id, "test-ami-A", "this is a test ami")
|
||||||
|
imageA = conn.get_image(imageA_id)
|
||||||
|
|
||||||
|
reservationB = conn.run_instances('ami-abcd1234')
|
||||||
|
instanceB = reservationB.instances[0]
|
||||||
|
instanceB.modify_attribute("architecture", "x86_64")
|
||||||
|
instanceB.modify_attribute("kernel", "k-abcd1234")
|
||||||
|
instanceB.modify_attribute("platform", "linux")
|
||||||
|
instanceB.modify_attribute("virtualization_type", "paravirtual")
|
||||||
|
imageB_id = conn.create_image(instanceB.id, "test-ami-B", "this is a test ami")
|
||||||
|
imageB = conn.get_image(imageB_id)
|
||||||
|
|
||||||
|
amis_by_architecture = conn.get_all_images(filters={'architecture': 'x86_64'})
|
||||||
|
set([ami.id for ami in amis_by_architecture]).should.equal(set([imageB.id]))
|
||||||
|
|
||||||
|
amis_by_kernel = conn.get_all_images(filters={'kernel-id': 'k-abcd1234'})
|
||||||
|
set([ami.id for ami in amis_by_kernel]).should.equal(set([imageB.id]))
|
||||||
|
|
||||||
|
amis_by_virtualization = conn.get_all_images(filters={'virtualization-type': 'paravirtual'})
|
||||||
|
set([ami.id for ami in amis_by_virtualization]).should.equal(set([imageB.id]))
|
||||||
|
|
||||||
|
amis_by_platform = conn.get_all_images(filters={'platform': 'windows'})
|
||||||
|
set([ami.id for ami in amis_by_platform]).should.equal(set([imageA.id]))
|
||||||
|
|
||||||
|
amis_by_id = conn.get_all_images(filters={'image-id': imageA.id})
|
||||||
|
set([ami.id for ami in amis_by_id]).should.equal(set([imageA.id]))
|
||||||
|
|
||||||
|
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
def test_getting_missing_ami():
|
def test_getting_missing_ami():
|
||||||
conn = boto.connect_ec2('the_key', 'the_secret')
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
@ -85,3 +143,97 @@ def test_getting_missing_ami():
|
|||||||
cm.exception.status.should.equal(400)
|
cm.exception.status.should.equal(400)
|
||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_getting_malformed_ami():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.get_image('foo-missing')
|
||||||
|
cm.exception.code.should.equal('InvalidAMIID.Malformed')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_ami_attribute():
|
||||||
|
conn = boto.connect_ec2('the_key', 'the_secret')
|
||||||
|
reservation = conn.run_instances('ami-1234abcd')
|
||||||
|
instance = reservation.instances[0]
|
||||||
|
image_id = conn.create_image(instance.id, "test-ami", "this is a test ami")
|
||||||
|
image = conn.get_image(image_id)
|
||||||
|
|
||||||
|
# Baseline
|
||||||
|
attributes = conn.get_image_attribute(image.id, attribute='launchPermission')
|
||||||
|
attributes.name.should.equal('launch_permission')
|
||||||
|
attributes.attrs.should.have.length_of(0)
|
||||||
|
|
||||||
|
ADD_GROUP_ARGS = {'image_id': image.id,
|
||||||
|
'attribute': 'launchPermission',
|
||||||
|
'operation': 'add',
|
||||||
|
'groups': 'all'}
|
||||||
|
|
||||||
|
REMOVE_GROUP_ARGS = {'image_id': image.id,
|
||||||
|
'attribute': 'launchPermission',
|
||||||
|
'operation': 'remove',
|
||||||
|
'groups': 'all'}
|
||||||
|
|
||||||
|
# Add 'all' group and confirm
|
||||||
|
conn.modify_image_attribute(**ADD_GROUP_ARGS)
|
||||||
|
|
||||||
|
attributes = conn.get_image_attribute(image.id, attribute='launchPermission')
|
||||||
|
attributes.attrs['groups'].should.have.length_of(1)
|
||||||
|
attributes.attrs['groups'].should.equal(['all'])
|
||||||
|
|
||||||
|
# Add is idempotent
|
||||||
|
conn.modify_image_attribute.when.called_with(**ADD_GROUP_ARGS).should_not.throw(EC2ResponseError)
|
||||||
|
|
||||||
|
# Remove 'all' group and confirm
|
||||||
|
conn.modify_image_attribute(**REMOVE_GROUP_ARGS)
|
||||||
|
|
||||||
|
attributes = conn.get_image_attribute(image.id, attribute='launchPermission')
|
||||||
|
attributes.attrs.should.have.length_of(0)
|
||||||
|
|
||||||
|
# Remove is idempotent
|
||||||
|
conn.modify_image_attribute.when.called_with(**REMOVE_GROUP_ARGS).should_not.throw(EC2ResponseError)
|
||||||
|
|
||||||
|
# Error: Add with group != 'all'
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.modify_image_attribute(image.id,
|
||||||
|
attribute='launchPermission',
|
||||||
|
operation='add',
|
||||||
|
groups='everyone')
|
||||||
|
cm.exception.code.should.equal('InvalidAMIAttributeItemValue')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
# Error: Add with invalid image ID
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.modify_image_attribute("ami-abcd1234",
|
||||||
|
attribute='launchPermission',
|
||||||
|
operation='add',
|
||||||
|
groups='all')
|
||||||
|
cm.exception.code.should.equal('InvalidAMIID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
# Error: Remove with invalid image ID
|
||||||
|
with assert_raises(EC2ResponseError) as cm:
|
||||||
|
conn.modify_image_attribute("ami-abcd1234",
|
||||||
|
attribute='launchPermission',
|
||||||
|
operation='remove',
|
||||||
|
groups='all')
|
||||||
|
cm.exception.code.should.equal('InvalidAMIID.NotFound')
|
||||||
|
cm.exception.status.should.equal(400)
|
||||||
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
# Error: Add or remove with user ID instead of group
|
||||||
|
conn.modify_image_attribute.when.called_with(image.id,
|
||||||
|
attribute='launchPermission',
|
||||||
|
operation='add',
|
||||||
|
user_ids=['user']).should.throw(NotImplementedError)
|
||||||
|
conn.modify_image_attribute.when.called_with(image.id,
|
||||||
|
attribute='launchPermission',
|
||||||
|
operation='remove',
|
||||||
|
user_ids=['user']).should.throw(NotImplementedError)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user