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

Call enhancer, carpool events #35

Open
wants to merge 35 commits into
base: plugins
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
295068e
moved static files to amarillo/static
Feb 6, 2024
ce90a35
copy static files to working directory
Feb 6, 2024
d998b13
moved gtfs generation and enhancer.py to enhancer plugin
Jan 30, 2024
a174c40
moved 'configure_enhancer_services' to enhancer plugin
Jan 30, 2024
98d84f9
moved routing.py, trips.py and stops.py to enhancer plugin
Feb 6, 2024
f4e8e6f
removed enhancer from Dockerfile
Feb 6, 2024
d2c9eec
moved tests to enhancer
Feb 6, 2024
3b440c3
moved gtfs services to enhancer
Feb 6, 2024
7a5281d
moved gtfs model to enhancer
Feb 8, 2024
668e619
moved region/gtfs endpoint to export plugin
Feb 9, 2024
73e7c51
carpool GRFS attributes
Feb 9, 2024
c85b7c0
__init__.py EOL, extra secrets clarification
May 13, 2024
f009182
CRUD metrics
Feb 19, 2024
267ece6
Added route_color and route_text_color to Carpool model
Feb 21, 2024
faaa82a
Basic-OAuth2-authentication
May 13, 2024
6815d03
Passwords-in-AgencyConf-model
May 13, 2024
a4dbd94
Verify password from agencyconf
Feb 28, 2024
4d9dfcd
Fix KeyError in authenticate_agency
Feb 28, 2024
73fa372
Make password and api_key optional in AgencyConf
May 13, 2024
27016f5
Use OAuth2 to authorize endpoints
Mar 1, 2024
ccdab80
Load secret key from env variable
May 17, 2024
f0d1dd4
Added secret_key variable to Dockerfile
May 17, 2024
3bdd8ce
Added error logging to file in logging.conf
Mar 6, 2024
ddce19e
Renamed AgencyConf to User
May 17, 2024
71661b7
Use /data for region and agency configuration
May 17, 2024
118e159
Added cryptography dependencies to requirements.txt
May 17, 2024
4fc6221
Get current user function
Apr 18, 2024
65bb57c
verify_permission function
Apr 22, 2024
08889dc
Use verify_permission in routes
Apr 22, 2024
4b415c0
Call enhancer in background task
May 17, 2024
0da7b7c
Build amarillo-base and derived image
Jun 24, 2024
357c8e7
Carpool event hooks
Jul 18, 2024
2bd8c51
Fixed error.log permissions
Jul 29, 2024
97140c2
Fix duplicate None api key
Jul 29, 2024
27fcf74
Add activating of virtual environment to README (#2)
frsaba Aug 1, 2024
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
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ setup.ipynb

*~
/.idea/
/.vscode/
secrets
gtfs/*.zip
gtfs/*.pbf
Expand All @@ -142,4 +143,12 @@ data/failed/
data/trash/
data/gtfs/
data/tmp

data/users/**
data/**

#these files are under app/static but they get copied to the outside directory on startup
logging.conf
config
static/**
templates/**
conf/**
26 changes: 14 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,29 @@ RUN \
# Remove package index obtained by `apt update`.
&& rm -rf /var/lib/apt/lists/*

ENV ADMIN_TOKEN=''
ENV RIDE2GO_TOKEN=''
ENV ADMIN_TOKEN=''
ENV RIDE2GO_TOKEN=''
ENV SECRET_KEY=''

EXPOSE 80

ARG PLUGINS

COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt ${PLUGINS}
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt

# set MODULE_NAME explicitly
ENV MODULE_NAME=amarillo.main

COPY ./amarillo /app/amarillo
COPY enhancer.py /app
COPY prestart.sh /app
COPY ./static /app/static
COPY ./templates /app/templates
COPY config /app
COPY logging.conf /app
COPY ./conf /app/conf
COPY ./amarillo/plugins /app/amarillo/plugins
COPY ./amarillo/static/static /app/static
COPY ./amarillo/static/templates /app/templates
COPY ./amarillo/static/config /app
COPY ./amarillo/static/logging.conf /app
COPY ./amarillo/static/data /app/data

# Create the error.log, otherwise we get a permission error when we try to write to it
RUN touch /app/error.log
RUN chmod 777 /app/error.log

# This image inherits uvicorn-gunicorn's CMD. If you'd like to start uvicorn, use this instead
# CMD ["uvicorn", "amarillo.main:app", "--host", "0.0.0.0", "--port", "8000"]
63 changes: 44 additions & 19 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
pipeline {
agent any
agent { label 'builtin' }
environment {
GITEA_CREDS = credentials('AMARILLO-JENKINS-GITEA-USER')
PYPI_CREDS = credentials('AMARILLO-JENKINS-PYPI-USER')
TWINE_REPO_URL = "https://git.gerhardt.io/api/packages/amarillo/pypi"
PLUGINS_REPO_URL = "git.gerhardt.io/api/packages/amarillo/pypi/simple"
DOCKER_REGISTRY_URL = 'https://git.gerhardt.io'
DOCKER_REGISTRY = 'git.gerhardt.io'
DERIVED_DOCKERFILE = 'standard.Dockerfile'
OWNER = 'amarillo'
BASE_IMAGE_NAME = 'amarillo-base'
IMAGE_NAME = 'amarillo'
AMARILLO_DISTRIBUTION = '0.2'
TAG = "${AMARILLO_DISTRIBUTION}.${BUILD_NUMBER}"
PLUGINS = 'amarillo-metrics amarillo-enhancer amarillo-grfs-export'
DEPLOY_WEBHOOK_URL = 'http://amarillo.mfdz.de:8888/mitanand'
AMARILLO_DISTRIBUTION = '0.3'
TAG = "${AMARILLO_DISTRIBUTION}.${BUILD_NUMBER}${env.BRANCH_NAME == 'main' ? '' : '-' + env.BRANCH_NAME}"
DEPLOY_WEBHOOK_URL = "http://amarillo.mfdz.de:8888/${env.BRANCH_NAME}"
DEPLOY_SECRET = credentials('AMARILLO-JENKINS-DEPLOY-SECRET')
}
stages {
Expand Down Expand Up @@ -42,35 +42,56 @@ pipeline {
sh 'python3 -m twine upload --skip-existing --verbose --repository-url $TWINE_REPO_URL --username $GITEA_CREDS_USR --password $GITEA_CREDS_PSW ./dist/*'
}
}
stage('Publish package to PyPI') {
stage('Build base docker image') {
when {
branch 'release'
isDeployBranch()
}
steps {
sh 'python3 -m twine upload --verbose --username $PYPI_CREDS_USR --password $PYPI_CREDS_PSW ./dist/*'
echo 'Building image'
script {
docker.build("${OWNER}/${BASE_IMAGE_NAME}:${TAG}")
}
}
}
stage('Push base image to container registry') {
when {
isDeployBranch()
}
steps {
echo 'Pushing image to registry'
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'AMARILLO-JENKINS-GITEA-USER'){
def image = docker.image("${OWNER}/${BASE_IMAGE_NAME}:${TAG}")
image.push()
image.push('latest')
}
}
}
}
stage('Build Mitanand docker image') {
stage('Build derived docker image') {
when {
branch 'mitanand'
isDeployBranch()
}
steps {
echo 'Building image'
script {
docker.build("${OWNER}/${IMAGE_NAME}:${TAG}",
//--no-cache to make sure plugins are updated
"--no-cache --build-arg='PACKAGE_REGISTRY_URL=${PLUGINS_REPO_URL}' --build-arg='PLUGINS=${PLUGINS}' --secret id=AMARILLO_REGISTRY_CREDENTIALS,env=GITEA_CREDS .")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'AMARILLO-JENKINS-GITEA-USER'){
docker.build("${OWNER}/${IMAGE_NAME}:${TAG}",
//--no-cache to make sure plugins are updated
"-f ${DERIVED_DOCKERFILE} --no-cache --build-arg='PACKAGE_REGISTRY_URL=${PLUGINS_REPO_URL}' --build-arg='DOCKER_REGISTRY=${DOCKER_REGISTRY}' --secret id=AMARILLO_REGISTRY_CREDENTIALS,env=GITEA_CREDS .")
}

}
}
}
stage('Push image to container registry') {
stage('Push derived image to container registry') {
when {
branch 'mitanand'
isDeployBranch()
}
steps {
echo 'Pushing image to registry'
script {
docker.withRegistry(DOCKER_REGISTRY_URL, 'AMARILLO-JENKINS-GITEA-USER'){
docker.withRegistry("https://${DOCKER_REGISTRY}", 'AMARILLO-JENKINS-GITEA-USER'){
def image = docker.image("${OWNER}/${IMAGE_NAME}:${TAG}")
image.push()
image.push('latest')
Expand All @@ -80,7 +101,7 @@ pipeline {
}
stage('Notify CD script') {
when {
branch 'mitanand'
isDeployBranch()
}
steps {
echo 'Triggering deploy webhook'
Expand All @@ -93,3 +114,7 @@ pipeline {
}
}
}

def isDeployBranch() {
return anyOf { branch 'main'; branch 'dev'; branch 'mitanand' }
}
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include amarillo/static/ *
recursive-include amarillo/tests/ *
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ An Amarillo is a [yellow-dressed person](https://www.dreamstime.com/sancti-spiri
- Python 3.9.2 with pip
- python3-venv

Create a virtual environment `python3 -m venv venv`.

Activate the environment and install the dependencies `pip install -r requirements.txt`.
Create a virtual environment `python3 -m venv venv`. Activate the environment with `source venv/bin/activate` and install the dependencies `pip install -r requirements.txt`.

Run `uvicorn amarillo.main:app`.

Expand All @@ -22,23 +20,25 @@ In development, you can use `--reload`.
- `env`
- `ADMIN_TOKEN`

E.g. set the environment variable like this: `export ADMIN_TOKEN=YOUR_SECRET_TOKEN_HERE`.

## Security

All endpoints are protected by an API-Key in the HTTP header.
There is a special *admin* user.
For this user, the API-Key must be passed in as an environment variable when
Amarillo is started.

The admin can create additional API-Keys in the `/agencyconf` endpoint. This
The admin can create additional API-Keys in the `/users` endpoint. This
endpoint is always available but not always shown in `/docs`, especially not
when running in production.
The Swagger docs for `/agencyconf` can be seen on the MFDZ demo server.
The Swagger docs for `/users` can be seen on the MFDZ demo server.

Permissions work this way
- the admin is allowed to call all operations on all resources. Only the admin
can create new API-Keys by POSTing an `AgencyConf` JSON object to `/agencyconf`.
can create new API-Keys by POSTing an `users` JSON object to `/users`.
- API-Keys for agencies are allowed to POST/PUT/GET/DELETE their own
resources and GET some public resources.
resources and GET some public resources.

## Development

Expand Down
51 changes: 4 additions & 47 deletions amarillo/configuration.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
# separate file so that it can be imported without initializing FastAPI
from amarillo.utils.container import container
import json
import logging
from glob import glob

from amarillo.models.Carpool import Agency, Carpool, Region
from amarillo.services import stops
from amarillo.services import trips
from amarillo.services.agencyconf import AgencyConfService, agency_conf_directory
from amarillo.services.carpools import CarpoolService
from amarillo.services.users import UserService, user_conf_directory
from amarillo.services.agencies import AgencyService
from amarillo.services.regions import RegionService

from amarillo.services.config import config

from amarillo.utils.utils import assert_folder_exists
import amarillo.services.gtfs_generator as gtfs_generator

logger = logging.getLogger(__name__)

Expand All @@ -34,12 +27,12 @@ def create_required_directories():
assert_folder_exists(f'data/{subdir}/{agency_id}')

# Agency configurations
assert_folder_exists(agency_conf_directory)
assert_folder_exists(user_conf_directory)


def configure_services():
container['agencyconf'] = AgencyConfService()
logger.info("Loaded %d agency configuration(s)", len(container['agencyconf'].agency_id_to_agency_conf))
container['users'] = UserService()
logger.info("Loaded %d user configuration(s)", len(container['users'].user_id_to_user_conf))

container['agencies'] = AgencyService()
logger.info("Loaded %d agencies", len(container['agencies'].agencies))
Expand All @@ -49,42 +42,6 @@ def configure_services():

create_required_directories()


def configure_enhancer_services():
configure_services()

logger.info("Load stops...")
with open(config.stop_sources_file) as stop_sources_file:
stop_sources = json.load(stop_sources_file)
stop_store = stops.StopsStore(stop_sources)

stop_store.load_stop_sources()
container['stops_store'] = stop_store
container['trips_store'] = trips.TripStore(stop_store)
container['carpools'] = CarpoolService(container['trips_store'])

logger.info("Restore carpools...")

for agency_id in container['agencies'].agencies:
for carpool_file_name in glob(f'data/carpool/{agency_id}/*.json'):
try:
with open(carpool_file_name) as carpool_file:
carpool = Carpool(**(json.load(carpool_file)))
container['carpools'].put(carpool.agency, carpool.id, carpool)
except Exception as e:
logger.warning("Issue during restore of carpool %s: %s", carpool_file_name, repr(e))

# notify carpool about carpools in trash, as delete notifications must be sent
for carpool_file_name in glob(f'data/trash/{agency_id}/*.json'):
with open(carpool_file_name) as carpool_file:
carpool = Carpool(**(json.load(carpool_file)))
container['carpools'].delete(carpool.agency, carpool.id)

logger.info("Restored carpools: %s", container['carpools'].get_all_ids())
logger.info("Starting scheduler")
gtfs_generator.start_schedule()


def configure_admin_token():
if config.admin_token is None:
message = "ADMIN_TOKEN environment variable not set"
Expand Down
10 changes: 8 additions & 2 deletions amarillo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
import mimetypes
from starlette.staticfiles import StaticFiles

from amarillo.utils.utils import copy_static_files
#this has to run before app.configuration is imported, otherwise we get validation error for config because the config file is not copied yet
copy_static_files(["data", "static", "templates", "logging.conf", "config"])

import amarillo.plugins
from amarillo.services.config import config
from amarillo.configuration import configure_services, configure_admin_token
from amarillo.routers import carpool, agency, agencyconf, region
from amarillo.routers import carpool, agency, users, region
import amarillo.services.oauth2 as oauth2
from fastapi import FastAPI

# https://pydantic-docs.helpmanual.io/usage/settings/
Expand Down Expand Up @@ -76,8 +81,9 @@

app.include_router(carpool.router)
app.include_router(agency.router)
app.include_router(agencyconf.router)
app.include_router(users.router)
app.include_router(region.router)
app.include_router(oauth2.router)


def iter_namespace(ns_pkg):
Expand Down
26 changes: 0 additions & 26 deletions amarillo/models/AgencyConf.py

This file was deleted.

Loading