Fetch AWS region from EC2 instance metadata

sig4() and query() upon getting a location set to None will attempt
to fetch it from EC2 instance metadata before falling back to
us-east-1.

get_region_from_metadata() uses caching just like creds() does. If
it can't connect to the metadata service, it does not try again.
If a value is successfully retrieved, it is cached so that future
calls are faster.

get_location() will call get_region_from_metadata() if location is
not explicitly set in config.

If sig4() receives an empty location value, it tries to
fetch it using get_region_from_metadata()
This commit is contained in:
Stanislav Blokhin 2015-08-14 16:56:26 +02:00
parent 612e342c70
commit 57943ff4f7

View file

@ -51,6 +51,7 @@ __AccessKeyId__ = ''
__SecretAccessKey__ = ''
__Token__ = ''
__Expiration__ = ''
__Location__ = ''
def creds(provider):
@ -140,7 +141,7 @@ def sig2(method, endpoint, params, provider, aws_api_version):
def sig4(method, endpoint, params, prov_dict,
aws_api_version=DEFAULT_AWS_API_VERSION, location=DEFAULT_LOCATION,
aws_api_version=DEFAULT_AWS_API_VERSION, location=None,
product='ec2', uri='/', requesturl=None, data=''):
'''
Sign a query against AWS services using Signature Version 4 Signing
@ -155,6 +156,11 @@ def sig4(method, endpoint, params, prov_dict,
# Retrieve access credentials from meta-data, or use provided
access_key_id, secret_access_key, token = creds(prov_dict)
if location is None:
location = get_region_from_metadata()
if location is None:
location = DEFAULT_LOCATION
params_with_headers = params.copy()
if product != 's3':
params_with_headers['Version'] = aws_api_version
@ -442,15 +448,56 @@ def query(params=None, setname=None, requesturl=None, location=None,
return ret
def get_region_from_metadata():
'''
Try to get region from instance identity document and cache it
.. versionadded:: 2015.5.6
'''
global __Location__
if __Location__ == 'do-not-get-from-metadata':
LOG.debug('Previously failed to get AWS region from metadata. Not trying again.')
return None
# Cached region
if __Location__ != '':
return __Location__
try:
# Connections to instance meta-data must never be proxied
result = requests.get(
"http://169.254.169.254/latest/dynamic/instance-identity/document",
proxies={'http': ''},
)
except requests.exceptions.RequestException:
LOG.warning('Failed to get AWS region from instance metadata.', exc_info=True)
# Do not try again
__Location__ = 'do-not-get-from-metadata'
return None
try:
region = result.json()['region']
__Location__ = region
return __Location__
except (ValueError, KeyError):
LOG.warning('Failed to decode JSON from instance metadata.')
return None
return None
def get_location(opts, provider=None):
'''
Return the region to use, in this order:
opts['location']
provider['location']
get_region_from_metadata()
DEFAULT_LOCATION
'''
return opts.get(
'location', provider.get(
'location', DEFAULT_LOCATION
)
)
ret = opts.get('location', provider.get('location'))
if ret is None:
ret = get_region_from_metadata()
if ret is None:
ret = DEFAULT_LOCATION
return ret