From 6d5a10fafb98e0491e8c4a9bcd59e20b14ac46cd Mon Sep 17 00:00:00 2001 From: Xavier Barbosa Date: Tue, 24 Sep 2013 14:21:09 +0200 Subject: [PATCH 1/2] Issue LIBCLOUD-335: Add IAM Profile on node creation for ec2 based providers. --- docs/compute/examples.rst | 11 +++++++++++ docs/examples/compute/create_ec2_node_iam.py | 20 ++++++++++++++++++++ docs/other/working-with-oo-apis.rst | 6 ++++++ libcloud/compute/drivers/ec2.py | 19 +++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 docs/examples/compute/create_ec2_node_iam.py diff --git a/docs/compute/examples.rst b/docs/compute/examples.rst index 1176795636..0892d6d4fd 100644 --- a/docs/compute/examples.rst +++ b/docs/compute/examples.rst @@ -93,6 +93,17 @@ supported providers and provider constants, see .. literalinclude:: /examples/compute/create_ec2_node_custom_ami.py :language: python +Create EC2 node using an IAM Profile +------------------------------------ + +.. note:: + + This example assumes the IAM profile already exists. If the key pair + doesn't exist yet, you must create it manually. + +.. literalinclude:: /examples/compute/create_ec2_node_iam.py + :language: python + Create a node on a CloudStack provider using a provided key pair and security groups ------------------------------------------------------------------------------------ diff --git a/docs/examples/compute/create_ec2_node_iam.py b/docs/examples/compute/create_ec2_node_iam.py new file mode 100644 index 0000000000..952ef2bf3e --- /dev/null +++ b/docs/examples/compute/create_ec2_node_iam.py @@ -0,0 +1,20 @@ +from libcloud.compute.types import Provider +from libcloud.compute.providers import get_driver + +ACCESS_ID = 'your access id' +SECRET_KEY = 'your secret key' +IAM_PROFILE = 'your IAM profile arn or IAM profile name' + +IMAGE_ID = 'ami-c8052d8d' +SIZE_ID = 't1.micro' +cls = get_driver(Provider.EC2_US_WEST) +driver = cls(ACCESS_ID, SECRET_KEY) + +# Here we select size and image +sizes = driver.list_sizes() +images = driver.list_images() + +size = [s for s in sizes if s.id == SIZE_ID][0] +image = [i for i in images if i.id == IMAGE_ID][0] + +node = driver.create_node(name='test-node', image=image, size=size, ex_iamprofile=IAM_PROFILE) diff --git a/docs/other/working-with-oo-apis.rst b/docs/other/working-with-oo-apis.rst index 45fb45a26b..96b29b62bc 100644 --- a/docs/other/working-with-oo-apis.rst +++ b/docs/other/working-with-oo-apis.rst @@ -59,3 +59,9 @@ Example 2 - creating an EC2 instance with a known ``NodeSize`` and ``NodeImage`` .. literalinclude:: /examples/compute/create_ec2_node_manual_instantiation.py :language: python + +Example 3 - creating an EC2 instance with an IAM profile +-------------------------------------------------------- + +.. literalinclude:: /examples/compute/create_ec2_node_iam.py + :language: python diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 3ddb3e58b9..3be281ebec 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -543,7 +543,9 @@ def _to_node(self, element, groups=None): 'clienttoken': findattr(element=element, xpath="clientToken", namespace=NAMESPACE), 'groups': groups, - 'tags': tags + 'tags': tags, + 'iam_profile': findattr(element, xpath="iamInstanceProfile/id", + namespace=NAMESPACE) } ) return n @@ -1325,7 +1327,7 @@ def create_node(self, **kwargs): @keyword ex_maxcount: Maximum number of instances to launch @type ex_maxcount: C{int} - @keyword ex_security_groups: A list of namees of security groups to + @keyword ex_security_groups: A list of names of security groups to assign to the node. @type ex_security_groups: C{list} @@ -1343,6 +1345,9 @@ def create_node(self, **kwargs): [{'DeviceName': '/dev/sda1', 'Ebs.VolumeSize': 10}, {'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}] @type ex_blockdevicemappings: C{list} of C{dict} + + @keyword ex_iamprofile: Name or ARN of IAM profile + @type ex_iamprofile: C{str} """ image = kwargs["image"] size = kwargs["size"] @@ -1412,6 +1417,16 @@ def create_node(self, **kwargs): for k, v in mapping.items(): params['BlockDeviceMapping.%d.%s' % (idx, k)] = str(v) + if 'ex_iamprofile' in kwargs: + try: + if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'): + params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile'] + else: + params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile'] + except AttributeError as exception: + raise AttributeError( + 'ex_iamprofile not string') + object = self.connection.request(self.path, params=params).object nodes = self._to_nodes(object, 'instancesSet/item') From d51bfc9af447056e2f2203487233ddcaaf57dd9c Mon Sep 17 00:00:00 2001 From: Xavier Barbosa Date: Wed, 25 Sep 2013 11:12:10 +0200 Subject: [PATCH 2/2] LIBCLOUD-335: EC2 IAM Profile are refactored for py2~3 and tested --- libcloud/compute/drivers/ec2.py | 17 +++++---- .../ec2/run_instances_iam_profile.xml | 35 +++++++++++++++++++ libcloud/test/compute/test_ec2.py | 29 +++++++++++++++ 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py index 3be281ebec..58471aa89a 100644 --- a/libcloud/compute/drivers/ec2.py +++ b/libcloud/compute/drivers/ec2.py @@ -26,7 +26,7 @@ from xml.etree import ElementTree as ET -from libcloud.utils.py3 import b +from libcloud.utils.py3 import b, basestring from libcloud.utils.xml import fixxpath, findtext, findattr, findall from libcloud.utils.publickey import get_pubkey_ssh2_fingerprint @@ -1418,14 +1418,13 @@ def create_node(self, **kwargs): params['BlockDeviceMapping.%d.%s' % (idx, k)] = str(v) if 'ex_iamprofile' in kwargs: - try: - if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'): - params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile'] - else: - params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile'] - except AttributeError as exception: - raise AttributeError( - 'ex_iamprofile not string') + if not isinstance(kwargs['ex_iamprofile'], basestring): + raise AttributeError('ex_iamprofile not string') + + if kwargs['ex_iamprofile'].startswith('arn:aws:iam:'): + params['IamInstanceProfile.Arn'] = kwargs['ex_iamprofile'] + else: + params['IamInstanceProfile.Name'] = kwargs['ex_iamprofile'] object = self.connection.request(self.path, params=params).object nodes = self._to_nodes(object, 'instancesSet/item') diff --git a/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml b/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml new file mode 100644 index 0000000000..51a89a130c --- /dev/null +++ b/libcloud/test/compute/fixtures/ec2/run_instances_iam_profile.xml @@ -0,0 +1,35 @@ + + r-47a5403e + AIDADH4IGTRXXKCD + + + default + + + + + i-2ba64343 + ami-be3adfd7 + + 0 + pending + + + + example-key-name + 0 + m1.small + 2007-08-07T11:51:50.000Z + + us-east-1b + + + true + + + AIDGPMS9RO4H3FEXAMPLE + arn:aws:iam::123456789012:instance-profile/ExampleInstanceProfile + + + + diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py index 840a786e0d..c51dc5a216 100644 --- a/libcloud/test/compute/test_ec2.py +++ b/libcloud/test/compute/test_ec2.py @@ -287,6 +287,31 @@ def test_list_sizes(self): self.driver.region_name = region_old + def test_ex_create_node_with_ex_iam_profile(self): + iamProfile = { + 'id': 'AIDGPMS9RO4H3FEXAMPLE', + 'name': 'Foo', + 'arn': 'arn:aws:iam:...' + } + + image = NodeImage(id='ami-be3adfd7', + name=self.image_name, + driver=self.driver) + size = NodeSize('m1.small', 'Small Instance', None, None, None, None, + driver=self.driver) + + EC2MockHttp.type = None + node1 = self.driver.create_node(name='foo', image=image, size=size) + EC2MockHttp.type = 'ex_iam_profile' + node2 = self.driver.create_node(name='bar', image=image, size=size, + ex_iam_profile=iamProfile['name']) + node3 = self.driver.create_node(name='bar', image=image, size=size, + ex_iam_profile=iamProfile['arn']) + + self.assertFalse(node1.extra['iam_profile']) + self.assertEqual(node2.extra['iam_profile'], iamProfile['id']) + self.assertEqual(node3.extra['iam_profile'], iamProfile['id']) + def test_list_images(self): images = self.driver.list_images() image = images[0] @@ -640,6 +665,10 @@ def _idempotent_mismatch_RunInstances(self, method, url, body, headers): body = self.fixtures.load('run_instances_idem_mismatch.xml') return (httplib.BAD_REQUEST, body, {}, httplib.responses[httplib.BAD_REQUEST]) + def _ex_iam_profile_RunInstances(self, method, url, body, headers): + body = self.fixtures.load('run_instances_iam_profile.xml') + return (httplib.OK, body, {}, httplib.responses[httplib.OK]) + def _TerminateInstances(self, method, url, body, headers): body = self.fixtures.load('terminate_instances.xml') return (httplib.OK, body, {}, httplib.responses[httplib.OK])