summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lindsley <daniel@toastdriven.com>2014-01-28 12:29:58 -0800
committerDaniel Lindsley <daniel@toastdriven.com>2014-01-28 14:18:38 -0800
commit8fa3b6cbf435193dbf06bf4095af903e0fca5133 (patch)
treee8d842a7e6dd7841663f355102c5e8911374f6bb
parentfd6b6326d68eb232e6901e3f1fb4c6b1e0790971 (diff)
downloadboto-8fa3b6cbf435193dbf06bf4095af903e0fca5133.tar.gz
Started change to load endpoints from JSON.
This is just SNS/SES for the moment, for purposes of getting early review feedback. Still needs tests & docs, plus integration into the rest of the services.
-rw-r--r--MANIFEST.in1
-rw-r--r--boto/__init__.py1
-rw-r--r--boto/endpoints.json284
-rw-r--r--boto/pyami/config.py12
-rw-r--r--boto/regioninfo.py125
-rw-r--r--boto/ses/__init__.py9
-rw-r--r--boto/sns/__init__.py34
-rw-r--r--scripts/rebuild_endpoints.py54
-rw-r--r--setup.py5
9 files changed, 479 insertions, 46 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index da3dfb3a..0b29a23f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,6 +3,7 @@ include README.rst
include boto/file/README
include .gitignore
include pylintrc
+include boto/endpoints.json
include boto/pyami/copybot.cfg
include boto/services/sonofmmm.cfg
include boto/mturk/test/*.doctest
diff --git a/boto/__init__.py b/boto/__init__.py
index a4d6c35f..bb322bbb 100644
--- a/boto/__init__.py
+++ b/boto/__init__.py
@@ -58,6 +58,7 @@ TOO_LONG_DNS_NAME_COMP = re.compile(r'[-_a-z0-9]{64}')
GENERATION_RE = re.compile(r'(?P<versionless_uri_str>.+)'
r'#(?P<generation>[0-9]+)$')
VERSION_RE = re.compile('(?P<versionless_uri_str>.+)#(?P<version_id>.+)$')
+ENDPOINTS_PATH = os.path.join(os.path.dirname(__file__), 'endpoints.json')
def init_logging():
diff --git a/boto/endpoints.json b/boto/endpoints.json
new file mode 100644
index 00000000..1f55865b
--- /dev/null
+++ b/boto/endpoints.json
@@ -0,0 +1,284 @@
+{
+ "autoscaling": {
+ "ap-northeast-1": "autoscaling.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "autoscaling.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "autoscaling.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "autoscaling.eu-west-1.amazonaws.com",
+ "sa-east-1": "autoscaling.sa-east-1.amazonaws.com",
+ "us-east-1": "autoscaling.us-east-1.amazonaws.com",
+ "us-gov-west-1": "autoscaling.us-gov-west-1.amazonaws.com",
+ "us-west-1": "autoscaling.us-west-1.amazonaws.com",
+ "us-west-2": "autoscaling.us-west-2.amazonaws.com"
+ },
+ "cloudformation": {
+ "ap-northeast-1": "cloudformation.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "cloudformation.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "cloudformation.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "cloudformation.eu-west-1.amazonaws.com",
+ "sa-east-1": "cloudformation.sa-east-1.amazonaws.com",
+ "us-east-1": "cloudformation.us-east-1.amazonaws.com",
+ "us-west-1": "cloudformation.us-west-1.amazonaws.com",
+ "us-west-2": "cloudformation.us-west-2.amazonaws.com"
+ },
+ "cloudfront": {
+ "ap-northeast-1": "cloudfront.amazonaws.com",
+ "ap-southeast-1": "cloudfront.amazonaws.com",
+ "ap-southeast-2": "cloudfront.amazonaws.com",
+ "eu-west-1": "cloudfront.amazonaws.com",
+ "sa-east-1": "cloudfront.amazonaws.com",
+ "us-east-1": "cloudfront.amazonaws.com",
+ "us-west-1": "cloudfront.amazonaws.com",
+ "us-west-2": "cloudfront.amazonaws.com"
+ },
+ "cloudsearch": {
+ "ap-southeast-1": "cloudsearch.ap-southeast-1.amazonaws.com",
+ "eu-west-1": "cloudsearch.eu-west-1.amazonaws.com",
+ "us-east-1": "cloudsearch.us-east-1.amazonaws.com",
+ "us-west-1": "cloudsearch.us-west-1.amazonaws.com",
+ "us-west-2": "cloudsearch.us-west-2.amazonaws.com"
+ },
+ "cloudtrail": {
+ "us-east-1": "cloudtrail.us-east-1.amazonaws.com",
+ "us-west-2": "cloudtrail.us-west-2.amazonaws.com"
+ },
+ "cloudwatch": {
+ "ap-northeast-1": "monitoring.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "monitoring.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "monitoring.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "monitoring.eu-west-1.amazonaws.com",
+ "sa-east-1": "monitoring.sa-east-1.amazonaws.com",
+ "us-east-1": "monitoring.us-east-1.amazonaws.com",
+ "us-gov-west-1": "monitoring.us-gov-west-1.amazonaws.com",
+ "us-west-1": "monitoring.us-west-1.amazonaws.com",
+ "us-west-2": "monitoring.us-west-2.amazonaws.com"
+ },
+ "datapipeline": {
+ "us-east-1": "datapipeline.us-east-1.amazonaws.com"
+ },
+ "directconnect": {
+ "ap-northeast-1": "directconnect.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "directconnect.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "directconnect.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "directconnect.eu-west-1.amazonaws.com",
+ "sa-east-1": "directconnect.sa-east-1.amazonaws.com",
+ "us-east-1": "directconnect.us-east-1.amazonaws.com",
+ "us-west-1": "directconnect.us-west-1.amazonaws.com",
+ "us-west-2": "directconnect.us-west-2.amazonaws.com"
+ },
+ "dynamodb": {
+ "ap-northeast-1": "dynamodb.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "dynamodb.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "dynamodb.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "dynamodb.eu-west-1.amazonaws.com",
+ "sa-east-1": "dynamodb.sa-east-1.amazonaws.com",
+ "us-east-1": "dynamodb.us-east-1.amazonaws.com",
+ "us-gov-west-1": "dynamodb.us-gov-west-1.amazonaws.com",
+ "us-west-1": "dynamodb.us-west-1.amazonaws.com",
+ "us-west-2": "dynamodb.us-west-2.amazonaws.com"
+ },
+ "ec2": {
+ "ap-northeast-1": "ec2.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "ec2.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "ec2.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "ec2.eu-west-1.amazonaws.com",
+ "sa-east-1": "ec2.sa-east-1.amazonaws.com",
+ "us-east-1": "ec2.us-east-1.amazonaws.com",
+ "us-gov-west-1": "ec2.us-gov-west-1.amazonaws.com",
+ "us-west-1": "ec2.us-west-1.amazonaws.com",
+ "us-west-2": "ec2.us-west-2.amazonaws.com"
+ },
+ "elasticache": {
+ "ap-northeast-1": "elasticache.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "elasticache.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "elasticache.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "elasticache.eu-west-1.amazonaws.com",
+ "sa-east-1": "elasticache.sa-east-1.amazonaws.com",
+ "us-east-1": "elasticache.us-east-1.amazonaws.com",
+ "us-west-1": "elasticache.us-west-1.amazonaws.com",
+ "us-west-2": "elasticache.us-west-2.amazonaws.com"
+ },
+ "elasticbeanstalk": {
+ "ap-northeast-1": "elasticbeanstalk.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "elasticbeanstalk.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "elasticbeanstalk.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "elasticbeanstalk.eu-west-1.amazonaws.com",
+ "sa-east-1": "elasticbeanstalk.sa-east-1.amazonaws.com",
+ "us-east-1": "elasticbeanstalk.us-east-1.amazonaws.com",
+ "us-west-1": "elasticbeanstalk.us-west-1.amazonaws.com",
+ "us-west-2": "elasticbeanstalk.us-west-2.amazonaws.com"
+ },
+ "elasticloadbalancing": {
+ "ap-northeast-1": "elasticloadbalancing.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "elasticloadbalancing.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "elasticloadbalancing.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "elasticloadbalancing.eu-west-1.amazonaws.com",
+ "sa-east-1": "elasticloadbalancing.sa-east-1.amazonaws.com",
+ "us-east-1": "elasticloadbalancing.us-east-1.amazonaws.com",
+ "us-gov-west-1": "elasticloadbalancing.us-gov-west-1.amazonaws.com",
+ "us-west-1": "elasticloadbalancing.us-west-1.amazonaws.com",
+ "us-west-2": "elasticloadbalancing.us-west-2.amazonaws.com"
+ },
+ "elasticmapreduce": {
+ "ap-northeast-1": "elasticmapreduce.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "elasticmapreduce.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "elasticmapreduce.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "elasticmapreduce.eu-west-1.amazonaws.com",
+ "sa-east-1": "elasticmapreduce.sa-east-1.amazonaws.com",
+ "us-east-1": "elasticmapreduce.us-east-1.amazonaws.com",
+ "us-gov-west-1": "elasticmapreduce.us-gov-west-1.amazonaws.com",
+ "us-west-1": "elasticmapreduce.us-west-1.amazonaws.com",
+ "us-west-2": "elasticmapreduce.us-west-2.amazonaws.com"
+ },
+ "elastictranscoder": {
+ "ap-northeast-1": "elastictranscoder.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "elastictranscoder.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "elastictranscoder.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "elastictranscoder.eu-west-1.amazonaws.com",
+ "sa-east-1": "elastictranscoder.sa-east-1.amazonaws.com",
+ "us-east-1": "elastictranscoder.us-east-1.amazonaws.com",
+ "us-west-1": "elastictranscoder.us-west-1.amazonaws.com",
+ "us-west-2": "elastictranscoder.us-west-2.amazonaws.com"
+ },
+ "glacier": {
+ "ap-northeast-1": "glacier.ap-northeast-1.amazonaws.com",
+ "ap-southeast-2": "glacier.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "glacier.eu-west-1.amazonaws.com",
+ "us-east-1": "glacier.us-east-1.amazonaws.com",
+ "us-west-1": "glacier.us-west-1.amazonaws.com",
+ "us-west-2": "glacier.us-west-2.amazonaws.com"
+ },
+ "iam": {
+ "ap-northeast-1": "iam.amazonaws.com",
+ "ap-southeast-1": "iam.amazonaws.com",
+ "ap-southeast-2": "iam.amazonaws.com",
+ "eu-west-1": "iam.amazonaws.com",
+ "sa-east-1": "iam.amazonaws.com",
+ "us-east-1": "iam.amazonaws.com",
+ "us-gov-west-1": "iam.us-gov.amazonaws.com",
+ "us-west-1": "iam.amazonaws.com",
+ "us-west-2": "iam.amazonaws.com"
+ },
+ "importexport": {
+ "ap-northeast-1": "importexport.amazonaws.com",
+ "ap-southeast-1": "importexport.amazonaws.com",
+ "ap-southeast-2": "importexport.amazonaws.com",
+ "eu-west-1": "importexport.amazonaws.com",
+ "sa-east-1": "importexport.amazonaws.com",
+ "us-east-1": "importexport.amazonaws.com",
+ "us-west-1": "importexport.amazonaws.com",
+ "us-west-2": "importexport.amazonaws.com"
+ },
+ "opsworks": {
+ "us-east-1": "opsworks.us-east-1.amazonaws.com"
+ },
+ "rds": {
+ "ap-northeast-1": "rds.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "rds.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "rds.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "rds.eu-west-1.amazonaws.com",
+ "sa-east-1": "rds.sa-east-1.amazonaws.com",
+ "us-east-1": "rds.us-east-1.amazonaws.com",
+ "us-gov-west-1": "rds.us-gov-west-1.amazonaws.com",
+ "us-west-1": "rds.us-west-1.amazonaws.com",
+ "us-west-2": "rds.us-west-2.amazonaws.com"
+ },
+ "redshift": {
+ "ap-northeast-1": "redshift.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "redshift.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "redshift.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "redshift.eu-west-1.amazonaws.com",
+ "us-east-1": "redshift.us-east-1.amazonaws.com",
+ "us-west-2": "redshift.us-west-2.amazonaws.com"
+ },
+ "route53": {
+ "ap-northeast-1": "route53.amazonaws.com",
+ "ap-southeast-1": "route53.amazonaws.com",
+ "ap-southeast-2": "route53.amazonaws.com",
+ "eu-west-1": "route53.amazonaws.com",
+ "sa-east-1": "route53.amazonaws.com",
+ "us-east-1": "route53.amazonaws.com",
+ "us-west-1": "route53.amazonaws.com",
+ "us-west-2": "route53.amazonaws.com"
+ },
+ "s3": {
+ "ap-northeast-1": "s3-ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "s3-ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "s3-ap-southeast-2.amazonaws.com",
+ "eu-west-1": "s3-eu-west-1.amazonaws.com",
+ "sa-east-1": "s3-sa-east-1.amazonaws.com",
+ "us-east-1": "s3.amazonaws.com",
+ "us-gov-west-1": "s3-us-gov-west-1.amazonaws.com",
+ "us-west-1": "s3-us-west-1.amazonaws.com",
+ "us-west-2": "s3-us-west-2.amazonaws.com"
+ },
+ "sdb": {
+ "ap-northeast-1": "sdb.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "sdb.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "sdb.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "sdb.eu-west-1.amazonaws.com",
+ "sa-east-1": "sdb.sa-east-1.amazonaws.com",
+ "us-east-1": "sdb.amazonaws.com",
+ "us-west-1": "sdb.us-west-1.amazonaws.com",
+ "us-west-2": "sdb.us-west-2.amazonaws.com"
+ },
+ "ses": {
+ "us-east-1": "email.us-east-1.amazonaws.com"
+ },
+ "sns": {
+ "ap-northeast-1": "sns.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "sns.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "sns.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "sns.eu-west-1.amazonaws.com",
+ "sa-east-1": "sns.sa-east-1.amazonaws.com",
+ "us-east-1": "sns.us-east-1.amazonaws.com",
+ "us-gov-west-1": "sns.us-gov-west-1.amazonaws.com",
+ "us-west-1": "sns.us-west-1.amazonaws.com",
+ "us-west-2": "sns.us-west-2.amazonaws.com"
+ },
+ "sqs": {
+ "ap-northeast-1": "sqs.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "sqs.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "sqs.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "sqs.eu-west-1.amazonaws.com",
+ "sa-east-1": "sqs.sa-east-1.amazonaws.com",
+ "us-east-1": "sqs.us-east-1.amazonaws.com",
+ "us-gov-west-1": "sqs.us-gov-west-1.amazonaws.com",
+ "us-west-1": "sqs.us-west-1.amazonaws.com",
+ "us-west-2": "sqs.us-west-2.amazonaws.com"
+ },
+ "storagegateway": {
+ "ap-northeast-1": "storagegateway.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "storagegateway.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "storagegateway.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "storagegateway.eu-west-1.amazonaws.com",
+ "sa-east-1": "storagegateway.sa-east-1.amazonaws.com",
+ "us-east-1": "storagegateway.us-east-1.amazonaws.com",
+ "us-west-1": "storagegateway.us-west-1.amazonaws.com",
+ "us-west-2": "storagegateway.us-west-2.amazonaws.com"
+ },
+ "sts": {
+ "ap-northeast-1": "sts.amazonaws.com",
+ "ap-southeast-1": "sts.amazonaws.com",
+ "ap-southeast-2": "sts.amazonaws.com",
+ "eu-west-1": "sts.amazonaws.com",
+ "sa-east-1": "sts.amazonaws.com",
+ "us-east-1": "sts.amazonaws.com",
+ "us-gov-west-1": "sts.us-gov-west-1.amazonaws.com",
+ "us-west-1": "sts.amazonaws.com",
+ "us-west-2": "sts.amazonaws.com"
+ },
+ "support": {
+ "us-east-1": "support.us-east-1.amazonaws.com"
+ },
+ "swf": {
+ "ap-northeast-1": "swf.ap-northeast-1.amazonaws.com",
+ "ap-southeast-1": "swf.ap-southeast-1.amazonaws.com",
+ "ap-southeast-2": "swf.ap-southeast-2.amazonaws.com",
+ "eu-west-1": "swf.eu-west-1.amazonaws.com",
+ "sa-east-1": "swf.sa-east-1.amazonaws.com",
+ "us-east-1": "swf.us-east-1.amazonaws.com",
+ "us-gov-west-1": "swf.us-gov-west-1.amazonaws.com",
+ "us-west-1": "swf.us-west-1.amazonaws.com",
+ "us-west-2": "swf.us-west-2.amazonaws.com"
+ }
+}
diff --git a/boto/pyami/config.py b/boto/pyami/config.py
index 48314e26..6669cc05 100644
--- a/boto/pyami/config.py
+++ b/boto/pyami/config.py
@@ -42,7 +42,7 @@ BotoConfigLocations = [BotoConfigPath]
UserConfigPath = os.path.join(expanduser('~'), '.boto')
BotoConfigLocations.append(UserConfigPath)
-# If there's a BOTO_CONFIG variable set, we load ONLY
+# If there's a BOTO_CONFIG variable set, we load ONLY
# that variable
if 'BOTO_CONFIG' in os.environ:
BotoConfigLocations = [expanduser(os.environ['BOTO_CONFIG'])]
@@ -149,14 +149,14 @@ class Config(ConfigParser.SafeConfigParser):
except:
val = default
return val
-
+
def getint(self, section, name, default=0):
try:
val = ConfigParser.SafeConfigParser.getint(self, section, name)
except:
val = int(default)
return val
-
+
def getfloat(self, section, name, default=0.0):
try:
val = ConfigParser.SafeConfigParser.getfloat(self, section, name)
@@ -174,13 +174,13 @@ class Config(ConfigParser.SafeConfigParser):
else:
val = default
return val
-
+
def setbool(self, section, name, value):
if value:
self.set(section, name, 'true')
else:
self.set(section, name, 'false')
-
+
def dump(self):
s = StringIO.StringIO()
self.write(s)
@@ -196,7 +196,7 @@ class Config(ConfigParser.SafeConfigParser):
fp.write('%s = xxxxxxxxxxxxxxxxxx\n' % option)
else:
fp.write('%s = %s\n' % (option, self.get(section, option)))
-
+
def dump_to_sdb(self, domain_name, item_name):
from boto.compat import json
sdb = boto.connect_sdb()
diff --git a/boto/regioninfo.py b/boto/regioninfo.py
index 6e936b37..39853fa1 100644
--- a/boto/regioninfo.py
+++ b/boto/regioninfo.py
@@ -20,6 +20,131 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+from __future__ import with_statement
+import os
+
+import boto
+from boto.compat import json
+from boto.exception import BotoClientError
+
+
+def load_endpoint_json(path):
+ """
+ Loads a given JSON file & returns it.
+
+ :param path: The path to the JSON file
+ :type path: string
+
+ :returns: The loaded data
+ """
+ with open(path, 'r') as endpoints_file:
+ return json.load(endpoints_file)
+
+
+def merge_endpoints(defaults, additions):
+ """
+ Given an existing set of endpoint data, this will deep-update it with
+ any similarly structured data in the additions.
+
+ :param defaults: The existing endpoints data
+ :type defaults: dict
+
+ :param defaults: The additional endpoints data
+ :type defaults: dict
+
+ :returns: The modified endpoints data
+ :rtype: dict
+ """
+ # We can't just do an ``defaults.update(...)`` here, as that could
+ # *overwrite* regions if present in both.
+ # We'll iterate instead, essentially doing a deeper merge.
+ for service, region_info in additions.items():
+ # Set the default, if not present, to an empty dict.
+ defaults.setdefault(service, {})
+ defaults[service].update(region_info)
+
+ return defaults
+
+
+def load_regions():
+ """
+ Actually load the region/endpoint information from the JSON files.
+
+ By default, this loads from the default included ``boto/endpoints.json``
+ file.
+
+ Users can override/extend this by supplying either a ``BOTO_ENDPOINTS``
+ environment variable or a ``endpoints_path`` config variable, either of
+ which should be an absolute path to the user's JSON file.
+
+ :returns: The endpoints data
+ :rtype: dict
+ """
+ # Load the defaults first.
+ endpoints = load_endpoint_json(boto.ENDPOINTS_PATH)
+ additional_path = None
+
+ # Try the ENV var. If not, check the config file.
+ if os.environ.get('BOTO_ENDPOINTS'):
+ additional_path = os.environ['BOTO_ENDPOINTS']
+ elif boto.config.get('boto', 'endpoints_path'):
+ additional_path = boto.config.get('boto', 'endpoints_path')
+
+ # If there's a file provided, we'll load it & additively merge it into
+ # the endpoints.
+ if additional_path:
+ additional = load_endpoint_json(additional_path)
+ endpoints = merge_endpoints(endpoints, additional)
+
+ return endpoints
+
+
+def get_regions(service_name, region_class=None, connection_cls=None):
+ """
+ Given a service name (like ``ec2``), returns a list of ``RegionInfo``
+ objects for that service.
+
+ This leverages the ``endpoints.json`` file (+ optional user overrides) to
+ configure/construct all the objects.
+
+ :param service_name: The name of the service to construct the ``RegionInfo``
+ objects for. Ex: ``ec2``, ``s3``, ``sns``, etc.
+ :type service_name: string
+
+ :param region_class: (Optional) The class to use when constructing. By
+ default, this is ``RegionInfo``.
+ :type region_class: class
+
+ :param connection_cls: (Optional) The connection class for the
+ ``RegionInfo`` object. Providing this allows the ``connect`` method on
+ the ``RegionInfo`` to work. Default is ``None`` (no connection).
+ :type connection_cls: class
+
+ :returns: A list of configured ``RegionInfo`` objects
+ :rtype: list
+ """
+ endpoints = load_regions()
+
+ if not service_name in endpoints:
+ raise BotoClientError(
+ "Service '%s' not found in endpoints." % service_name
+ )
+
+ if region_class is None:
+ region_class = RegionInfo
+
+ region_objs = []
+
+ for region_name, endpoint in endpoints.get(service_name, {}).items():
+ region_objs.append(
+ region_class(
+ name=region_name,
+ endpoint=endpoint,
+ connection_cls=connection_cls
+ )
+ )
+
+ return region_objs
class RegionInfo(object):
diff --git a/boto/ses/__init__.py b/boto/ses/__init__.py
index 9131f119..81d4206d 100644
--- a/boto/ses/__init__.py
+++ b/boto/ses/__init__.py
@@ -21,7 +21,7 @@
# IN THE SOFTWARE.
from connection import SESConnection
-from boto.regioninfo import RegionInfo
+from boto.regioninfo import RegionInfo, get_regions
def regions():
@@ -31,12 +31,7 @@ def regions():
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo` instances
"""
- return [RegionInfo(name='us-east-1',
- endpoint='email.us-east-1.amazonaws.com',
- connection_cls=SESConnection),
- RegionInfo(name='eu-west-1',
- endpoint='email.eu-west-1.amazonaws.com',
- connection_cls=SESConnection)]
+ return get_regions('ses', connection_cls=SESConnection)
def connect_to_region(region_name, **kw_params):
diff --git a/boto/sns/__init__.py b/boto/sns/__init__.py
index 4764a94f..1517f5f1 100644
--- a/boto/sns/__init__.py
+++ b/boto/sns/__init__.py
@@ -23,7 +23,7 @@
# this is here for backward compatibility
# originally, the SNSConnection class was defined here
from connection import SNSConnection
-from boto.regioninfo import RegionInfo
+from boto.regioninfo import RegionInfo, get_regions
def regions():
@@ -33,37 +33,7 @@ def regions():
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo` instances
"""
- return [RegionInfo(name='us-east-1',
- endpoint='sns.us-east-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='eu-west-1',
- endpoint='sns.eu-west-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='us-gov-west-1',
- endpoint='sns.us-gov-west-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='us-west-1',
- endpoint='sns.us-west-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='sa-east-1',
- endpoint='sns.sa-east-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='us-west-2',
- endpoint='sns.us-west-2.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='ap-northeast-1',
- endpoint='sns.ap-northeast-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='ap-southeast-1',
- endpoint='sns.ap-southeast-1.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='ap-southeast-2',
- endpoint='sns.ap-southeast-2.amazonaws.com',
- connection_cls=SNSConnection),
- RegionInfo(name='cn-north-1',
- endpoint=' sns.cn-north-1.amazonaws.com.cn',
- connection_cls=SNSConnection),
- ]
+ return get_regions('sns', connection_cls=SNSConnection)
def connect_to_region(region_name, **kw_params):
diff --git a/scripts/rebuild_endpoints.py b/scripts/rebuild_endpoints.py
new file mode 100644
index 00000000..6ec71537
--- /dev/null
+++ b/scripts/rebuild_endpoints.py
@@ -0,0 +1,54 @@
+from __future__ import print_function
+
+import json
+from pyquery import PyQuery as pq
+import requests
+
+
+class FetchError(Exception):
+ pass
+
+
+def fetch_endpoints():
+ # We utilize what the Java SDK publishes as a baseline.
+ resp = requests.get('https://raw2.github.com/aws/aws-sdk-java/master/src/main/resources/etc/regions.xml')
+
+ if int(resp.status_code) != 200:
+ raise FetchError("Failed to fetch the endpoints. Got {0}: {1}".format(
+ resp.status,
+ resp.body
+ ))
+
+ return resp.text
+
+def parse_xml(raw_xml):
+ return pq(raw_xml, parser='xml')
+
+
+def build_data(doc):
+ data = {}
+
+ # Run through all the regions. These have all the data we need.
+ for region_elem in doc('Regions').find('Region'):
+ region = pq(region_elem, parser='xml')
+ region_name = region.find('Name').text()
+
+ for endp in region.find('Endpoint'):
+ service_name = endp.find('ServiceName').text
+ endpoint = endp.find('Hostname').text
+
+ data.setdefault(service_name, {})
+ data[service_name][region_name] = endpoint
+
+ return data
+
+
+def main():
+ raw_xml = fetch_endpoints()
+ doc = parse_xml(raw_xml)
+ data = build_data(doc)
+ print(json.dumps(data, indent=4, sort_keys=True))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/setup.py b/setup.py
index 840ddffd..4ae4f595 100644
--- a/setup.py
+++ b/setup.py
@@ -76,7 +76,10 @@ setup(name = "boto",
"boto.elastictranscoder", "boto.opsworks", "boto.redshift",
"boto.dynamodb2", "boto.support", "boto.cloudtrail",
"boto.directconnect", "boto.kinesis"],
- package_data = {"boto.cacerts": ["cacerts.txt"]},
+ package_data = {
+ "boto.cacerts": ["cacerts.txt"],
+ "boto": ["endpoints.json"],
+ },
license = "MIT",
platforms = "Posix; MacOS X; Windows",
classifiers = ["Development Status :: 5 - Production/Stable",