Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions .github/workflows/docker-configs/ldap-docker-compose.yml

This file was deleted.

23 changes: 15 additions & 8 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Fetch tags
run: git fetch --tags --prune --unshallow
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: shogo82148/actions-setup-redis@v1
Expand All @@ -36,14 +36,8 @@ jobs:
run: |
# sudo apt install redis

pushd ..
git clone https://github.com/bitnami/containers.git
cd containers/bitnami/openldap/2.6/debian-12
docker build -t bitnami/openldap:latest .
popd

# Start LDAP
source start_LDAP.sh
bash continuous_integration/scripts/start_LDAP.sh

# These packages are installed in the base environment but may be older
# versions. Explicitly upgrade them because they often create
Expand All @@ -70,6 +64,19 @@ jobs:

pip list
- name: Test with pytest
env:
PYTEST_ADDOPTS: "--durations=20"
run: |
coverage run -m pytest -vv
coverage report -m
- name: Dump LDAP diagnostics on failure
if: failure()
run: |
docker ps
docker compose -f continuous_integration/docker-configs/ldap-docker-compose.yml ps
LDAP_CONTAINER_ID=$(docker compose -f continuous_integration/docker-configs/ldap-docker-compose.yml ps -q openldap | tr -d '[:space:]')
if [ -n "$LDAP_CONTAINER_ID" ]; then
docker logs --tail 200 "$LDAP_CONTAINER_ID"
else
docker compose -f continuous_integration/docker-configs/ldap-docker-compose.yml logs --tail 200 openldap
fi
67 changes: 46 additions & 21 deletions bluesky_httpserver/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.utils import get_openapi

from .authentication import Mode
from .console_output import CollectPublishedConsoleOutput, ConsoleOutputStream, SystemInfoStream
from .authenticators import ProxiedOIDCAuthenticator
from .console_output import (
CollectPublishedConsoleOutput,
ConsoleOutputStream,
SystemInfoStream,
)
from .core import PatchedStreamingResponse
from .database.core import purge_expired
from .protocols import ExternalAuthenticator, InternalAuthenticator
from .resources import SERVER_RESOURCES as SR
from .routers import core_api
from .settings import get_settings
Expand Down Expand Up @@ -158,9 +163,9 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
logger.info("All custom routers are included successfully.")

from .authentication import (
add_external_routes,
add_internal_routes,
base_authentication_router,
build_auth_code_route,
build_handle_credentials_route,
oauth2_scheme,
)

Expand All @@ -179,20 +184,14 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
for spec in authentication["providers"]:
provider = spec["provider"]
authenticator = spec["authenticator"]
mode = authenticator.mode
if mode == Mode.password:
authentication_router.post(f"/provider/{provider}/token")(
build_handle_credentials_route(authenticator, provider)
)
elif mode == Mode.external:
authentication_router.get(f"/provider/{provider}/code")(
build_auth_code_route(authenticator, provider)
)
authentication_router.post(f"/provider/{provider}/code")(
build_auth_code_route(authenticator, provider)
)
if isinstance(authenticator, InternalAuthenticator):
add_internal_routes(authentication_router, provider, authenticator)
elif isinstance(authenticator, ExternalAuthenticator):
add_external_routes(authentication_router, provider, authenticator)
if isinstance(authenticator, ProxiedOIDCAuthenticator):
app.state.provider = provider
else:
raise ValueError(f"unknown authentication mode {mode}")
raise ValueError(f"unknown authenticator type {type(authenticator)}")
for custom_router in getattr(authenticator, "include_routers", []):
authentication_router.include_router(custom_router, prefix=f"/provider/{provider}")

Expand Down Expand Up @@ -236,9 +235,11 @@ async def startup_event():
from .database import orm
from .database.core import ( # make_admin_by_identity,
REQUIRED_REVISION,
DatabaseUpgradeNeeded,
UninitializedDatabase,
check_database,
initialize_database,
upgrade,
)

connect_args = {}
Expand All @@ -256,6 +257,10 @@ async def startup_event():
)
initialize_database(engine)
logger.info("Database initialized.")
except DatabaseUpgradeNeeded:
logger.info(f"Database at {redacted_url} is out of date. Upgrading to {REQUIRED_REVISION}...")
upgrade(engine, REQUIRED_REVISION)
logger.info("Database upgraded.")
else:
logger.info(f"Connected to existing database at {redacted_url}.")
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Expand Down Expand Up @@ -390,10 +395,30 @@ async def purge_expired_sessions_and_api_keys():

@app.on_event("shutdown")
async def shutdown_event():
await SR.RM.close()
await SR.console_output_loader.stop()
await SR.console_output_stream.stop()
await SR.system_info_stream.stop()
"""Safely shutdown and perform the cleanup robustly

This change ensures that the application shuts down and cleans up resources even if there is
a problem, without silencing the errors.
"""
for task in getattr(app.state, "tasks", []):
task.cancel()
for closer_name in (
"console_output_loader",
"console_output_stream",
"system_info_stream",
):
closer = getattr(SR, closer_name, None)
if closer is not None:
try:
await closer.stop()
except Exception:
logger.exception("Error stopping %s", closer_name)
rm = getattr(SR, "RM", None)
if rm is not None:
try:
await rm.close()
except Exception:
logger.exception("Error closing REManagerAPI connection")

@lru_cache(1)
def override_get_authenticators():
Expand Down
Loading
Loading