Skip to content

Commit

Permalink
Merge branch 'mbarrien-ebs-encrypted' into develop
Browse files Browse the repository at this point in the history
Adds support for EBS encryption. Fixes boto#2282.

Conflicts:
	tests/unit/ec2/test_snapshot.py
  • Loading branch information
danielgtaylor committed May 29, 2014
2 parents 37aaa0f + 8976fea commit d85a449
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 9 deletions.
12 changes: 9 additions & 3 deletions boto/ec2/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@

class EC2Connection(AWSQueryConnection):

APIVersion = boto.config.get('Boto', 'ec2_version', '2013-10-15')
APIVersion = boto.config.get('Boto', 'ec2_version', '2014-05-01')
DefaultRegionName = boto.config.get('Boto', 'ec2_region_name', 'us-east-1')
DefaultRegionEndpoint = boto.config.get('Boto', 'ec2_region_endpoint',
'ec2.us-east-1.amazonaws.com')
Expand Down Expand Up @@ -2231,8 +2231,8 @@ def modify_volume_attribute(self, volume_id, attribute, new_value,
params['DryRun'] = 'true'
return self.get_status('ModifyVolumeAttribute', params, verb='POST')

def create_volume(self, size, zone, snapshot=None,
volume_type=None, iops=None, dry_run=False):
def create_volume(self, size, zone, snapshot=None, volume_type=None,
iops=None, encrypted=False, dry_run=False):
"""
Create a new EBS Volume.
Expand All @@ -2254,6 +2254,10 @@ def create_volume(self, size, zone, snapshot=None,
:param iops: The provisioned IOPs you want to associate with
this volume. (optional)
:type encrypted: bool
:param encrypted: Specifies whether the volume should be encrypted.
(optional)
:type dry_run: bool
:param dry_run: Set to True if the operation should not actually run.
Expand All @@ -2271,6 +2275,8 @@ def create_volume(self, size, zone, snapshot=None,
params['VolumeType'] = volume_type
if iops:
params['Iops'] = str(iops)
if encrypted:
params['Encrypted'] = 'true'
if dry_run:
params['DryRun'] = 'true'
return self.get_object('CreateVolume', params, Volume, verb='POST')
Expand Down
4 changes: 4 additions & 0 deletions boto/ec2/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(self, connection=None):
self.owner_alias = None
self.volume_size = None
self.description = None
self.encrypted = None

def __repr__(self):
return 'Snapshot:%s' % self.id
Expand All @@ -65,6 +66,8 @@ def endElement(self, name, value, connection):
self.volume_size = value
elif name == 'description':
self.description = value
elif name == 'encrypted':
self.encrypted = (value.lower() == 'true')
else:
setattr(self, name, value)

Expand Down Expand Up @@ -152,6 +155,7 @@ def create_volume(self, zone, size=None, volume_type=None, iops=None,
self.id,
volume_type,
iops,
self.encrypted,
dry_run=dry_run
)

Expand Down
4 changes: 4 additions & 0 deletions boto/ec2/volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Volume(TaggedEC2Object):
:ivar type: The type of volume (standard or consistent-iops)
:ivar iops: If this volume is of type consistent-iops, this is
the number of IOPS provisioned (10-300).
:ivar encrypted: True if this volume is encrypted.
"""

def __init__(self, connection=None):
Expand All @@ -57,6 +58,7 @@ def __init__(self, connection=None):
self.zone = None
self.type = None
self.iops = None
self.encrypted = None

def __repr__(self):
return 'Volume:%s' % self.id
Expand Down Expand Up @@ -92,6 +94,8 @@ def endElement(self, name, value, connection):
self.type = value
elif name == 'iops':
self.iops = int(value)
elif name == 'encrypted':
self.encrypted = (value.lower() == 'true')
else:
setattr(self, name, value)

Expand Down
154 changes: 153 additions & 1 deletion tests/unit/ec2/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import httplib

from datetime import datetime, timedelta
from mock import MagicMock, Mock, patch
from mock import MagicMock, Mock
from tests.unit import unittest
from tests.unit import AWSMockServiceTestCase

Expand Down Expand Up @@ -1445,5 +1445,157 @@ def test_associate_address(self):
self.assertEqual(False, result)


class TestDescribeVolumes(TestEC2ConnectionBase):
def default_body(self):
return """
<DescribeVolumesResponse xmlns="http://ec2.amazonaws.com/doc/2014-02-01/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<volumeSet>
<item>
<volumeId>vol-1a2b3c4d</volumeId>
<size>80</size>
<snapshotId/>
<availabilityZone>us-east-1a</availabilityZone>
<status>in-use</status>
<createTime>YYYY-MM-DDTHH:MM:SS.SSSZ</createTime>
<attachmentSet>
<item>
<volumeId>vol-1a2b3c4d</volumeId>
<instanceId>i-1a2b3c4d</instanceId>
<device>/dev/sdh</device>
<status>attached</status>
<attachTime>YYYY-MM-DDTHH:MM:SS.SSSZ</attachTime>
<deleteOnTermination>false</deleteOnTermination>
</item>
</attachmentSet>
<volumeType>standard</volumeType>
<encrypted>true</encrypted>
</item>
<item>
<volumeId>vol-5e6f7a8b</volumeId>
<size>80</size>
<snapshotId/>
<availabilityZone>us-east-1a</availabilityZone>
<status>in-use</status>
<createTime>YYYY-MM-DDTHH:MM:SS.SSSZ</createTime>
<attachmentSet>
<item>
<volumeId>vol-5e6f7a8b</volumeId>
<instanceId>i-5e6f7a8b</instanceId>
<device>/dev/sdz</device>
<status>attached</status>
<attachTime>YYYY-MM-DDTHH:MM:SS.SSSZ</attachTime>
<deleteOnTermination>false</deleteOnTermination>
</item>
</attachmentSet>
<volumeType>standard</volumeType>
<encrypted>false</encrypted>
</item>
</volumeSet>
</DescribeVolumesResponse>
"""

def test_get_all_volumes(self):
self.set_http_response(status_code=200)
result = self.ec2.get_all_volumes(volume_ids=['vol-1a2b3c4d', 'vol-5e6f7a8b'])
self.assert_request_parameters({
'Action': 'DescribeVolumes',
'VolumeId.1': 'vol-1a2b3c4d',
'VolumeId.2': 'vol-5e6f7a8b'},
ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
'SignatureVersion', 'Timestamp',
'Version'])
self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, 'vol-1a2b3c4d')
self.assertTrue(result[0].encrypted)
self.assertEqual(result[1].id, 'vol-5e6f7a8b')
self.assertFalse(result[1].encrypted)


class TestDescribeSnapshots(TestEC2ConnectionBase):
def default_body(self):
return """
<DescribeSnapshotsResponse xmlns="http://ec2.amazonaws.com/doc/2014-02-01/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<snapshotSet>
<item>
<snapshotId>snap-1a2b3c4d</snapshotId>
<volumeId>vol-1a2b3c4d</volumeId>
<status>pending</status>
<startTime>YYYY-MM-DDTHH:MM:SS.SSSZ</startTime>
<progress>80%</progress>
<ownerId>111122223333</ownerId>
<volumeSize>15</volumeSize>
<description>Daily Backup</description>
<tagSet/>
<encrypted>true</encrypted>
</item>
</snapshotSet>
<snapshotSet>
<item>
<snapshotId>snap-5e6f7a8b</snapshotId>
<volumeId>vol-5e6f7a8b</volumeId>
<status>completed</status>
<startTime>YYYY-MM-DDTHH:MM:SS.SSSZ</startTime>
<progress>100%</progress>
<ownerId>111122223333</ownerId>
<volumeSize>15</volumeSize>
<description>Daily Backup</description>
<tagSet/>
<encrypted>false</encrypted>
</item>
</snapshotSet>
</DescribeSnapshotsResponse>
"""

def test_get_all_snapshots(self):
self.set_http_response(status_code=200)
result = self.ec2.get_all_snapshots(snapshot_ids=['snap-1a2b3c4d', 'snap-5e6f7a8b'])
self.assert_request_parameters({
'Action': 'DescribeSnapshots',
'SnapshotId.1': 'snap-1a2b3c4d',
'SnapshotId.2': 'snap-5e6f7a8b'},
ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
'SignatureVersion', 'Timestamp',
'Version'])
self.assertEqual(len(result), 2)
self.assertEqual(result[0].id, 'snap-1a2b3c4d')
self.assertTrue(result[0].encrypted)
self.assertEqual(result[1].id, 'snap-5e6f7a8b')
self.assertFalse(result[1].encrypted)


class TestCreateVolume(TestEC2ConnectionBase):
def default_body(self):
return """
<CreateVolumeResponse xmlns="http://ec2.amazonaws.com/doc/2014-05-01/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
<volumeId>vol-1a2b3c4d</volumeId>
<size>80</size>
<snapshotId/>
<availabilityZone>us-east-1a</availabilityZone>
<status>creating</status>
<createTime>YYYY-MM-DDTHH:MM:SS.000Z</createTime>
<volumeType>standard</volumeType>
<encrypted>true</encrypted>
</CreateVolumeResponse>
"""

def test_create_volume(self):
self.set_http_response(status_code=200)
result = self.ec2.create_volume(80, 'us-east-1e', snapshot='snap-1a2b3c4d',
encrypted=True)
self.assert_request_parameters({
'Action': 'CreateVolume',
'AvailabilityZone': 'us-east-1e',
'Size': 80,
'SnapshotId': 'snap-1a2b3c4d',
'Encrypted': 'true'},
ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
'SignatureVersion', 'Timestamp',
'Version'])
self.assertEqual(result.id, 'vol-1a2b3c4d')
self.assertTrue(result.encrypted)

if __name__ == '__main__':
unittest.main()
3 changes: 2 additions & 1 deletion tests/unit/ec2/test_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ def default_body(self):
<value>demo_db_14_backup</value>
</item>
</tagSet>
<encrypted>false</encrypted>
</item>
</snapshotSet>
</DescribeSnapshotsResponse>
"""

def test_get_all_snapshots(self):
def test_describe_snapshots(self):
self.set_http_response(status_code=200)
response = self.service_connection.get_all_snapshots(['snap-1a2b3c4d', 'snap-9f8e7d6c'],
owner=['self', '111122223333'],
Expand Down
10 changes: 6 additions & 4 deletions tests/unit/ec2/test_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ def test_startElement_else_returns_None(self, startElement):
retval = volume.startElement("not tagSet or attachmentSet", None, None)
self.assertEqual(retval, None)

def check_that_attribute_has_been_set(self, name, value, attribute):
def check_that_attribute_has_been_set(self, name, value, attribute, obj_value=None):
volume = Volume()
volume.endElement(name, value, None)
self.assertEqual(getattr(volume, attribute), value)
expected_value = obj_value if obj_value is not None else value
self.assertEqual(getattr(volume, attribute), expected_value)

def test_endElement_sets_correct_attributes_with_values(self):
for arguments in [("volumeId", "some value", "id"),
Expand All @@ -90,8 +91,9 @@ def test_endElement_sets_correct_attributes_with_values(self):
("size", 5, "size"),
("snapshotId", 1, "snapshot_id"),
("availabilityZone", "some zone", "zone"),
("someName", "some value", "someName")]:
self.check_that_attribute_has_been_set(arguments[0], arguments[1], arguments[2])
("someName", "some value", "someName"),
("encrypted", "true", "encrypted", True)]:
self.check_that_attribute_has_been_set(*arguments)

def test_endElement_with_name_status_and_empty_string_value_doesnt_set_status(self):
volume = Volume()
Expand Down

0 comments on commit d85a449

Please sign in to comment.