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

Introduce FastAPI #2745

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ This repository contains [docker-compose.yml](./docker-compose.yml) for
(can be also [used with podman](https://fedoramagazine.org/use-docker-compose-with-podman-to-orchestrate-containers-on-fedora)).
Before you run it, we suggest that you open the file and read all the comments.
You can also run only certain pieces of packit-service for local development
(e.g. worker, database or service/httpd).
(e.g. worker, database or http service).
You also need to populate `secrets/packit/dev/` manually, for instructions
see [deployment repo](https://github.com/packit/deployment/tree/main/secrets).

When you are running service/httpd and making requests to it,
When you are running http service and making requests to it,
make sure that `server_name` configuration file in `packit-service.yaml` is set.

### tokman
Expand Down Expand Up @@ -84,7 +84,7 @@ For this reason `docker-compose` needs access to ports lower than 1024:
### binding on other hosts

If you are not binding the service on `localhost`
then you **need** to make requests to httpd using the hostname
then you **need** to make requests to http service using the hostname
(which can be done by creating a new entry in `/etc/hosts` on your laptop)
and you have to provide a route to that host.

Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ services:
args:
SOURCE_BRANCH: main
image: quay.io/packit/packit-service:dev
command: /usr/bin/run_httpd.sh
command: /usr/bin/run_server.sh
depends_on:
- redis
- postgres
Expand All @@ -157,7 +157,7 @@ services:
- ./secrets/packit/dev/fedora.keytab:/secrets/fedora.keytab:ro,z
- ./secrets/packit/dev/fullchain.pem:/secrets/fullchain.pem:ro,z
- ./secrets/packit/dev/privkey.pem:/secrets/privkey.pem:ro,z
- ./files/run_httpd.sh:/usr/bin/run_httpd.sh:ro,z
- ./files/run_server.sh:/usr/bin/run_server.sh:ro,z
user: "1024"

dashboard:
Expand All @@ -172,7 +172,7 @@ services:
# server_name: service.localhost
# dashboard_url: https://dashboard.localhost:8443
image: quay.io/packit/dashboard:stg
command: /usr/bin/run_httpd.sh
command: /usr/bin/run_server.sh
depends_on:
- service
ports:
Expand Down
2 changes: 1 addition & 1 deletion docs/database/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ the project which handles migrations and schema versioning for SQLAlchemy.
To generate a migration script for your recent change you can use docker or
more easily, with rootless podman, you can use our make target.

Both expect that the `alembic upgrade head` is run in [run_httpd.sh](../../files/run_httpd.sh)
Both expect that the `alembic upgrade head` is run in [run_server.sh](../../files/run_server.sh)
during (packit-)service pod/container start.

When modifying the migration manually, do not forget to update the downgrade
Expand Down
4 changes: 2 additions & 2 deletions files/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Image for the web service (httpd), for celery worker see files/docker/Dockerfile.worker
# Image for the web service, for celery worker see files/docker/Dockerfile.worker

FROM quay.io/packit/base:fedora

Expand Down Expand Up @@ -34,4 +34,4 @@ COPY alembic/ ./alembic/

EXPOSE 8443

CMD ["/usr/bin/run_httpd.sh"]
CMD ["/usr/bin/run_server.sh"]
1 change: 0 additions & 1 deletion files/packit.wsgi

This file was deleted.

2 changes: 1 addition & 1 deletion files/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
packit_service_path: /src
tasks:
- import_tasks: tasks/common.yaml
- import_tasks: tasks/httpd.yaml
- import_tasks: tasks/server.yaml
41 changes: 0 additions & 41 deletions files/run_httpd.sh

This file was deleted.

46 changes: 46 additions & 0 deletions files/run_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/bash

set -eux

# if all containers started at the same time, pg is definitely not ready to serve
# so let's try this for a few times
ATTEMPTS=7
n=0
while [[ $n -lt $ATTEMPTS ]]; do
alembic-3 upgrade head && break
n=$((n+1))
sleep 2
done

# If the number of attempts was exhausted: the migration failed.
# Exit with an error.
if [[ $n -eq $ATTEMPTS ]]; then
echo "Migration failed after $ATTEMPTS attempts. Exiting."
exit 1
fi

export PACKIT_SERVICE_CONFIG="${HOME}/.config/packit-service.yaml"
HTTPS_PORT=$(sed -nr 's/^server_name: ([^:]+)(:([0-9]+))?$/\3/p' "$PACKIT_SERVICE_CONFIG")

DEPLOYMENT=${DEPLOYMENT:-dev}

# Gunicorn is recommended for prod deployment, because it's more robust and scalable
if [[ "${DEPLOYMENT}" == "prod" || "${DEPLOYMENT}" == "stg" ]]; then
echo "Running Gunicorn with Uvicorn workers"
exec gunicorn -k uvicorn.workers.UvicornWorker \
-b 0.0.0.0:"${HTTPS_PORT:-8443}" \
--access-logfile - \
--log-level debug \
--certfile /secrets/fullchain.pem \
--keyfile /secrets/privkey.pem \
"packit_service.service.app:app"
else
echo "Running Uvicorn in development mode"
exec uvicorn packit_service.service.app:app \
--host 0.0.0.0 \
--port "${HTTPS_PORT:-8443}" \
--log-level debug \
--ssl-certfile /secrets/fullchain.pem \
--ssl-keyfile /secrets/privkey.pem \
--reload
fi
13 changes: 0 additions & 13 deletions files/tasks/httpd.yaml

This file was deleted.

8 changes: 8 additions & 0 deletions files/tasks/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

- name: Copy run_server.sh
ansible.builtin.copy:
src: run_server.sh
dest: /usr/bin/run_server.sh
mode: 0775
2 changes: 1 addition & 1 deletion packit_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def is_multi_threaded() -> bool:
# fails to rollback you have to restart the workers so that they pick another session.
singleton_session = Session()
logger.debug("Going to use a single SQLAlchemy session.")
else: # service/httpd
else: # http service
Session = scoped_session(Session)
singleton_session = None

Expand Down
10 changes: 9 additions & 1 deletion packit_service/sentry_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ def configure_sentry(
celery_integration: bool = False,
flask_integration: bool = False,
sqlalchemy_integration: bool = False,
fastapi_integration: bool = False,
) -> None:
logger.debug(
f"Setup sentry for {runner_type}: "
f"celery_integration={celery_integration}, "
f"flask_integration={flask_integration}, "
f"sqlalchemy_integration={sqlalchemy_integration}",
f"sqlalchemy_integration={sqlalchemy_integration}, "
f"fastapi_integration={fastapi_integration}",
)

secret_key = getenv("SENTRY_SECRET")
Expand Down Expand Up @@ -71,6 +73,12 @@ def configure_sentry(

integrations.append(SqlalchemyIntegration())

if fastapi_integration:
# https://docs.sentry.io/platforms/python/integrations/fastapi/
from sentry_sdk.integrations.fastapi import FastApiIntegration

integrations.append(FastApiIntegration())

# https://docs.sentry.io/platforms/python/guides/logging/
sentry_logging = LoggingIntegration(
level=logging.DEBUG, # Log everything, from DEBUG and above
Expand Down
2 changes: 1 addition & 1 deletion packit_service/service/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from packit_service.service.api.webhooks import ns as webhooks_ns

# https://flask-restplus.readthedocs.io/en/stable/scaling.html
blueprint = Blueprint("api", __name__, url_prefix="/api")
blueprint = Blueprint("api", __name__)
api = Api(
app=blueprint,
version="1.0",
Expand Down
7 changes: 7 additions & 0 deletions packit_service/service/api_v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from .healthz import router as healthz_router
from .system import router as system_router

routers = [healthz_router, system_router]
12 changes: 12 additions & 0 deletions packit_service/service/api_v1/healthz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from fastapi import APIRouter

router = APIRouter()


@router.get("/healthz")
async def health_check() -> dict:
"""Health check"""
return {"status": "ok"}
60 changes: 60 additions & 0 deletions packit_service/service/api_v1/system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

import re
from logging import getLogger
from typing import Optional

import ogr
import packit
import specfile
from fastapi import APIRouter
from pydantic import BaseModel
from setuptools_scm import get_version

import packit_service

logger = getLogger("packit_service")

router = APIRouter()


def get_commit_from_version(version) -> Optional[str]:
"""
Version can look like this:
0.76.0.post18+g116edc5
0.1.dev1+gc03b1bd.d20230615
0.18.0.post4+g28cb117
0.45.1.dev2+g3b0fc3b

The 7 characters after the "+g" is the short version of the git commit hash.
"""
if matches := re.search(r"\+g([A-Za-z0-9]{7})", version):
return matches.groups()[0]
return None


class PackageVersionResponse(BaseModel):
commit: Optional[str]
version: str


@router.get("/system")
async def get_system_information() -> dict[str, PackageVersionResponse]:
"""System information"""
packages_and_versions = {project: project.__version__ for project in [ogr, specfile, packit]}
# packit_service might not be installed (i.e. when running locally)
# so it's treated differently
packages_and_versions[packit_service] = packit_service.__version__ or get_version(
root="..",
relative_to=packit_service.__file__,
)

return {
project.__name__: PackageVersionResponse(
commit=get_commit_from_version(version),
version=version,
)
for project, version in packages_and_versions.items()
if version
}
Loading
Loading