|
|
|
import socket
|
|
|
|
import tenacity
|
|
|
|
from os import environ
|
|
|
|
import logging as log
|
|
|
|
|
|
|
|
@tenacity.retry(stop=tenacity.stop_after_attempt(100),
|
|
|
|
wait=tenacity.wait_random(min=2, max=5))
|
|
|
|
def resolve_hostname(hostname):
|
|
|
|
""" This function uses system DNS to resolve a hostname.
|
|
|
|
It is capable of retrying in case the host is not immediately available
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return sorted(socket.getaddrinfo(hostname, None, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE), key=lambda s:s[0])[0][4][0]
|
|
|
|
except Exception as e:
|
|
|
|
log.warn("Unable to lookup '%s': %s",hostname,e)
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
|
|
def resolve_address(address):
|
|
|
|
""" This function is identical to ``resolve_hostname`` but also supports
|
|
|
|
resolving an address, i.e. including a port.
|
|
|
|
"""
|
|
|
|
hostname, *rest = address.rsplit(":", 1)
|
|
|
|
ip_address = resolve_hostname(hostname)
|
|
|
|
if ":" in ip_address:
|
|
|
|
ip_address = "[{}]".format(ip_address)
|
|
|
|
return ip_address + "".join(":" + port for port in rest)
|
|
|
|
|
|
|
|
|
|
|
|
def get_host_address_from_environment(name, default):
|
|
|
|
""" This function looks up an envionment variable ``{{ name }}_ADDRESS``.
|
|
|
|
If it's defined, it is returned unmodified. If it's undefined, an environment
|
|
|
|
variable ``HOST_{{ name }}`` is looked up and resolved to an ip address.
|
|
|
|
If this is also not defined, the default is resolved to an ip address.
|
|
|
|
"""
|
|
|
|
if "{}_ADDRESS".format(name) in environ:
|
|
|
|
return environ.get("{}_ADDRESS".format(name))
|
|
|
|
return resolve_address(environ.get("HOST_{}".format(name), default))
|