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

Proposed by Thomas Herve
Status: Merged
Approved by: Jamu Kakar
Approved revision: 71
Merged at revision: 71
Proposed branch: lp:~therve/txaws/import-keypair
Merge into: lp:txaws
Diff against target: 260 lines (+98/-17)
3 files modified
txaws/ec2/client.py (+32/-4)
txaws/ec2/tests/test_client.py (+57/-13)
txaws/testing/payload.py (+9/-0)
To merge this branch: bzr merge lp:~therve/txaws/import-keypair
Reviewer Review Type Date Requested Status
Jamu Kakar Approve
Review via email: mp+54098@code.launchpad.net

Description of the change

Rather simple branch, with some cleanups.

To post a comment you must log in.
Revision history for this message
Jamu Kakar (jkakar) wrote :

[1]

Please add a docstring to _parse_import_keypair and
test_import_keypair. It would also be nice to add @param and @return
bits to the import_keypair function.

Nice work, +1!

review: Approve
lp:~therve/txaws/import-keypair updated
72. By Thomas Herve

Docstrings

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txaws/ec2/client.py'
2--- txaws/ec2/client.py 2011-02-02 18:17:13 +0000
3+++ txaws/ec2/client.py 2011-03-21 08:56:43 +0000
4@@ -33,7 +33,7 @@
5
6 def describe_instances(self, *instance_ids):
7 """Describe current instances."""
8- instances= {}
9+ instances = {}
10 for pos, instance_id in enumerate(instance_ids):
11 instances["InstanceId.%d" % (pos + 1)] = instance_id
12 query = self.query_factory(
13@@ -167,7 +167,7 @@
14 """
15 instances = {}
16 for pos, instance_id in enumerate(instance_ids):
17- instances["InstanceId.%d" % (pos+1)] = instance_id
18+ instances["InstanceId.%d" % (pos + 1)] = instance_id
19 query = self.query_factory(
20 action="TerminateInstances", creds=self.creds,
21 endpoint=self.endpoint, other_params=instances)
22@@ -197,7 +197,7 @@
23 """
24 group_names = {}
25 if names:
26- group_names = dict([("GroupName.%d" % (i+1), name)
27+ group_names = dict([("GroupName.%d" % (i + 1), name)
28 for i, name in enumerate(names)])
29 query = self.query_factory(
30 action="DescribeSecurityGroups", creds=self.creds,
31@@ -648,6 +648,34 @@
32 d = query.submit()
33 return d.addCallback(self._parse_truth_return)
34
35+ def import_keypair(self, keypair_name, key_material):
36+ """
37+ Import an existing SSH key into EC2. It supports:
38+ * OpenSSH public key format (e.g., the format in
39+ ~/.ssh/authorized_keys)
40+ * Base64 encoded DER format
41+ * SSH public key file format as specified in RFC4716
42+
43+ @param keypair_name: The name of the key to create.
44+ @param key_material: The material in one of the supported format.
45+
46+ @return: A L{Deferred} firing with a L{model.Keypair} instance if
47+ successful.
48+ """
49+ query = self.query_factory(
50+ action="ImportKeyPair", creds=self.creds, endpoint=self.endpoint,
51+ other_params={"KeyName": keypair_name,
52+ "PublicKeyMaterial": b64encode(key_material)})
53+ d = query.submit()
54+ return d.addCallback(self._parse_import_keypair, key_material)
55+
56+ def _parse_import_keypair(self, xml_bytes, key_material):
57+ """Extract the key name and the fingerprint from the result."""
58+ keypair_data = XML(xml_bytes)
59+ key_name = keypair_data.findtext("keyName")
60+ key_fingerprint = keypair_data.findtext("keyFingerprint")
61+ return model.Keypair(key_name, key_fingerprint, key_material)
62+
63 def allocate_address(self):
64 """
65 Acquire an elastic IP address to be attached subsequently to EC2
66@@ -734,7 +762,7 @@
67 def describe_availability_zones(self, names=None):
68 zone_names = None
69 if names:
70- zone_names = dict([("ZoneName.%d" % (i+1), name)
71+ zone_names = dict([("ZoneName.%d" % (i + 1), name)
72 for i, name in enumerate(names)])
73 query = self.query_factory(
74 action="DescribeAvailabilityZones", creds=self.creds,
75
76=== modified file 'txaws/ec2/tests/test_client.py'
77--- txaws/ec2/tests/test_client.py 2011-02-02 18:17:13 +0000
78+++ txaws/ec2/tests/test_client.py 2011-03-21 08:56:43 +0000
79@@ -120,7 +120,6 @@
80 self.assertEquals(zone.name, "us-east-1a")
81 self.assertEquals(zone.state, "available")
82
83-
84 creds = AWSCredentials("foo", "bar")
85 ec2 = client.EC2Client(creds, query_factory=StubQuery)
86 d = ec2.describe_availability_zones(["us-east-1a"])
87@@ -1360,6 +1359,47 @@
88 d.addCallback(self.assertFalse)
89 return d
90
91+ def test_import_keypair(self):
92+ """
93+ L{client.EC2Client.import_keypair} calls the C{ImportKeyPair} method
94+ with the given arguments, encoding the key material in base64, and
95+ returns a C{Keypair} instance.
96+ """
97+
98+ def check_parsed_import_keypair(keypair):
99+ self.assertEquals(keypair.name, "example-key-name")
100+ self.assertEquals(
101+ keypair.fingerprint,
102+ "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f")
103+ self.assertEquals(keypair.material, material)
104+
105+ class StubQuery(object):
106+
107+ def __init__(stub, action="", creds=None, endpoint=None,
108+ other_params={}):
109+ self.assertEqual(action, "ImportKeyPair")
110+ self.assertEqual("foo", creds)
111+ self.assertEquals(
112+ other_params,
113+ {"KeyName": "example-key-name",
114+ "PublicKeyMaterial":
115+ "c3NoLWRzcyBBQUFBQjNOemFDMWtjM01BQUFDQkFQNmFjakFQeitUR"
116+ "jJkREtmZGlhcnp2cXBBcjhlbUl6UElBWUp6QXNoTFgvUTJCZ2tWc0"
117+ "42eGI2QUlIUGE1MUFtWXVieU5PYjMxeVhWS2FRQTF6L213SHZtRld"
118+ "LQ1ZFQ0wwPSkgdXNlckBob3N0"})
119+
120+ def submit(self):
121+ return succeed(payload.sample_import_keypair_result)
122+
123+ ec2 = client.EC2Client(creds="foo", query_factory=StubQuery)
124+ material = (
125+ "ssh-dss AAAAB3NzaC1kc3MAAACBAP6acjAPz+TF2dDKfdiarzvqpAr8emIzPIAY"
126+ "JzAshLX/Q2BgkVsN6xb6AIHPa51AmYubyNOb31yXVKaQA1z/mwHvmFWKCVECL0=)"
127+ " user@host")
128+ d = ec2.import_keypair("example-key-name", material)
129+ d.addCallback(check_parsed_import_keypair)
130+ return d
131+
132
133 class EC2ErrorWrapperTestCase(TXAWSTestCase):
134
135@@ -1486,7 +1526,7 @@
136 query = client.Query(
137 action="DescribeInstances", creds=self.creds,
138 endpoint=self.endpoint, other_params={"InstanceId.0": "12345"},
139- time_tuple=(2007,11,12,13,14,15,0,0,0))
140+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
141 self.assertEqual(
142 query.params,
143 {"AWSAccessKeyId": "foo",
144@@ -1518,7 +1558,7 @@
145 query = client.Query(
146 action="DescribeInstances", creds=self.creds,
147 endpoint=self.endpoint, other_params={"fun": "games"},
148- time_tuple=(2007,11,12,13,14,15,0,0,0))
149+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
150 self.assertEqual([
151 ("AWSAccessKeyId", "foo"),
152 ("Action", "DescribeInstances"),
153@@ -1547,9 +1587,9 @@
154 query = client.Query(
155 action="DescribeInstances", creds=self.creds,
156 endpoint=self.endpoint,
157- other_params={"fu n": "g/ames", "argwithnovalue":"",
158+ other_params={"fu n": "g/ames", "argwithnovalue": "",
159 "InstanceId.1": "i-1234"},
160- time_tuple=(2007,11,12,13,14,15,0,0,0))
161+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
162 expected_query = ("AWSAccessKeyId=foo&Action=DescribeInstances"
163 "&InstanceId.1=i-1234"
164 "&SignatureVersion=2&"
165@@ -1560,7 +1600,8 @@
166 def test_signing_text(self):
167 query = client.Query(
168 action="DescribeInstances", creds=self.creds,
169- endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0))
170+ endpoint=self.endpoint,
171+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0))
172 signing_text = ("GET\n%s\n/\n" % self.endpoint.host +
173 "AWSAccessKeyId=foo&Action=DescribeInstances&"
174 "SignatureVersion=2&"
175@@ -1570,7 +1611,8 @@
176 def test_old_signing_text(self):
177 query = client.Query(
178 action="DescribeInstances", creds=self.creds,
179- endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
180+ endpoint=self.endpoint,
181+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
182 other_params={"SignatureVersion": "1"})
183 signing_text = (
184 "ActionDescribeInstancesAWSAccessKeyIdfooSignatureVersion1"
185@@ -1589,7 +1631,8 @@
186 def test_old_sign(self):
187 query = client.Query(
188 action="DescribeInstances", creds=self.creds,
189- endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
190+ endpoint=self.endpoint,
191+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
192 other_params={"SignatureVersion": "1"})
193 query.sign()
194 self.assertEqual(
195@@ -1598,7 +1641,8 @@
196 def test_unsupported_sign(self):
197 query = client.Query(
198 action="DescribeInstances", creds=self.creds,
199- endpoint=self.endpoint, time_tuple=(2007,11,12,13,14,15,0,0,0),
200+ endpoint=self.endpoint,
201+ time_tuple=(2007, 11, 12, 13, 14, 15, 0, 0, 0),
202 other_params={"SignatureVersion": "0"})
203 self.assertRaises(RuntimeError, query.sign)
204
205@@ -1622,7 +1666,7 @@
206
207 query = client.Query(
208 action='BadQuery', creds=self.creds, endpoint=self.endpoint,
209- time_tuple=(2009,8,15,13,14,15,0,0,0))
210+ time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
211
212 failure = query.submit()
213 d = self.assertFailure(failure, TwistedWebError)
214@@ -1647,7 +1691,7 @@
215
216 query = client.Query(
217 action='BadQuery', creds=self.creds, endpoint=self.endpoint,
218- time_tuple=(2009,8,15,13,14,15,0,0,0))
219+ time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
220
221 failure = query.submit()
222 d = self.assertFailure(failure, TwistedWebError)
223@@ -1676,7 +1720,7 @@
224
225 query = client.Query(
226 action='BadQuery', creds=self.creds, endpoint=self.endpoint,
227- time_tuple=(2009,8,15,13,14,15,0,0,0))
228+ time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
229
230 failure = query.submit()
231 d = self.assertFailure(failure, TwistedWebError)
232@@ -1731,7 +1775,7 @@
233 """Copied from twisted.web.test.test_webclient."""
234 query = client.Query(
235 action="DummyQuery", creds=self.creds, endpoint=self.endpoint,
236- time_tuple=(2009,8,17,13,14,15,0,0,0))
237+ time_tuple=(2009, 8, 15, 13, 14, 15, 0, 0, 0))
238 deferred = query.get_page(self.get_url("file"))
239 deferred.addCallback(self.assertEquals, "0123456789")
240 return deferred
241
242=== modified file 'txaws/testing/payload.py'
243--- txaws/testing/payload.py 2010-07-20 10:15:48 +0000
244+++ txaws/testing/payload.py 2011-03-21 08:56:43 +0000
245@@ -706,6 +706,15 @@
246 """
247
248
249+sample_import_keypair_result = """\
250+<?xml version="1.0"?>
251+<ImportKeyPairResponse xmlns="http://ec2.amazonaws.com/doc/%s/">
252+ <keyName>example-key-name</keyName>
253+ <keyFingerprint>1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f</keyFingerprint>
254+</ImportKeyPairResponse>
255+""" % (version.ec2_api,)
256+
257+
258 sample_allocate_address_result = """\
259 <?xml version="1.0"?>
260 <AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/%s/">

Subscribers

People subscribed via source and target branches

to all changes: