summaryrefslogtreecommitdiff
path: root/boto/regioninfo.py
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 /boto/regioninfo.py
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.
Diffstat (limited to 'boto/regioninfo.py')
-rw-r--r--boto/regioninfo.py125
1 files changed, 125 insertions, 0 deletions
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):