Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add health / loadbalancer endpoints for Mozilla Dockerflow #1443

Open
spwoodcock opened this issue Apr 11, 2024 · 1 comment
Open

Add health / loadbalancer endpoints for Mozilla Dockerflow #1443

spwoodcock opened this issue Apr 11, 2024 · 1 comment
Labels
backend devops enhancement New feature or request

Comments

@spwoodcock
Copy link
Member

spwoodcock commented Apr 11, 2024

Is your feature request related to a problem? Please describe.

  • There isn't much standardisation across containerisation of web APIs.
  • There is a good attempt taken by Mozilla: Dockerflow spec
  • We should add these endpoints to help DevOps.

Describe the solution you'd like

  • Always place app files under /app dir inside container.
  • A file /app/version.json should be generated within the CI workflow and baked into the built image:
    {
      "source" : "https://github.com/hotosm/fmtm", 
      "version": "release tag or string for humans. can be blank for non-release", 
      "commit" : "<git hash>",
      "build"  : "uri to CI build job"
    }
  • Respond to /__version__ with the contents of /app/version.json.
  • Respond to /__heartbeat__ with a HTTP 200 or 5xx on error. This should check backing services like a database for connectivity and may respond with the status of backing services and application components as a JSON payload.
  • Respond to /__lbheartbeat__ with an HTTP 200. This is for load balancer checks and should not check backing services.
  • Always log to terminal (STDOUT), never to a file.
  • Backend serves its own static, frontend serves it's own static. I.e. do not rely on public/static directory of frontend from backend.

Describe alternatives you've considered

I don't know any other good specs for this.

@spwoodcock
Copy link
Member Author

Example implementation:

from pathlib import Path
from fastapi import Depends
from fastapi.responses import JSONResponse, Response
from loguru import logger as log
from sqlalchemy import text
from sqlalchemy.orm import Session

from app.__version__ import __version__
from app.db.database import get_db
from app.models.enums import HTTPStatus


@api.get("/__version__")
async def deployment_details():
    """Mozilla Dockerflow Spec: source, version, commit, and link to CI build."""
    details = {}

    version_path = Path("/opt/version.json")
    if version_path.exists():
        with open(version_path, "r") as version_file:
            details = json.load(version_file)
    commit = details.get("commit", "commit key was not found in file!")
    build = details.get("build", "build key was not found in file!")

    return JSONResponse(status_code=HTTPStatus.OK, content={
        "source" : "https://github.com/hotosm/fmtm", 
        "version": __version__,
        "commit" : commit or "/app/version.json not found",
        "build"  : build or "/app/version.json not found"
    })


@api.get("/__heartbeat__")
async def heartbeat_plus_db(db: Session = Depends(get_db)):
    """Heartbeat that checks that API and DB are both up and running."""
    try:
        db.execute(text("SELECT 1"))
        return Response(status_code=HTTPStatus.OK)
    except Exception as e:
        log.warning(e)
        log.warning("Server failed __heartbeat__ database connection check")
        return JSONResponse(
            status_code=HTTPStatus.INTERNAL_SERVER_ERROR, content={"error": str(e)}
        )


@api.get("/__lbheartbeat__")
async def simple_heartbeat():
    """Simple ping/pong API response."""
    return Response(status_code=HTTPStatus.OK)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend devops enhancement New feature or request
Projects
Development

No branches or pull requests

1 participant