diff options
author | Daniel Lindsley <daniel@toastdriven.com> | 2014-01-28 12:29:58 -0800 |
---|---|---|
committer | Daniel Lindsley <daniel@toastdriven.com> | 2014-01-28 14:18:38 -0800 |
commit | 8fa3b6cbf435193dbf06bf4095af903e0fca5133 (patch) | |
tree | e8d842a7e6dd7841663f355102c5e8911374f6bb /boto/regioninfo.py | |
parent | fd6b6326d68eb232e6901e3f1fb4c6b1e0790971 (diff) | |
download | boto-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.py | 125 |
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): |