Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
tcjennings committed Dec 17, 2024
1 parent 2a59dff commit 2dfdad2
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 12 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,22 @@ jobs:
with:
fetch-depth: 0

- uses: lsst-sqre/build-and-push-to-ghcr@v1
- uses: lsst-sqre/build-and-push-to-ghcr@tickets/DM-48192
id: build-service
with:
dockerfile: docker/Dockerfile
target: cmservice
image: ${{ github.repository }}
github_token: ${{ secrets.GITHUB_TOKEN }}

- uses: lsst-sqre/build-and-push-to-ghcr@tickets/DM-48192
id: build-worker
with:
dockerfile: docker/Dockerfile
target: cmworker
build-args: |
PYTHON_VERSION=3.11
UV_VERSION=0.5
image: ${{ github.repository }}
github_token: ${{ secrets.GITHUB_TOKEN }}

Expand Down
14 changes: 14 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ services:
CM_DATABASE_ECHO: true
CM_DATABASE_URL: postgresql://cm-service@postgresql:5432/cm-service
CM_DATABASE_PASSWORD: INSECURE-PASSWORD
networks:
- cmservice
depends_on:
postgresql:
condition: service_healthy
Expand All @@ -33,6 +35,7 @@ services:
build:
context: .
dockerfile: docker/Dockerfile
target: cmservice
entrypoint:
- uvicorn
command:
Expand All @@ -44,6 +47,8 @@ services:
environment: *cmenv
ports:
- "8080:8080"
networks:
- cmservice
depends_on:
init-db:
condition: service_completed_successfully
Expand All @@ -55,12 +60,15 @@ services:
build:
context: .
dockerfile: docker/Dockerfile
target: cmworker
entrypoint:
- /opt/venv/bin/python3
- -m
command:
- lsst.cmservice.daemon
environment: *cmenv
networks:
- cmservice
depends_on:
init-db:
condition: service_completed_successfully
Expand All @@ -74,6 +82,8 @@ services:
POSTGRES_DB: "cm-service"
ports:
- "5432"
networks:
- cmservice
volumes:
- "pgsql:/var/lib/postgresql/data"
healthcheck:
Expand All @@ -87,3 +97,7 @@ services:

volumes:
pgsql:

networks:
cmservice:
driver: bridge
24 changes: 18 additions & 6 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# syntax=docker/dockerfile:1

ARG PYTHON_VERSION=3.11
ARG UV_VERSION=0.5
ARG PYTHON_VERSION
ARG UV_VERSION

#==============================================================================
# UV SOURCE IMAGE
FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv


# HTCONDOR SOURCE IMAGE
FROM continuumio/miniconda3:latest AS htcondor
RUN conda create -p /opt/htcondor -c conda-forge htcondor-utils --no-default-packages


#==============================================================================
# BASE IMAGE
FROM python:${PYTHON_VERSION}-slim-bookworm AS base-image
Expand Down Expand Up @@ -69,8 +74,8 @@ ENDRUN


#==============================================================================
# RUNTIME IMAGE
FROM base-image AS runtime-image
# RUNTIME IMAGE - CM-Service
FROM base-image AS cmservice

# Expose the API port
EXPOSE 8080
Expand Down Expand Up @@ -105,5 +110,12 @@ EOF
ENTRYPOINT ["./docker-entrypoint.sh"]
CMD ["lsst.cmservice.main:app", "--host", "0.0.0.0", "--port", "8080"]

# ENTRYPOINT ["/opt/venv/bin/python3", "-m"]
# CMD ["lsst.cmservice.daemon"]

#==============================================================================
# RUNTIME IMAGE - Daemon
FROM cmservice AS cmworker

COPY --from=htcondor /opt/htcondor /opt/htcondor

ENTRYPOINT ["/opt/venv/bin/python3", "-m"]
CMD ["lsst.cmservice.daemon"]
3 changes: 3 additions & 0 deletions src/lsst/cmservice/config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from dotenv import load_dotenv
from pydantic import Field
from pydantic_settings import BaseSettings
from safir.logging import LogLevel, Profile

__all__ = ["Configuration", "config"]

load_dotenv()


class Configuration(BaseSettings):
"""Configuration for cm-service."""
Expand Down
70 changes: 65 additions & 5 deletions src/lsst/cmservice/daemon.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
import asyncio
from asyncio import create_task, sleep
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from datetime import datetime, timedelta
from time import sleep

import structlog
import uvicorn
from fastapi import APIRouter, FastAPI, HTTPException
from safir.database import create_async_session, create_database_engine

from . import __version__
from .common.daemon import daemon_iteration
from .config import config


class State:
"""A class defining an application State object.
Attributes
----------
tasks : set
Set of coroutine tasks that have been added to the event loop.
"""

tasks: set

def __init__(self) -> None:
self.tasks = set()


health_router = APIRouter()
"""An API Router for a health endpoint"""

state = State()
"""A global State object"""


@health_router.get("/healthz", tags=["internal", "health"])
async def get_healthz() -> dict:
server_ok = True
health_response = {}

for task in state.tasks:
task_response: dict[str, bool | str | None] = {"task_running": True, "task_exception": None}
if task.done():
server_ok = False
task_response["task_running"] = False
task_response["task_exception"] = str(task.exception())
health_response[task.get_name()] = task_response

if not server_ok:
raise HTTPException(status_code=500, detail=health_response)
else:
return health_response


@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncGenerator:
# start
daemon = create_task(main_loop(), name="daemon")
state.tasks.add(daemon)
yield
# stop


async def main_loop() -> None:
logger = structlog.get_logger(config.logger_name)
engine = create_database_engine(config.database_url, config.database_password)
Expand All @@ -26,12 +80,18 @@ async def main_loop() -> None:
logger.info(f"Daemon completed {iteration_count} iterations in {delta_seconds} seconds.")
last_log_time = datetime.now()
await daemon_iteration(session)
sleep(15)
await sleep(15)


def main() -> None:
loop = asyncio.get_event_loop()
loop.run_until_complete(main_loop())
app = FastAPI(
lifespan=lifespan,
version=__version__,
)

app.include_router(health_router)

uvicorn.run(app, host="0.0.0.0", port=8081)


if __name__ == "__main__":
Expand Down

0 comments on commit 2dfdad2

Please sign in to comment.