← Back to team overview

txaws-dev team mailing list archive

[Merge] lp:~therve/txaws/import-keypair into lp:txaws

 

Thomas Herve has proposed merging lp:~therve/txaws/import-keypair into lp:txaws.

Requested reviews:
  txAWS Developers (txaws-dev)
Related bugs:
  Bug #738131 in txAWS: "Add support for ImportKeyPair"
  https://bugs.launchpad.net/txaws/+bug/738131

For more details, see:
https://code.launchpad.net/~therve/txaws/import-keypair/+merge/54098

Rather simple branch, with some cleanups.
-- 
https://code.launchpad.net/~therve/txaws/import-keypair/+merge/54098
Your team txAWS Developers is requested to review the proposed merge of lp:~therve/txaws/import-keypair into lp:txaws.
=== modified file 'txaws/ec2/client.py'
--- txaws/ec2/client.py	2011-02-02 18:17:13 +0000
+++ txaws/ec2/client.py	2011-03-19 10:51:36 +0000
@@ -33,7 +33,7 @@
 
     def describe_instances(self, *instance_ids):
         """Describe current instances."""
-        instances= {}
+        instances = {}
         for pos, instance_id in enumerate(instance_ids):
             instances["InstanceId.%d" % (pos + 1)] = instance_id
         query = self.query_factory(
@@ -167,7 +167,7 @@
         """
         instances = {}
         for pos, instance_id in enumerate(instance_ids):
-            instances["InstanceId.%d" % (pos+1)] = instance_id
+            instances["InstanceId.%d" % (pos + 1)] = instance_id
         query = self.query_factory(
             action="TerminateInstances", creds=self.creds,
             endpoint=self.endpoint, other_params=instances)
@@ -197,7 +197,7 @@
         """
         group_names = {}
         if names:
-            group_names = dict([("GroupName.%d" % (i+1), name)
+            group_names = dict([("GroupName.%d" % (i + 1), name)
                                 for i, name in enumerate(names)])
         query = self.query_factory(
             action="DescribeSecurityGroups", creds=self.creds,
@@ -648,6 +648,27 @@
         d = query.submit()
         return d.addCallback(self._parse_truth_return)
 
+    def import_keypair(self, keypair_name, key_material):
+        """
+        Import an existing SSH key into EC2. It supports:
+            * OpenSSH public key format (e.g., the format in
+              ~/.ssh/authorized_keys)
+            * Base64 encoded DER format
+            * SSH public key file format as specified in RFC4716
+        """
+        query = self.query_factory(
+            action="ImportKeyPair", creds=self.creds, endpoint=self.endpoint,
+            other_params={"KeyName": keypair_name,
+                          "PublicKeyMaterial": b64encode(key_material)})
+        d = query.submit()
+        return d.addCallback(self._parse_import_keypair, key_material)
+
+    def _parse_import_keypair(self, xml_bytes, key_material):
+        keypair_data = XML(xml_bytes)
+        key_name = keypair_data.findtext("keyName")
+        key_fingerprint = keypair_data.findtext("keyFingerprint")
+        return model.Keypair(key_name, key_fingerprint, key_material)
+
     def allocate_address(self):
         """
         Acquire an elastic IP address to be attached subsequently to EC2
@@ -734,7 +755,7 @@
     def describe_availability_zones(self, names=None):
         zone_names = None
         if names:
-            zone_names = dict([("ZoneName.%d" % (i+1), name)
+            zone_names = dict([("ZoneName.%d" % (i + 1), name)
                                 for i, name in enumerate(names)])
         query = self.query_factory(
             action="DescribeAvailabilityZones", creds=self.creds,

=== modified file 'txaws/ec2/tests/test_client.py'
--- txaws/ec2/tests/test_client.py	2011-02-02 18:17:13 +0000
+++ txaws/ec2/tests/test_client.py	2011-03-19 10:51:36 +0000
@@ -120,7 +120,6 @@
             self.assertEquals(zone.name, "us-east-1a")
             self.assertEquals(zone.state, "available")
 
-
         creds = AWSCredentials("foo", "bar")
         ec2 = client.EC2Client(creds, query_factory=StubQuery)
         d = ec2.describe_availability_zones(["us-east-1a"])
@@ -1360,6 +1359,42 @@
         d.addCallback(self.assertFalse)
         return d
 
+    def test_import_keypair(self):
+
+        def check_parsed_import_keypair(keypair):
+            self.assertEquals(keypair.name, "example-key-name")
+            self.assertEquals(
+                keypair.fingerprint,
+                "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f")
+            self.assertEquals(keypair.material, material)
+
+        class StubQuery(object):
+
+            def __init__(stub, action="", creds=None, endpoint=None,
+                         other_params={}):
+                self.assertEqual(action, "ImportKeyPair")
+                self.assertEqual("foo", creds)
+                self.assertEquals(
+                    other_params,
+                    {"KeyName": "example-key-name",
+                     "PublicKeyMaterial":
+                        "c3NoLWRzcyBBQUFBQjNOemFDMWtjM01BQUFDQkFQNmFjakFQeitUR"
+                        "jJkREtmZGlhcnp2cXBBcjhlbUl6UElBWUp6QXNoTFgvUTJCZ2tWc0"
+                        "42eGI2QUlIUGE1MUFtWXVieU5PYjMxeVhWS2FRQTF6L213SHZtRld"
+                        "LQ1ZFQ0wwPSkgdXNlckBob3N0"})
+
+            def submit(self):
+                return succeed(payload.sample_import_keypair_result)
+
+        ec2 = client.EC2Client(creds="foo", query_factory=StubQuery)
+        material = (
+            "ssh-dss AAAAB3NzaC1kc3MAAACBAP6acjAPz+TF2dDKfdiarzvqpAr8emIzPIAY"
+            "JzAshLX/Q2BgkVsN6xb6AIHPa51AmYubyNOb31yXVKaQA1z/mwHvmFWKCVECL0=)"
+            " user@host")
+        d = ec2.import_keypair("example-key-name", material)
+        d.addCallback(check_parsed_import_keypair)
+        return d
+
 
 class EC2ErrorWrapperTestCase(TXAWSTestCase):
 
@@ -1486,7 +1521,7 @@
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
             endpoint=self.endpoint, other_params={"InstanceId.0": "12345"},
-            time_tuple=(2007,11,12,13,14,15,0,0,0))
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
         self.assertEqual(
             query.params,
             {"AWSAccessKeyId": "foo",
@@ -1518,7 +1553,7 @@
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
             endpoint=self.endpoint, other_params={"fun": "games"},
-            time_tuple=(2007,11,12,13,14,15,0,0,0))
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
         self.assertEqual([
             ("AWSAccessKeyId", "foo"),
             ("Action", "DescribeInstances"),
@@ -1547,9 +1582,9 @@
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
             endpoint=self.endpoint,
-            other_params={"fu n": "g/ames", "argwithnovalue":"",
+            other_params={"fu n": "g/ames", "argwithnovalue": "",
                           "InstanceId.1": "i-1234"},
-            time_tuple=(2007,11,12,13,14,15,0,0,0))
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
         expected_query = ("AWSAccessKeyId=foo&Action=DescribeInstances"
             "&InstanceId.1=i-1234"
             "&SignatureVersion=2&"
@@ -1560,7 +1595,8 @@
     def test_signing_text(self):
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
-            endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0))
+            endpoint=self.endpoint,
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
         signing_text = ("GET\n%s\n/\n" % self.endpoint.host +
             "AWSAccessKeyId=foo&Action=DescribeInstances&"
             "SignatureVersion=2&"
@@ -1570,7 +1606,8 @@
     def test_old_signing_text(self):
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
-            endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
+            endpoint=self.endpoint,
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
             other_params={"SignatureVersion": "1"})
         signing_text = (
             "ActionDescribeInstancesAWSAccessKeyIdfooSignatureVersion1"
@@ -1589,7 +1626,8 @@
     def test_old_sign(self):
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
-            endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
+            endpoint=self.endpoint,
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
             other_params={"SignatureVersion": "1"})
         query.sign()
         self.assertEqual(
@@ -1598,7 +1636,8 @@
     def test_unsupported_sign(self):
         query = client.Query(
             action="DescribeInstances", creds=self.creds,
-            endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
+            endpoint=self.endpoint,
+            time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
             other_params={"SignatureVersion": "0"})
         self.assertRaises(RuntimeError, query.sign)
 
@@ -1622,7 +1661,7 @@
 
         query = client.Query(
             action='BadQuery', creds=self.creds, endpoint=self.endpoint,
-            time_tuple=(2009,8,15,13,14,15,0,0,0))
+            time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
 
         failure = query.submit()
         d = self.assertFailure(failure, TwistedWebError)
@@ -1647,7 +1686,7 @@
 
         query = client.Query(
             action='BadQuery', creds=self.creds, endpoint=self.endpoint,
-            time_tuple=(2009,8,15,13,14,15,0,0,0))
+            time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
 
         failure = query.submit()
         d = self.assertFailure(failure, TwistedWebError)
@@ -1676,7 +1715,7 @@
 
         query = client.Query(
             action='BadQuery', creds=self.creds, endpoint=self.endpoint,
-            time_tuple=(2009,8,15,13,14,15,0,0,0))
+            time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
 
         failure = query.submit()
         d = self.assertFailure(failure, TwistedWebError)
@@ -1731,7 +1770,7 @@
         """Copied from twisted.web.test.test_webclient."""
         query = client.Query(
             action="DummyQuery", creds=self.creds, endpoint=self.endpoint,
-            time_tuple=(2009,8,17,13,14,15,0,0,0))
+            time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
         deferred = query.get_page(self.get_url("file"))
         deferred.addCallback(self.assertEquals, "0123456789")
         return deferred

=== modified file 'txaws/testing/payload.py'
--- txaws/testing/payload.py	2010-07-20 10:15:48 +0000
+++ txaws/testing/payload.py	2011-03-19 10:51:36 +0000
@@ -706,6 +706,15 @@
 """
 
 
+sample_import_keypair_result = """\
+<?xml version="1.0"?>
+<ImportKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/%s/";>
+  <keyName>example-key-name</keyName>
+  <keyFingerprint>1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f</keyFingerprint>
+</ImportKeyPairResponse>
+""" % (version.ec2_api,)
+
+
 sample_allocate_address_result = """\
 <?xml version="1.0"?>
 <AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/%s/";>