Merge lp:~oubiwann/txaws/416109-arbitrary-endpoints into lp:~txawsteam/txaws/trunk

Proposed by Robert Collins
Status: Superseded
Proposed branch: lp:~oubiwann/txaws/416109-arbitrary-endpoints
Merge into: lp:~txawsteam/txaws/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~oubiwann/txaws/416109-arbitrary-endpoints

This proposal supersedes a proposal from 2009-08-20.

This proposal has been superseded by a proposal from 2009-08-25.

To post a comment you must log in.
Revision history for this message
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal

This branch adds support for a service object that manages host endpoints as well as authorization keys (thus obviating the need for the AWSCredential object).

Revision history for this message
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal

On Thu, 2009-08-20 at 19:25 +0000, Duncan McGreggor wrote:
> Duncan McGreggor has proposed merging
> lp:~oubiwann/txaws/416109-arbitrary-endpoints into lp:txaws.
>
> Requested reviews:
> txAWS Team (txawsteam)
>
> This branch adds support for a service object that manages host
> endpoints as well as authorization keys (thus obviating the need for
> the AWSCredential object).

Lets be careful to keep space under storage, ec2 etc for server
components. storage.service isn't really a storage service :) Lets call
the description of an end point AWSServiceEndpoint, or something like
that.

local and credentials appear orthogonal to me - for instance,
EC2 EU and EC2 US are different endpoints/services with common
credentials. I think conflating them is unnecessary and undesirable.
Further to that, the AWSCredentials are usable on related services in a
single region - EC2, S3 and so on, so when we're passing around a
description, we probably want to have a region that describes the
endpoints for a collection of services. The goal being able to have a
static object
AWS_US1 = #...
AWS_US2 = #...
and for people to make their own;
my_eucalyptus_region = #...

At runtime then, one would ask a region for a client of a particular
service, using some credentials.

AWS_US1.make_ec2_client(my_creds)
AWS_US1.make_sqs_client(my_creds)

etc.

We could do this without changing the existing clients at all, by just
storing scheme,host tuples in a AWSRegion - but I think it is cleaner to
do the sort of refactoring you have done. I think it would be best by
having an AWSServiceEndpoint which has the scheme and url, and keeping
the creds separate. For instance,
class AWSServiceRegion:
    def make_ec2_client(self, creds=None):
        return EC2Client(creds=creds, service_endpoint=self.ec2_endpoint)

Also a bit of detail review - 'default_schema = https' - in URL terms
(see http://www.ietf.org/rfc/rfc3986.txt) that is a _scheme_, not a
_schema_.

review needsfixing

Revision history for this message
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal

> On Thu, 2009-08-20 at 19:25 +0000, Duncan McGreggor wrote:
> > Duncan McGreggor has proposed merging
> > lp:~oubiwann/txaws/416109-arbitrary-endpoints into lp:txaws.
> >
> > Requested reviews:
> > txAWS Team (txawsteam)
> >
> > This branch adds support for a service object that manages host
> > endpoints as well as authorization keys (thus obviating the need for
> > the AWSCredential object).
>
>
> Lets be careful to keep space under storage, ec2 etc for server
> components. storage.service isn't really a storage service :) Lets call
> the description of an end point AWSServiceEndpoint, or something like
> that.
>
> local and credentials appear orthogonal to me - for instance,
> EC2 EU and EC2 US are different endpoints/services with common
> credentials. I think conflating them is unnecessary and undesirable.
> Further to that, the AWSCredentials are usable on related services in a
> single region - EC2, S3 and so on, so when we're passing around a
> description, we probably want to have a region that describes the
> endpoints for a collection of services. The goal being able to have a
> static object
> AWS_US1 = #...
> AWS_US2 = #...
> and for people to make their own;
> my_eucalyptus_region = #...
>
> At runtime then, one would ask a region for a client of a particular
> service, using some credentials.
>
> AWS_US1.make_ec2_client(my_creds)
> AWS_US1.make_sqs_client(my_creds)
>
> etc.
>
> We could do this without changing the existing clients at all, by just
> storing scheme,host tuples in a AWSRegion - but I think it is cleaner to
> do the sort of refactoring you have done. I think it would be best by
> having an AWSServiceEndpoint which has the scheme and url, and keeping
> the creds separate. For instance,
> class AWSServiceRegion:
> def make_ec2_client(self, creds=None):
> return EC2Client(creds=creds, service_endpoint=self.ec2_endpoint)
>
> Also a bit of detail review - 'default_schema = https' - in URL terms
> (see http://www.ietf.org/rfc/rfc3986.txt) that is a _scheme_, not a
> _schema_.
>
> review needsfixing

+1 on these suggestions. I'll give it another go with this in mind.

Revision history for this message
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal

> On Thu, 2009-08-20 at 19:25 +0000, Duncan McGreggor wrote:
> > Duncan McGreggor has proposed merging
> > lp:~oubiwann/txaws/416109-arbitrary-endpoints into lp:txaws.
> >
> > Requested reviews:
> > txAWS Team (txawsteam)
> >
> > This branch adds support for a service object that manages host
> > endpoints as well as authorization keys (thus obviating the need for
> > the AWSCredential object).
>
>
> Lets be careful to keep space under storage, ec2 etc for server
> components. storage.service isn't really a storage service :) Lets call
> the description of an end point AWSServiceEndpoint, or something like
> that.

[1] Renamed.

> local and credentials appear orthogonal to me - for instance,
> EC2 EU and EC2 US are different endpoints/services with common
> credentials. I think conflating them is unnecessary and undesirable.
> Further to that, the AWSCredentials are usable on related services in a
> single region - EC2, S3 and so on, so when we're passing around a
> description, we probably want to have a region that describes the
> endpoints for a collection of services.

[2]

Brought the credentials back into the source. Pulled credential code out of service endpoint code.

> The goal being able to have a
> static object
> AWS_US1 = #...
> AWS_US2 = #...
> and for people to make their own;
> my_eucalyptus_region = #...
>
> At runtime then, one would ask a region for a client of a particular
> service, using some credentials.
>
> AWS_US1.make_ec2_client(my_creds)
> AWS_US1.make_sqs_client(my_creds)
>
> etc.
>
> We could do this without changing the existing clients at all, by just
> storing scheme,host tuples in a AWSRegion - but I think it is cleaner to
> do the sort of refactoring you have done. I think it would be best by
> having an AWSServiceEndpoint which has the scheme and url, and keeping
> the creds separate. For instance,
> class AWSServiceRegion:
> def make_ec2_client(self, creds=None):
> return EC2Client(creds=creds, service_endpoint=self.ec2_endpoint)

[3]

I'm got an implementation of this in place right now. It ended up pretty similar to what you suggested. There are some missing unit tests right now -- I'll be hitting those this afternoon.

> Also a bit of detail review - 'default_schema = https' - in URL terms
> (see http://www.ietf.org/rfc/rfc3986.txt) that is a _scheme_, not a
> _schema_.

[4]

Ugh, thanks. The first place I wrote it was good, then I copied a typo everywhere else. Fixed.

Revision history for this message
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal

Okay! Just pushed up the latest code for the missing unit tests. It's ready for another review :-)

Revision history for this message
Robert Collins (lifeless) wrote :

On Sun, 2009-08-23 at 21:30 +0000, Robert Collins wrote:
> === modified file 'txaws/client/gui/gtk.py'
> --- txaws/client/gui/gtk.py 2009-08-18 22:53:53 +0000
> +++ txaws/client/gui/gtk.py 2009-08-20 19:14:32 +0000
> @@ -8,7 +8,7 @@
> import gobject
> import gtk
>
> -from txaws.credentials import AWSCredentials
> +from txaws.ec2.service import EC2Service
>
>
> __all__ = ['main']
> @@ -27,10 +27,10 @@
> # Nested import because otherwise we get 'reactor already
> installed'.
> self.password_dialog = None
> try:
> - creds = AWSCredentials()
> + service = AWSService()

This is going to be a NameError :P

...
> === added file 'txaws/credentials.py'

this file already exists. I think you've done something weird in your
branch. lets review once thats fixed. Find me in #bzr :P

review needsfixing

--

27. By Duncan McGreggor

Removed new cred files.

28. By Duncan McGreggor

Reverted to original cred files (-r13..12) in an effort to fix some weirdness
in this branch with those files.

29. By Duncan McGreggor

Reapplied the recent changes to the cred files.

30. By Duncan McGreggor

- Changed the gtk client to use creds and service region instead of the
  no-longer-supported service object.
- Added a cache-purging keyword parameter to the AWS service region's
  get_client method.
- Added a docstring.
- Added region string objects to service.__all__.
- Tweaked the gtk code to check for an already-installed gtk Twisted reactor.
- Cleaned up some remaining references to the service object in the client and
  replaced those with endpoint references.
- Fixed a stub query signature in a unit test to include a parameter for an
  endpoint object.

31. By Duncan McGreggor

- Removed old service unit test file.
- Added unit test for purge client option.
- Fixed typo in client check unit tests.

32. By Duncan McGreggor

- Added access and secret key parameters to the AWSServiceRegion constructor.
- Updated AWSServiceRegion to create creds based on access and secret key if no
  creds are supplied.
- Updated docstrings.

33. By Duncan McGreggor

Added a uri parameter for service region creation to ease the creation of
service region objects with non-Amazon endpoints (e.g., in Landscape).

34. By Duncan McGreggor

Swapped the ordering of an import to be in alphabetical order.

35. By Duncan McGreggor

- Fixed the creds parameter in the get_ec2_client method.
- Removed redundant code in check_parsed_instances.
- Created a testing subpackage for generally useful testing classes.
- Added fake ec2 client and region classes.
- Moved base test case into new testing module.

36. By Duncan McGreggor

- Removed unimplemented methods (jkakar 1).
- Made environment mutation methods private (jkakar 3).
- Tweaked the default values for the FakeEC2Client (jkakar 4).
- Removed unnecessary test case methods (jkakar 5).

37. By Duncan McGreggor

Tweaked the storage request object's enpoint/uri stuff and added some unit
tests (jkakar 2).

38. By Duncan McGreggor

Removed unnecessary region instantiation (therve 3).
Added parse utility function (therve 4).

39. By Duncan McGreggor

Fixed pyflakes (therve 1).

40. By Duncan McGreggor

Removed unneeded try/except block in gtk client (therve 2).

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'txaws/client/gui/gtk.py'
--- txaws/client/gui/gtk.py 2009-08-18 22:53:53 +0000
+++ txaws/client/gui/gtk.py 2009-08-20 19:14:32 +0000
@@ -8,7 +8,7 @@
8import gobject8import gobject
9import gtk9import gtk
1010
11from txaws.credentials import AWSCredentials11from txaws.ec2.service import EC2Service
1212
1313
14__all__ = ['main']14__all__ = ['main']
@@ -27,10 +27,10 @@
27 # Nested import because otherwise we get 'reactor already installed'.27 # Nested import because otherwise we get 'reactor already installed'.
28 self.password_dialog = None28 self.password_dialog = None
29 try:29 try:
30 creds = AWSCredentials()30 service = AWSService()
31 except ValueError:31 except ValueError:
32 creds = self.from_gnomekeyring()32 service = self.from_gnomekeyring()
33 self.create_client(creds)33 self.create_client(service)
34 menu = '''34 menu = '''
35 <ui>35 <ui>
36 <menubar name="Menubar">36 <menubar name="Menubar">
@@ -54,10 +54,10 @@
54 '/Menubar/Menu/Stop instances').props.parent54 '/Menubar/Menu/Stop instances').props.parent
55 self.connect('popup-menu', self.on_popup_menu)55 self.connect('popup-menu', self.on_popup_menu)
5656
57 def create_client(self, creds):57 def create_client(self, service):
58 from txaws.ec2.client import EC2Client58 from txaws.ec2.client import EC2Client
59 if creds is not None:59 if service is not None:
60 self.client = EC2Client(creds=creds)60 self.client = EC2Client(service=service)
61 self.on_activate(None)61 self.on_activate(None)
62 else:62 else:
63 # waiting on user entered credentials.63 # waiting on user entered credentials.
@@ -65,7 +65,7 @@
6565
66 def from_gnomekeyring(self):66 def from_gnomekeyring(self):
67 # Try for gtk gui specific credentials.67 # Try for gtk gui specific credentials.
68 creds = None68 service = None
69 try:69 try:
70 items = gnomekeyring.find_items_sync(70 items = gnomekeyring.find_items_sync(
71 gnomekeyring.ITEM_GENERIC_SECRET,71 gnomekeyring.ITEM_GENERIC_SECRET,
@@ -78,7 +78,7 @@
78 return None78 return None
79 else:79 else:
80 key_id, secret_key = items[0].secret.split(':')80 key_id, secret_key = items[0].secret.split(':')
81 return AWSCredentials(access_key=key_id, secret_key=secret_key)81 return EC2Service(access_key=key_id, secret_key=secret_key)
8282
83 def show_a_password_dialog(self):83 def show_a_password_dialog(self):
84 self.password_dialog = gtk.Dialog(84 self.password_dialog = gtk.Dialog(
@@ -133,8 +133,8 @@
133 content = self.password_dialog.get_content_area()133 content = self.password_dialog.get_content_area()
134 key_id = content.get_children()[0].get_children()[1].get_text()134 key_id = content.get_children()[0].get_children()[1].get_text()
135 secret_key = content.get_children()[1].get_children()[1].get_text()135 secret_key = content.get_children()[1].get_children()[1].get_text()
136 creds = AWSCredentials(access_key=key_id, secret_key=secret_key)136 service = EC2Service(access_key=key_id, secret_key=secret_key)
137 self.create_client(creds)137 self.create_client(service)
138 gnomekeyring.item_create_sync(138 gnomekeyring.item_create_sync(
139 None,139 None,
140 gnomekeyring.ITEM_GENERIC_SECRET,140 gnomekeyring.ITEM_GENERIC_SECRET,
141141
=== added file 'txaws/credentials.py'
--- txaws/credentials.py 1970-01-01 00:00:00 +0000
+++ txaws/credentials.py 2009-08-21 14:50:25 +0000
@@ -0,0 +1,42 @@
1# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
2# Licenced under the txaws licence available at /LICENSE in the txaws source.
3
4"""Credentials for accessing AWS services."""
5
6import os
7
8from txaws.util import hmac_sha1
9
10
11__all__ = ['AWSCredentials']
12
13
14ENV_ACCESS_KEY = "AWS_ACCESS_KEY_ID"
15ENV_SECRET_KEY = "AWS_SECRET_ACCESS_KEY"
16
17
18class AWSCredentials(object):
19
20 def __init__(self, access_key="", secret_key=""):
21 """Create an AWSCredentials object.
22
23 @param access_key: The access key to use. If None the environment
24 variable AWS_ACCESS_KEY_ID is consulted.
25 @param secret_key: The secret key to use. If None the environment
26 variable AWS_SECRET_ACCESS_KEY is consulted.
27 """
28 self.access_key = access_key
29 self.secret_key = secret_key
30 if not self.access_key:
31 self.access_key = os.environ.get(ENV_ACCESS_KEY)
32 if not self.access_key:
33 raise ValueError("Could not find %s" % ENV_ACCESS_KEY)
34 # perform checks for secret key
35 if not self.secret_key:
36 self.secret_key = os.environ.get(ENV_SECRET_KEY)
37 if not self.secret_key:
38 raise ValueError("Could not find %s" % ENV_SECRET_KEY)
39
40 def sign(self, bytes):
41 """Sign some bytes."""
42 return hmac_sha1(self.secret_key, bytes)
043
=== removed file 'txaws/credentials.py'
--- txaws/credentials.py 2009-08-17 11:18:56 +0000
+++ txaws/credentials.py 1970-01-01 00:00:00 +0000
@@ -1,37 +0,0 @@
1# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
2# Licenced under the txaws licence available at /LICENSE in the txaws source.
3
4"""Credentials for accessing AWS services."""
5
6import os
7
8from txaws.util import *
9
10
11__all__ = ['AWSCredentials']
12
13
14class AWSCredentials(object):
15
16 def __init__(self, access_key=None, secret_key=None):
17 """Create an AWSCredentials object.
18
19 :param access_key: The access key to use. If None the environment
20 variable AWS_ACCESS_KEY_ID is consulted.
21 :param secret_key: The secret key to use. If None the environment
22 variable AWS_SECRET_ACCESS_KEY is consulted.
23 """
24 self.secret_key = secret_key
25 if self.secret_key is None:
26 self.secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
27 if self.secret_key is None:
28 raise ValueError('Could not find AWS_SECRET_ACCESS_KEY')
29 self.access_key = access_key
30 if self.access_key is None:
31 self.access_key = os.environ.get('AWS_ACCESS_KEY_ID')
32 if self.access_key is None:
33 raise ValueError('Could not find AWS_ACCESS_KEY_ID')
34
35 def sign(self, bytes):
36 """Sign some bytes."""
37 return hmac_sha1(self.secret_key, bytes)
380
=== modified file 'txaws/ec2/client.py'
--- txaws/ec2/client.py 2009-08-21 03:26:35 +0000
+++ txaws/ec2/client.py 2009-08-21 14:50:25 +0000
@@ -8,7 +8,8 @@
88
9from twisted.web.client import getPage9from twisted.web.client import getPage
1010
11from txaws import credentials11from txaws.credentials import AWSCredentials
12from txaws.service import AWSServiceEndpoint
12from txaws.util import iso8601time, XML13from txaws.util import iso8601time, XML
1314
1415
@@ -77,16 +78,16 @@
7778
78 name_space = '{http://ec2.amazonaws.com/doc/2008-12-01/}'79 name_space = '{http://ec2.amazonaws.com/doc/2008-12-01/}'
7980
80 def __init__(self, creds=None, query_factory=None):81 def __init__(self, creds=None, endpoint=None, query_factory=None):
81 """Create an EC2Client.82 """Create an EC2Client.
8283
83 @param creds: Explicit credentials to use. If None, credentials are84 @param creds: User authentication credentials to use.
84 inferred as per txaws.credentials.AWSCredentials.85 @param endpoint: The service URI.
86 @param query_factory: The class or function that produces a query
87 object for making requests to the EC2 service.
85 """88 """
86 if creds is None:89 self.creds = creds or AWSCredentials()
87 self.creds = credentials.AWSCredentials()90 self.endpoint = endpoint or AWSServiceEndpoint()
88 else:
89 self.creds = creds
90 if query_factory is None:91 if query_factory is None:
91 self.query_factory = Query92 self.query_factory = Query
92 else:93 else:
@@ -177,7 +178,8 @@
177 instanceset = {}178 instanceset = {}
178 for pos, instance_id in enumerate(instance_ids):179 for pos, instance_id in enumerate(instance_ids):
179 instanceset["InstanceId.%d" % (pos+1)] = instance_id180 instanceset["InstanceId.%d" % (pos+1)] = instance_id
180 q = self.query_factory('TerminateInstances', self.creds, instanceset)181 q = self.query_factory('TerminateInstances', self.creds, self.endpoint,
182 instanceset)
181 d = q.submit()183 d = q.submit()
182 return d.addCallback(self._parse_terminate_instances)184 return d.addCallback(self._parse_terminate_instances)
183185
@@ -200,24 +202,24 @@
200class Query(object):202class Query(object):
201 """A query that may be submitted to EC2."""203 """A query that may be submitted to EC2."""
202204
203 def __init__(self, action, creds, other_params=None, time_tuple=None):205 def __init__(self, action, creds, endpoint, other_params=None,
206 time_tuple=None):
204 """Create a Query to submit to EC2."""207 """Create a Query to submit to EC2."""
208 self.creds = creds
209 self.endpoint = endpoint
205 # Require params (2008-12-01 API):210 # Require params (2008-12-01 API):
206 # Version, SignatureVersion, SignatureMethod, Action, AWSAccessKeyId,211 # Version, SignatureVersion, SignatureMethod, Action, AWSAccessKeyId,
207 # Timestamp || Expires, Signature, 212 # Timestamp || Expires, Signature,
208 self.params = {'Version': '2008-12-01',213 self.params = {
214 'Version': '2008-12-01',
209 'SignatureVersion': '2',215 'SignatureVersion': '2',
210 'SignatureMethod': 'HmacSHA1',216 'SignatureMethod': 'HmacSHA1',
211 'Action': action,217 'Action': action,
212 'AWSAccessKeyId': creds.access_key,218 'AWSAccessKeyId': self.creds.access_key,
213 'Timestamp': iso8601time(time_tuple),219 'Timestamp': iso8601time(time_tuple),
214 }220 }
215 if other_params:221 if other_params:
216 self.params.update(other_params)222 self.params.update(other_params)
217 self.method = 'GET'
218 self.host = 'ec2.amazonaws.com'
219 self.uri = '/'
220 self.creds = creds
221223
222 def canonical_query_params(self):224 def canonical_query_params(self):
223 """Return the canonical query params (used in signing)."""225 """Return the canonical query params (used in signing)."""
@@ -230,18 +232,19 @@
230 """Encode a_string as per the canonicalisation encoding rules.232 """Encode a_string as per the canonicalisation encoding rules.
231233
232 See the AWS dev reference page 90 (2008-12-01 version).234 See the AWS dev reference page 90 (2008-12-01 version).
233 :return: a_string encoded.235 @return: a_string encoded.
234 """236 """
235 return quote(a_string, safe='~')237 return quote(a_string, safe='~')
236238
237 def signing_text(self):239 def signing_text(self):
238 """Return the text to be signed when signing the query."""240 """Return the text to be signed when signing the query."""
239 result = "%s\n%s\n%s\n%s" % (self.method, self.host, self.uri,241 result = "%s\n%s\n%s\n%s" % (self.endpoint.method, self.endpoint.host,
240 self.canonical_query_params())242 self.endpoint.path,
243 self.canonical_query_params())
241 return result244 return result
242245
243 def sign(self):246 def sign(self):
244 """Sign this query using its built in credentials.247 """Sign this query using its built in service.
245 248
246 This prepares it to be sent, and should be done as the last step before249 This prepares it to be sent, and should be done as the last step before
247 submitting the query. Signing is done automatically - this is a public250 submitting the query. Signing is done automatically - this is a public
@@ -256,9 +259,9 @@
256 def submit(self):259 def submit(self):
257 """Submit this query.260 """Submit this query.
258261
259 :return: A deferred from twisted.web.client.getPage262 @return: A deferred from twisted.web.client.getPage
260 """263 """
261 self.sign()264 self.sign()
262 url = 'http://%s%s?%s' % (self.host, self.uri,265 url = "%s?%s" % (self.endpoint.get_uri(),
263 self.canonical_query_params())266 self.canonical_query_params())
264 return getPage(url, method=self.method)267 return getPage(url, method=self.service.method)
265268
=== modified file 'txaws/ec2/tests/test_client.py'
--- txaws/ec2/tests/test_client.py 2009-08-21 03:26:35 +0000
+++ txaws/ec2/tests/test_client.py 2009-08-21 14:50:25 +0000
@@ -7,6 +7,7 @@
77
8from txaws.credentials import AWSCredentials8from txaws.credentials import AWSCredentials
9from txaws.ec2 import client9from txaws.ec2 import client
10from txaws.service import AWSServiceEndpoint, EC2_ENDPOINT_US
10from txaws.tests import TXAWSTestCase11from txaws.tests import TXAWSTestCase
1112
1213
@@ -117,7 +118,7 @@
117 self.assertEquals(instance.ramdisk_id, "id4")118 self.assertEquals(instance.ramdisk_id, "id4")
118119
119120
120class TestEC2Client(TXAWSTestCase):121class EC2ClientTestCase(TXAWSTestCase):
121 122
122 def test_init_no_creds(self):123 def test_init_no_creds(self):
123 os.environ['AWS_SECRET_ACCESS_KEY'] = 'foo'124 os.environ['AWS_SECRET_ACCESS_KEY'] = 'foo'
@@ -129,7 +130,7 @@
129 self.assertRaises(ValueError, client.EC2Client)130 self.assertRaises(ValueError, client.EC2Client)
130131
131 def test_init_explicit_creds(self):132 def test_init_explicit_creds(self):
132 creds = 'foo'133 creds = AWSCredentials("foo", "bar")
133 ec2 = client.EC2Client(creds=creds)134 ec2 = client.EC2Client(creds=creds)
134 self.assertEqual(creds, ec2.creds)135 self.assertEqual(creds, ec2.creds)
135136
@@ -162,7 +163,8 @@
162163
163164
164 def test_parse_reservation(self):165 def test_parse_reservation(self):
165 ec2 = client.EC2Client(creds='foo')166 creds = AWSCredentials("foo", "bar")
167 ec2 = client.EC2Client(creds=creds)
166 results = ec2._parse_instances(sample_describe_instances_result)168 results = ec2._parse_instances(sample_describe_instances_result)
167 self.check_parsed_instances(results)169 self.check_parsed_instances(results)
168170
@@ -170,25 +172,31 @@
170 class StubQuery(object):172 class StubQuery(object):
171 def __init__(stub, action, creds):173 def __init__(stub, action, creds):
172 self.assertEqual(action, 'DescribeInstances')174 self.assertEqual(action, 'DescribeInstances')
173 self.assertEqual('foo', creds)175 self.assertEqual(creds.access_key, "foo")
176 self.assertEqual(creds.secret_key, "bar")
174 def submit(self):177 def submit(self):
175 return succeed(sample_describe_instances_result)178 return succeed(sample_describe_instances_result)
176 ec2 = client.EC2Client(creds='foo', query_factory=StubQuery)179 creds = AWSCredentials("foo", "bar")
180 ec2 = client.EC2Client(creds, query_factory=StubQuery)
177 d = ec2.describe_instances()181 d = ec2.describe_instances()
178 d.addCallback(self.check_parsed_instances)182 d.addCallback(self.check_parsed_instances)
179 return d183 return d
180184
181 def test_terminate_instances(self):185 def test_terminate_instances(self):
182 class StubQuery(object):186 class StubQuery(object):
183 def __init__(stub, action, creds, other_params):187 def __init__(stub, action, creds, endpoint, other_params):
184 self.assertEqual(action, 'TerminateInstances')188 self.assertEqual(action, 'TerminateInstances')
185 self.assertEqual('foo', creds)189 self.assertEqual(creds.access_key, "foo")
190 self.assertEqual(creds.secret_key, "bar")
186 self.assertEqual(191 self.assertEqual(
187 {'InstanceId.1': 'i-1234', 'InstanceId.2': 'i-5678'},192 {'InstanceId.1': 'i-1234', 'InstanceId.2': 'i-5678'},
188 other_params)193 other_params)
189 def submit(self):194 def submit(self):
190 return succeed(sample_terminate_instances_result)195 return succeed(sample_terminate_instances_result)
191 ec2 = client.EC2Client(creds='foo', query_factory=StubQuery)196 creds = AWSCredentials("foo", "bar")
197 endpoint = AWSServiceEndpoint(uri=EC2_ENDPOINT_US)
198 ec2 = client.EC2Client(creds=creds, endpoint=endpoint,
199 query_factory=StubQuery)
192 d = ec2.terminate_instances('i-1234', 'i-5678')200 d = ec2.terminate_instances('i-1234', 'i-5678')
193 def check_transition(changes):201 def check_transition(changes):
194 self.assertEqual([('i-1234', 'running', 'shutting-down'),202 self.assertEqual([('i-1234', 'running', 'shutting-down'),
@@ -196,14 +204,15 @@
196 return d204 return d
197205
198206
199class TestQuery(TXAWSTestCase):207class QueryTestCase(TXAWSTestCase):
200208
201 def setUp(self):209 def setUp(self):
202 TXAWSTestCase.setUp(self)210 TXAWSTestCase.setUp(self)
203 self.creds = AWSCredentials('foo', 'bar')211 self.creds = AWSCredentials('foo', 'bar')
212 self.endpoint = AWSServiceEndpoint(uri=EC2_ENDPOINT_US)
204213
205 def test_init_minimum(self):214 def test_init_minimum(self):
206 query = client.Query('DescribeInstances', self.creds)215 query = client.Query('DescribeInstances', self.creds, self.endpoint)
207 self.assertTrue('Timestamp' in query.params)216 self.assertTrue('Timestamp' in query.params)
208 del query.params['Timestamp']217 del query.params['Timestamp']
209 self.assertEqual(218 self.assertEqual(
@@ -221,7 +230,7 @@
221 self.assertRaises(TypeError, client.Query, None)230 self.assertRaises(TypeError, client.Query, None)
222231
223 def test_init_other_args_are_params(self):232 def test_init_other_args_are_params(self):
224 query = client.Query('DescribeInstances', self.creds,233 query = client.Query('DescribeInstances', self.creds, self.endpoint,
225 {'InstanceId.0': '12345'},234 {'InstanceId.0': '12345'},
226 time_tuple=(2007,11,12,13,14,15,0,0,0))235 time_tuple=(2007,11,12,13,14,15,0,0,0))
227 self.assertEqual(236 self.assertEqual(
@@ -235,7 +244,7 @@
235 query.params)244 query.params)
236245
237 def test_sorted_params(self):246 def test_sorted_params(self):
238 query = client.Query('DescribeInstances', self.creds,247 query = client.Query('DescribeInstances', self.creds, self.endpoint,
239 {'fun': 'games'},248 {'fun': 'games'},
240 time_tuple=(2007,11,12,13,14,15,0,0,0))249 time_tuple=(2007,11,12,13,14,15,0,0,0))
241 self.assertEqual([250 self.assertEqual([
@@ -251,16 +260,16 @@
251 def test_encode_unreserved(self):260 def test_encode_unreserved(self):
252 all_unreserved = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'261 all_unreserved = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
253 'abcdefghijklmnopqrstuvwxyz0123456789-_.~')262 'abcdefghijklmnopqrstuvwxyz0123456789-_.~')
254 query = client.Query('DescribeInstances', self.creds)263 query = client.Query('DescribeInstances', self.creds, self.endpoint)
255 self.assertEqual(all_unreserved, query.encode(all_unreserved))264 self.assertEqual(all_unreserved, query.encode(all_unreserved))
256265
257 def test_encode_space(self):266 def test_encode_space(self):
258 """This may be just 'url encode', but the AWS manual isn't clear."""267 """This may be just 'url encode', but the AWS manual isn't clear."""
259 query = client.Query('DescribeInstances', self.creds)268 query = client.Query('DescribeInstances', self.creds, self.endpoint)
260 self.assertEqual('a%20space', query.encode('a space'))269 self.assertEqual('a%20space', query.encode('a space'))
261270
262 def test_canonical_query(self):271 def test_canonical_query(self):
263 query = client.Query('DescribeInstances', self.creds,272 query = client.Query('DescribeInstances', self.creds, self.endpoint,
264 {'fu n': 'g/ames', 'argwithnovalue':'',273 {'fu n': 'g/ames', 'argwithnovalue':'',
265 'InstanceId.1': 'i-1234'},274 'InstanceId.1': 'i-1234'},
266 time_tuple=(2007,11,12,13,14,15,0,0,0))275 time_tuple=(2007,11,12,13,14,15,0,0,0))
@@ -272,17 +281,17 @@
272 self.assertEqual(expected_query, query.canonical_query_params())281 self.assertEqual(expected_query, query.canonical_query_params())
273282
274 def test_signing_text(self):283 def test_signing_text(self):
275 query = client.Query('DescribeInstances', self.creds,284 query = client.Query('DescribeInstances', self.creds, self.endpoint,
276 time_tuple=(2007,11,12,13,14,15,0,0,0))285 time_tuple=(2007,11,12,13,14,15,0,0,0))
277 signing_text = ('GET\nec2.amazonaws.com\n/\n'286 signing_text = ('GET\n%s\n/\n' % self.endpoint.host +
278 'AWSAccessKeyId=foo&Action=DescribeInstances&'287 'AWSAccessKeyId=foo&Action=DescribeInstances&'
279 'SignatureMethod=HmacSHA1&SignatureVersion=2&'288 'SignatureMethod=HmacSHA1&SignatureVersion=2&'
280 'Timestamp=2007-11-12T13%3A14%3A15Z&Version=2008-12-01')289 'Timestamp=2007-11-12T13%3A14%3A15Z&Version=2008-12-01')
281 self.assertEqual(signing_text, query.signing_text())290 self.assertEqual(signing_text, query.signing_text())
282291
283 def test_sign(self):292 def test_sign(self):
284 query = client.Query('DescribeInstances', self.creds,293 query = client.Query('DescribeInstances', self.creds, self.endpoint,
285 time_tuple=(2007,11,12,13,14,15,0,0,0))294 time_tuple=(2007,11,12,13,14,15,0,0,0))
286 query.sign()295 query.sign()
287 self.assertEqual('4hEtLuZo9i6kuG3TOXvRQNOrE/U=',296 self.assertEqual('JuCpwFA2H4OVF3Ql/lAQs+V6iMc=',
288 query.params['Signature'])297 query.params['Signature'])
289298
=== added file 'txaws/ec2/tests/test_service.py'
=== added file 'txaws/service.py'
--- txaws/service.py 1970-01-01 00:00:00 +0000
+++ txaws/service.py 2009-08-21 20:50:36 +0000
@@ -0,0 +1,94 @@
1# Copyright (C) 2009 Duncan McGreggor <duncan@canonical.com>
2# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
3# Licenced under the txaws licence available at /LICENSE in the txaws source.
4
5import os
6
7from twisted.web.client import _parse
8
9
10
11__all__ = ["AWSServiceEndpoint", "AWSServiceRegion"]
12
13
14REGION_US = "US"
15REGION_EU = "EU"
16EC2_ENDPOINT_US = "https://us-east-1.ec2.amazonaws.com/"
17EC2_ENDPOINT_EU = "https://eu-west-1.ec2.amazonaws.com/"
18DEFAULT_PORT = 80
19
20
21class AWSServiceEndpoint(object):
22 """
23 @param uri: The URL for the service.
24 @param method: The HTTP method used when accessing a service.
25 """
26
27 def __init__(self, uri="", method="GET"):
28 self.host = ""
29 self.port = DEFAULT_PORT
30 self.path = "/"
31 self.method = method
32 self._parse_uri(uri)
33 if not self.scheme:
34 self.scheme = "http"
35
36 def _parse_uri(self, uri):
37 scheme, host, port, path = _parse(
38 str(uri), defaultPort=DEFAULT_PORT)
39 self.scheme = scheme
40 self.host = host
41 self.port = port
42 self.path = path
43
44 def set_path(self, path):
45 self.path = path
46
47 def get_uri(self):
48 """Get a URL representation of the service."""
49 uri = "%s://%s" % (self.scheme, self.host)
50 if self.port and self.port != DEFAULT_PORT:
51 uri = "%s:%s" % (uri, self.port)
52 return uri + self.path
53
54
55class AWSServiceRegion(object):
56 """
57 This object represents a collection of client factories that use the same
58 credentials. With Amazon, this collection is associated with a region
59 (e.g., US or EU).
60 """
61 def __init__(self, creds=None, region=REGION_US):
62 self.creds = creds
63 self._clients = {}
64 if region == REGION_US:
65 ec2_endpoint = EC2_ENDPOINT_US
66 elif region == REGION_EU:
67 ec2_endpoint = EC2_ENDPOINT_EU
68 self.ec2_endpoint = AWSServiceEndpoint(uri=ec2_endpoint)
69
70 def get_client(self, cls, *args, **kwds):
71 key = str(cls) + str(args) + str(kwds)
72 instance = self._clients.get(key)
73 if not instance:
74 instance = cls(*args, **kwds)
75 self._clients[key] = instance
76 return instance
77
78 def get_ec2_client(self, creds=None):
79 from txaws.ec2.client import EC2Client
80
81 if creds:
82 self.creds = creds
83 return self.get_client(EC2Client, creds=creds,
84 endpoint=self.ec2_endpoint, query_factory=None)
85
86 def get_s3_client(self):
87 raise NotImplementedError
88
89 def get_simpledb_client(self):
90 raise NotImplementedError
91
92 def get_sqs_client(self):
93 raise NotImplementedError
94
095
=== modified file 'txaws/storage/client.py'
--- txaws/storage/client.py 2009-08-20 12:15:12 +0000
+++ txaws/storage/client.py 2009-08-21 14:50:25 +0000
@@ -25,19 +25,18 @@
25class S3Request(object):25class S3Request(object):
2626
27 def __init__(self, verb, bucket=None, object_name=None, data='',27 def __init__(self, verb, bucket=None, object_name=None, data='',
28 content_type=None,28 content_type=None, metadata={}, creds=None, endpoint=None):
29 metadata={}, root_uri='https://s3.amazonaws.com', creds=None):
30 self.verb = verb29 self.verb = verb
31 self.bucket = bucket30 self.bucket = bucket
32 self.object_name = object_name31 self.object_name = object_name
33 self.data = data32 self.data = data
34 self.content_type = content_type33 self.content_type = content_type
35 self.metadata = metadata34 self.metadata = metadata
36 self.root_uri = root_uri
37 self.creds = creds35 self.creds = creds
36 self.endpoint = endpoint or self.get_uri()
38 self.date = datetimeToString()37 self.date = datetimeToString()
3938
40 def get_uri_path(self):39 def get_path(self):
41 path = '/'40 path = '/'
42 if self.bucket is not None:41 if self.bucket is not None:
43 path += self.bucket42 path += self.bucket
@@ -46,7 +45,8 @@
46 return path45 return path
4746
48 def get_uri(self):47 def get_uri(self):
49 return self.root_uri + self.get_uri_path()48 self.endpoint.set_path(self.get_path())
49 return self.endpoint.get_uri()
5050
51 def get_headers(self):51 def get_headers(self):
52 headers = {'Content-Length': len(self.data),52 headers = {'Content-Length': len(self.data),
@@ -66,7 +66,7 @@
66 return headers66 return headers
6767
68 def get_canonicalized_resource(self):68 def get_canonicalized_resource(self):
69 return self.get_uri_path()69 return self.get_path()
7070
71 def get_canonicalized_amz_headers(self, headers):71 def get_canonicalized_amz_headers(self, headers):
72 result = ''72 result = ''
@@ -76,12 +76,12 @@
76 return ''.join('%s:%s\n' % (name, value) for name, value in headers)76 return ''.join('%s:%s\n' % (name, value) for name, value in headers)
7777
78 def get_signature(self, headers):78 def get_signature(self, headers):
79 text = self.verb + '\n'79 text = (self.verb + '\n' +
80 text += headers.get('Content-MD5', '') + '\n'80 headers.get('Content-MD5', '') + '\n' +
81 text += headers.get('Content-Type', '') + '\n'81 headers.get('Content-Type', '') + '\n' +
82 text += headers.get('Date', '') + '\n'82 headers.get('Date', '') + '\n' +
83 text += self.get_canonicalized_amz_headers(headers)83 self.get_canonicalized_amz_headers(headers) +
84 text += self.get_canonicalized_resource()84 self.get_canonicalized_resource())
85 return self.creds.sign(text)85 return self.creds.sign(text)
8686
87 def submit(self):87 def submit(self):
@@ -94,20 +94,21 @@
9494
95class S3(object):95class S3(object):
9696
97 root_uri = 'https://s3.amazonaws.com/'
98 request_factory = S3Request97 request_factory = S3Request
9998
100 def __init__(self, creds):99 def __init__(self, creds, endpoint):
101 self.creds = creds100 self.creds = creds
101 self.endpoint = endpoint
102102
103 def make_request(self, *a, **kw):103 def make_request(self, *a, **kw):
104 """104 """
105 Create a request with the arguments passed in.105 Create a request with the arguments passed in.
106106
107 This uses the request_factory attribute, adding the credentials to the107 This uses the request_factory attribute, adding the creds and endpoint
108 arguments passed in.108 to the arguments passed in.
109 """109 """
110 return self.request_factory(creds=self.creds, *a, **kw)110 return self.request_factory(creds=self.creds, endpoint=self.endpoint,
111 *a, **kw)
111112
112 def _parse_bucket_list(self, response):113 def _parse_bucket_list(self, response):
113 """114 """
114115
=== modified file 'txaws/storage/tests/test_client.py'
--- txaws/storage/tests/test_client.py 2009-08-20 12:15:12 +0000
+++ txaws/storage/tests/test_client.py 2009-08-21 14:50:25 +0000
@@ -5,6 +5,7 @@
5from twisted.internet.defer import succeed5from twisted.internet.defer import succeed
66
7from txaws.credentials import AWSCredentials7from txaws.credentials import AWSCredentials
8from txaws.service import AWSServiceEndpoint
8from txaws.storage.client import S3, S3Request9from txaws.storage.client import S3, S3Request
9from txaws.tests import TXAWSTestCase10from txaws.tests import TXAWSTestCase
10from txaws.util import calculate_md511from txaws.util import calculate_md5
@@ -18,10 +19,10 @@
18 return succeed('')19 return succeed('')
1920
2021
21class RequestTests(TXAWSTestCase):22class RequestTestCase(TXAWSTestCase):
2223
23 creds = AWSCredentials(access_key='0PN5J17HBGZHT7JJ3X82',24 creds = AWSCredentials(access_key='fookeyid', secret_key='barsecretkey')
24 secret_key='uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o')25 endpoint = AWSServiceEndpoint("https://s3.amazonaws.com/")
2526
26 def test_objectRequest(self):27 def test_objectRequest(self):
27 """28 """
@@ -31,18 +32,22 @@
31 DIGEST = 'zhdB6gwvocWv/ourYUWMxA=='32 DIGEST = 'zhdB6gwvocWv/ourYUWMxA=='
3233
33 request = S3Request('PUT', 'somebucket', 'object/name/here', DATA,34 request = S3Request('PUT', 'somebucket', 'object/name/here', DATA,
34 content_type='text/plain', metadata={'foo': 'bar'})35 content_type='text/plain', metadata={'foo': 'bar'},
36 creds=self.creds, endpoint=self.endpoint)
37 request.get_signature = lambda headers: "TESTINGSIG="
35 self.assertEqual(request.verb, 'PUT')38 self.assertEqual(request.verb, 'PUT')
36 self.assertEqual(39 self.assertEqual(
37 request.get_uri(),40 request.get_uri(),
38 'https://s3.amazonaws.com/somebucket/object/name/here')41 'https://s3.amazonaws.com/somebucket/object/name/here')
39 headers = request.get_headers()42 headers = request.get_headers()
40 self.assertNotEqual(headers.pop('Date'), '')43 self.assertNotEqual(headers.pop('Date'), '')
41 self.assertEqual(headers,44 self.assertEqual(
42 {'Content-Type': 'text/plain',45 headers, {
43 'Content-Length': len(DATA),46 'Authorization': 'AWS fookeyid:TESTINGSIG=',
44 'Content-MD5': DIGEST,47 'Content-Type': 'text/plain',
45 'x-amz-meta-foo': 'bar'})48 'Content-Length': len(DATA),
49 'Content-MD5': DIGEST,
50 'x-amz-meta-foo': 'bar'})
46 self.assertEqual(request.data, 'objectData')51 self.assertEqual(request.data, 'objectData')
4752
48 def test_bucketRequest(self):53 def test_bucketRequest(self):
@@ -51,22 +56,27 @@
51 """56 """
52 DIGEST = '1B2M2Y8AsgTpgAmY7PhCfg=='57 DIGEST = '1B2M2Y8AsgTpgAmY7PhCfg=='
5358
54 request = S3Request('GET', 'somebucket')59 request = S3Request('GET', 'somebucket', creds=self.creds,
60 endpoint=self.endpoint)
61 request.get_signature = lambda headers: "TESTINGSIG="
55 self.assertEqual(request.verb, 'GET')62 self.assertEqual(request.verb, 'GET')
56 self.assertEqual(63 self.assertEqual(
57 request.get_uri(), 'https://s3.amazonaws.com/somebucket')64 request.get_uri(), 'https://s3.amazonaws.com/somebucket')
58 headers = request.get_headers()65 headers = request.get_headers()
59 self.assertNotEqual(headers.pop('Date'), '')66 self.assertNotEqual(headers.pop('Date'), '')
60 self.assertEqual(headers,67 self.assertEqual(
61 {'Content-Length': 0,68 headers, {
62 'Content-MD5': DIGEST})69 'Authorization': 'AWS fookeyid:TESTINGSIG=',
70 'Content-Length': 0,
71 'Content-MD5': DIGEST})
63 self.assertEqual(request.data, '')72 self.assertEqual(request.data, '')
6473
65 def test_submit(self):74 def test_submit(self):
66 """75 """
67 Submitting the request should invoke getPage correctly.76 Submitting the request should invoke getPage correctly.
68 """77 """
69 request = StubbedS3Request('GET', 'somebucket')78 request = StubbedS3Request('GET', 'somebucket', creds=self.creds,
79 endpoint=self.endpoint)
7080
71 def _postCheck(result):81 def _postCheck(result):
72 self.assertEqual(result, '')82 self.assertEqual(result, '')
@@ -80,13 +90,14 @@
80 return request.submit().addCallback(_postCheck)90 return request.submit().addCallback(_postCheck)
8191
82 def test_authenticationTestCases(self):92 def test_authenticationTestCases(self):
83 req = S3Request('GET', creds=self.creds)93 request = S3Request('GET', creds=self.creds, endpoint=self.endpoint)
84 req.date = 'Wed, 28 Mar 2007 01:29:59 +0000'94 request.get_signature = lambda headers: "TESTINGSIG="
95 request.date = 'Wed, 28 Mar 2007 01:29:59 +0000'
8596
86 headers = req.get_headers()97 headers = request.get_headers()
87 self.assertEqual(98 self.assertEqual(
88 headers['Authorization'], 99 headers['Authorization'],
89 'AWS 0PN5J17HBGZHT7JJ3X82:jF7L3z/FTV47vagZzhKupJ9oNig=')100 'AWS fookeyid:TESTINGSIG=')
90101
91102
92class InertRequest(S3Request):103class InertRequest(S3Request):
@@ -153,16 +164,18 @@
153 TXAWSTestCase.setUp(self)164 TXAWSTestCase.setUp(self)
154 self.creds = AWSCredentials(165 self.creds = AWSCredentials(
155 access_key='accessKey', secret_key='secretKey')166 access_key='accessKey', secret_key='secretKey')
156 self.s3 = TestableS3(creds=self.creds)167 self.endpoint = AWSServiceEndpoint()
168 self.s3 = TestableS3(creds=self.creds, endpoint=self.endpoint)
157169
158 def test_make_request(self):170 def test_make_request(self):
159 """171 """
160 Test that make_request passes in the service credentials.172 Test that make_request passes in the credentials object.
161 """173 """
162 marker = object()174 marker = object()
163175
164 def _cb(*a, **kw):176 def _cb(*a, **kw):
165 self.assertEqual(kw['creds'], self.creds)177 self.assertEqual(kw['creds'], self.creds)
178 self.assertEqual(kw['endpoint'], self.endpoint)
166 return marker179 return marker
167180
168 self.s3.request_factory = _cb181 self.s3.request_factory = _cb
169182
=== added file 'txaws/tests/test_credentials.py'
--- txaws/tests/test_credentials.py 1970-01-01 00:00:00 +0000
+++ txaws/tests/test_credentials.py 2009-08-21 14:50:25 +0000
@@ -0,0 +1,53 @@
1# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
2# Licenced under the txaws licence available at /LICENSE in the txaws source.
3
4import os
5
6from twisted.trial.unittest import TestCase
7
8from txaws.credentials import AWSCredentials, ENV_ACCESS_KEY, ENV_SECRET_KEY
9from txaws.tests import TXAWSTestCase
10
11from txaws.tests import TXAWSTestCase
12
13
14class TestCredentials(TXAWSTestCase):
15
16 def setUp(self):
17 self.addCleanup(self.clean_environment)
18
19 def clean_environment(self):
20 if os.environ.has_key(ENV_ACCESS_KEY):
21 del os.environ[ENV_ACCESS_KEY]
22 if os.environ.has_key(ENV_SECRET_KEY):
23 del os.environ[ENV_SECRET_KEY]
24
25 def test_no_access_errors(self):
26 # Without anything in os.environ, AWSService() blows up
27 os.environ[ENV_SECRET_KEY] = "bar"
28 self.assertRaises(ValueError, AWSCredentials)
29
30 def test_no_secret_errors(self):
31 # Without anything in os.environ, AWSService() blows up
32 os.environ[ENV_ACCESS_KEY] = "foo"
33 self.assertRaises(ValueError, AWSCredentials)
34
35 def test_found_values_used(self):
36 os.environ[ENV_ACCESS_KEY] = "foo"
37 os.environ[ENV_SECRET_KEY] = "bar"
38 service = AWSCredentials()
39 self.assertEqual("foo", service.access_key)
40 self.assertEqual("bar", service.secret_key)
41 self.clean_environment()
42
43 def test_explicit_access_key(self):
44 os.environ[ENV_SECRET_KEY] = "foo"
45 service = AWSCredentials(access_key="bar")
46 self.assertEqual("foo", service.secret_key)
47 self.assertEqual("bar", service.access_key)
48
49 def test_explicit_secret_key(self):
50 os.environ[ENV_ACCESS_KEY] = "bar"
51 service = AWSCredentials(secret_key="foo")
52 self.assertEqual("foo", service.secret_key)
53 self.assertEqual("bar", service.access_key)
054
=== removed file 'txaws/tests/test_credentials.py'
--- txaws/tests/test_credentials.py 2009-08-20 12:15:12 +0000
+++ txaws/tests/test_credentials.py 1970-01-01 00:00:00 +0000
@@ -1,41 +0,0 @@
1# Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
2# Licenced under the txaws licence available at /LICENSE in the txaws source.
3
4import os
5
6from twisted.trial.unittest import TestCase
7
8from txaws.credentials import AWSCredentials
9from txaws.tests import TXAWSTestCase
10
11
12class TestCredentials(TXAWSTestCase):
13
14 def test_no_access_errors(self):
15 # Without anything in os.environ, AWSCredentials() blows up
16 os.environ['AWS_SECRET_ACCESS_KEY'] = 'foo'
17 self.assertRaises(Exception, AWSCredentials)
18
19 def test_no_secret_errors(self):
20 # Without anything in os.environ, AWSCredentials() blows up
21 os.environ['AWS_ACCESS_KEY_ID'] = 'bar'
22 self.assertRaises(Exception, AWSCredentials)
23
24 def test_found_values_used(self):
25 os.environ['AWS_SECRET_ACCESS_KEY'] = 'foo'
26 os.environ['AWS_ACCESS_KEY_ID'] = 'bar'
27 creds = AWSCredentials()
28 self.assertEqual('foo', creds.secret_key)
29 self.assertEqual('bar', creds.access_key)
30
31 def test_explicit_access_key(self):
32 os.environ['AWS_SECRET_ACCESS_KEY'] = 'foo'
33 creds = AWSCredentials(access_key='bar')
34 self.assertEqual('foo', creds.secret_key)
35 self.assertEqual('bar', creds.access_key)
36
37 def test_explicit_secret_key(self):
38 os.environ['AWS_ACCESS_KEY_ID'] = 'bar'
39 creds = AWSCredentials(secret_key='foo')
40 self.assertEqual('foo', creds.secret_key)
41 self.assertEqual('bar', creds.access_key)
420
=== added file 'txaws/tests/test_service.py'
--- txaws/tests/test_service.py 1970-01-01 00:00:00 +0000
+++ txaws/tests/test_service.py 2009-08-21 21:13:27 +0000
@@ -0,0 +1,105 @@
1# Copyright (C) 2009 Duncan McGreggor <duncan@canonical.com>
2# Licenced under the txaws licence available at /LICENSE in the txaws source.
3
4import os
5
6from txaws.credentials import AWSCredentials
7from txaws.ec2.client import EC2Client
8from txaws.service import AWSServiceEndpoint, AWSServiceRegion, EC2_ENDPOINT_US
9from txaws.tests import TXAWSTestCase
10
11class AWSServiceEndpointTestCase(TXAWSTestCase):
12
13 def setUp(self):
14 self.endpoint = AWSServiceEndpoint(uri="http://my.service/da_endpoint")
15
16 def test_simple_creation(self):
17 endpoint = AWSServiceEndpoint()
18 self.assertEquals(endpoint.scheme, "http")
19 self.assertEquals(endpoint.host, "")
20 self.assertEquals(endpoint.port, 80)
21 self.assertEquals(endpoint.path, "/")
22 self.assertEquals(endpoint.method, "GET")
23
24 def test_parse_uri(self):
25 self.assertEquals(self.endpoint.scheme, "http")
26 self.assertEquals(self.endpoint.host, "my.service")
27 self.assertEquals(self.endpoint.port, 80)
28 self.assertEquals(self.endpoint.path, "/da_endpoint")
29
30 def test_parse_uri_https_and_custom_port(self):
31 endpoint = AWSServiceEndpoint(uri="https://my.service:8080/endpoint")
32 self.assertEquals(endpoint.scheme, "https")
33 self.assertEquals(endpoint.host, "my.service")
34 self.assertEquals(endpoint.port, 8080)
35 self.assertEquals(endpoint.path, "/endpoint")
36
37 def test_custom_method(self):
38 endpoint = AWSServiceEndpoint(uri="http://service/endpoint",
39 method="PUT")
40 self.assertEquals(endpoint.method, "PUT")
41
42 def test_get_uri(self):
43 uri = self.endpoint.get_uri()
44 self.assertEquals(uri, "http://my.service/da_endpoint")
45
46 def test_get_uri_custom_port(self):
47 uri = "https://my.service:8080/endpoint"
48 endpoint = AWSServiceEndpoint(uri=uri)
49 new_uri = endpoint.get_uri()
50 self.assertEquals(new_uri, uri)
51
52 def test_set_path(self):
53 original_path = self.endpoint.path
54 self.endpoint.set_path("/newpath")
55 self.assertEquals(
56 self.endpoint.get_uri(),
57 "http://my.service/newpath")
58
59
60class AWSServiceRegionTestCase(TXAWSTestCase):
61
62 def setUp(self):
63 self.creds = AWSCredentials("foo", "bar")
64 self.region = AWSServiceRegion(creds=self.creds)
65
66 def test_simple_creation(self):
67 self.assertEquals(self.creds, self.region.creds)
68 self.assertEquals(self.region._clients, {})
69 self.assertEquals(self.region.ec2_endpoint.get_uri(), EC2_ENDPOINT_US)
70
71 def test_get_client_with_empty_cache(self):
72 key = str(EC2Client) + str(self.creds) + str(self.region.ec2_endpoint)
73 original_client = self.region._clients.get(key)
74 new_client = self.region.get_client(
75 EC2Client, self.creds, self.region.ec2_endpoint)
76 self.assertEquals(original_client, None)
77 self.assertNotEquals(original_client, new_client)
78 self.assertTrue(isinstance(new_client, EC2Client))
79
80 def test_get_client_from_cache(self):
81 client1 = self.region.get_client(
82 EC2Client, self.creds, self.region.ec2_endpoint)
83 client2 = self.region.get_client(
84 EC2Client, self.creds, self.region.ec2_endpoint)
85 self.assertTrue(isinstance(client1, EC2Client))
86 self.assertTrue(isinstance(client2, EC2Client))
87 self.assertEquals(client2, client2)
88
89 def test_get_ec2_client_from_cache(self):
90 client1 = self.region.get_ec2_client(self.creds)
91 client2 = self.region.get_ec2_client(self.creds)
92 self.assertEquals(self.creds, self.region.creds)
93 self.assertTrue(isinstance(client1, EC2Client))
94 self.assertTrue(isinstance(client2, EC2Client))
95 self.assertEquals(client2, client2)
96
97
98 def test_get_s3_client(self):
99 self.assertRaises(NotImplementedError, self.region.get_s3_client)
100
101 def test_get_simpledb_client(self):
102 self.assertRaises(NotImplementedError, self.region.get_simpledb_client)
103
104 def test_get_sqs_client(self):
105 self.assertRaises(NotImplementedError, self.region.get_sqs_client)

Subscribers

People subscribed via source and target branches