diff --git a/python/lsst/daf/butler/remote_butler/server/_server.py b/python/lsst/daf/butler/remote_butler/server/_server.py index b9404d47a5..4552c541b4 100644 --- a/python/lsst/daf/butler/remote_butler/server/_server.py +++ b/python/lsst/daf/butler/remote_butler/server/_server.py @@ -36,6 +36,7 @@ from fastapi import Depends, FastAPI from fastapi.middleware.gzip import GZipMiddleware from lsst.daf.butler import Butler +from safir.metadata import Metadata, get_metadata from ._config import get_config_from_env from ._factory import Factory @@ -56,6 +57,26 @@ def factory_dependency() -> Factory: return Factory(butler=_make_global_butler()) +@app.get( + "/", + description=( + "Return metadata about the running application. Can also be used as" + " a health check. This route is not exposed outside the cluster and" + " therefore cannot be used by external clients." + ), + include_in_schema=False, + response_model=Metadata, + response_model_exclude_none=True, + summary="Application metadata", +) +async def get_index() -> Metadata: + """GET ``/`` (the app's internal root). + + By convention, this endpoint returns only the application's metadata. + """ + return get_metadata(package_name="lsst.daf.butler", application_name="butler") + + @app.get("/butler/v1/universe", response_model=dict[str, Any]) def get_dimension_universe(factory: Factory = Depends(factory_dependency)) -> dict[str, Any]: """Allow remote client to get dimensions definition.""" diff --git a/requirements.txt b/requirements.txt index 011a295ad6..acc5f03b28 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,3 +21,4 @@ pyarrow >= 0.16 responses >= 0.12.0 urllib3 >= 1.25.10 fastapi +safir >= 3.4.0 diff --git a/tests/test_server.py b/tests/test_server.py index 401e0126dd..32c84a9c3a 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -82,6 +82,11 @@ def tearDownClass(cls): del app.dependency_overrides[factory_dependency] removeTestTempDir(cls.root) + def test_health_check(self): + response = self.client.get("/") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["name"], "butler") + def test_simple(self): response = self.client.get("/butler/v1/universe") self.assertEqual(response.status_code, 200)