#!/usr/bin/env python
"""Utility to do a blocking sleep until a Galaxy instance is responsive.
This is useful in docker images, in RUN steps, where one needs to wait
for a currently starting Galaxy to be alive, before API requests can be
made successfully.
The script functions by making repeated requests to
``http(s)://fqdn/api/version``, an API which requires no authentication
to access."""
import sys
import time
from argparse import ArgumentParser
import requests
from galaxy.util import unicodify
from .common_parser import (
get_common_args,
HideUnderscoresHelpFormatter,
)
DEFAULT_SLEEP_WAIT = 1
MESSAGE_KEY_NOT_YET_VALID = "[%02d] Provided key not (yet) valid... %s\n"
MESSAGE_INVALID_JSON = "[%02d] No valid json returned... %s\n"
MESSAGE_FETCHING_USER = "[%02d] Connection error fetching user details, exiting with error code. %s\n"
MESSAGE_KEY_NOT_YET_ADMIN = "[%02d] Provided key not (yet) admin... %s\n"
MESSAGE_GALAXY_NOT_YET_UP = "[%02d] Galaxy not up yet... %s\n"
MESSAGE_TIMEOUT = "Failed to contact Galaxy within timeout (%s), exiting with error code.\n"
def _parser():
"""Constructs the parser object"""
parent = get_common_args(login_required=False)
parser = ArgumentParser(
parents=[parent],
usage="usage: %(prog)s <options>",
formatter_class=HideUnderscoresHelpFormatter,
description="Script to sleep and wait for Galaxy to be alive.",
)
parser.add_argument(
"--timeout",
default=0,
type=int,
help="Galaxy startup timeout in seconds. The default value of 0 waits forever",
)
parser.add_argument(
"-a",
"--api-key",
"--api_key",
dest="api_key",
help="Sleep until key becomes available.",
)
parser.add_argument("--ensure-admin", "--ensure_admin", default=False, action="store_true")
return parser
[docs]
class SleepCondition:
def __init__(self):
self.sleep = True
[docs]
def cancel(self):
self.sleep = False
[docs]
def galaxy_wait(
galaxy_url,
verbose=False,
timeout=0,
sleep_condition=None,
api_key=None,
ensure_admin=False,
):
"""Pass user_key to ensure it works before returning."""
if verbose:
sys.stdout.write(f"calling galaxy_wait with timeout={timeout} ensure_admin={ensure_admin}\n\n\n")
sys.stdout.flush()
version_url = galaxy_url + "/api/version"
if api_key:
# adding the key to the URL will ensure Galaxy returns invalid responses until
# the key is available.
version_url = f"{version_url}?key={api_key}"
current_user_url = f"{galaxy_url}/api/users/current?key={api_key}"
else:
assert not ensure_admin
if sleep_condition is None:
sleep_condition = SleepCondition()
count = 0
version_obtained = False
while sleep_condition.sleep:
try:
if not version_obtained:
result = requests.get(version_url)
if result.status_code == 403:
if verbose:
sys.stdout.write(MESSAGE_KEY_NOT_YET_VALID % (count, result.__str__()))
sys.stdout.flush()
else:
try:
result = result.json()
if verbose:
sys.stdout.write("Galaxy Version: %s\n" % result["version_major"])
sys.stdout.flush()
version_obtained = True
except ValueError:
if verbose:
sys.stdout.write(MESSAGE_INVALID_JSON % (count, result.__str__()))
sys.stdout.flush()
if version_obtained:
if ensure_admin:
result = requests.get(current_user_url)
if result.status_code != 200:
if verbose:
sys.stdout.write(MESSAGE_FETCHING_USER % (count, result.__str__()))
sys.stdout.flush()
return False
result = result.json()
is_admin = result["is_admin"]
if is_admin:
if verbose:
sys.stdout.write("Verified supplied key an admin key.\n")
sys.stdout.flush()
break
else:
if verbose:
sys.stdout.write(MESSAGE_KEY_NOT_YET_ADMIN % (count, result.__str__()))
sys.stdout.flush()
else:
break
except requests.exceptions.ConnectionError as e:
if verbose:
sys.stdout.write(MESSAGE_GALAXY_NOT_YET_UP % (count, unicodify(e)[:100]))
sys.stdout.flush()
count += 1
# If we cannot talk to galaxy and are over the timeout
if timeout != 0 and count > timeout:
sys.stderr.write(MESSAGE_TIMEOUT % timeout)
return False
time.sleep(DEFAULT_SLEEP_WAIT)
return True
[docs]
def main(argv=None):
"""
Main function
"""
options = _parser().parse_args(argv)
galaxy_alive = galaxy_wait(
galaxy_url=options.galaxy,
verbose=options.verbose,
timeout=options.timeout,
api_key=options.api_key,
ensure_admin=options.ensure_admin,
)
exit_code = 0 if galaxy_alive else 1
sys.exit(exit_code)
if __name__ == "__main__":
main()