diff --git a/server/app/app.py b/server/app/app.py index f3eb05ea..260bc9ae 100644 --- a/server/app/app.py +++ b/server/app/app.py @@ -14,6 +14,7 @@ import server.common.rest as common_rest from server.common.errors import DatasetAccessError from server.common.utils import path_join, Float32JSONEncoder from server.common.data_locator import DataLocator +from server.common.health import health_check from server.data_common.matrix_loader import MatrixDataLoader from functools import wraps @@ -48,6 +49,12 @@ def favicon(): return send_from_directory(os.path.join(webbp.root_path, "static/img/"), "favicon.png") +@webbp.route("/health") +def health(): + config = current_app.app_config + return health_check(config) + + def get_data_adaptor(dataset=None): config = current_app.app_config diff --git a/server/common/health.py b/server/common/health.py new file mode 100644 index 00000000..a12b4a35 --- /dev/null +++ b/server/common/health.py @@ -0,0 +1,29 @@ +from http import HTTPStatus +from flask import make_response, jsonify + +from server import __version__ as cellxgene_version +from server.common.data_locator import DataLocator + + +def _is_accessible(path): + if path is None: + return True + + try: + dl = DataLocator(path) + return dl.exists() + except RuntimeError: + return False + + +def health_check(config): + """ + simple health check - return HTTP response. + See https://tools.ietf.org/id/draft-inadarei-api-health-check-01.html + """ + health = {"status": None, "version": "1", "releaseID": cellxgene_version} + + checks = [_is_accessible(config.datapath), _is_accessible(config.dataroot)] + health["status"] = "pass" if all(checks) else "fail" + code = HTTPStatus.OK if health["status"] == "pass" else HTTPStatus.BAD_REQUEST + return make_response(jsonify(health), code, {"Content-Type": "application/health+json"},)