Skip to content
This repository has been archived by the owner on Nov 14, 2023. It is now read-only.

API health check #311

Open
wants to merge 2 commits into
base: develop
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
2 changes: 2 additions & 0 deletions API/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from .download_export import router as download_router
# from .test_router import router as test_router
from .status import router as status_router
from .system import router as health_router
from src.galaxy.db_session import database_instance
from src.galaxy.config import use_connection_pooling, use_s3_to_upload, logger as logging, config
from fastapi_versioning import VersionedFastAPI
Expand Down Expand Up @@ -71,6 +72,7 @@
app.include_router(tm_router)
app.include_router(status_router)
app.include_router(raw_data_router)
app.include_router(health_router)

if use_s3_to_upload is False:
# only mount the disk if config is set to disk
Expand Down
50 changes: 50 additions & 0 deletions API/system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (C) 2021 Humanitarian OpenStreetmap Team

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

# Humanitarian OpenStreetmap Team
# 1100 13th Street NW Suite 800 Washington, D.C. 20005
# <[email protected]>

"""
Router Responsible for Checking API Health
"""

from fastapi import APIRouter
from fastapi_versioning import version
from src.galaxy.config import MAIN_API_URL
from src.galaxy.validation.models import SystemHealthOutput, SystemHealthType
from src.galaxy.app import SystemHealth
from .test_data import raw_data_testing_query, mapathon_testing_query

router = APIRouter(prefix="/health")

@router.get("/", response_model=SystemHealthOutput)
@version(1)
def check_system_health():
"""Simple health check for API accessibility"""

authentication = SystemHealth().monitor_endpoint(endpoint=f'{MAIN_API_URL}/auth/login/', request_type='GET')
mapathon_summary = SystemHealth().monitor_endpoint(endpoint=f'{MAIN_API_URL}/mapathon/summary/', request_type='POST', body=mapathon_testing_query)
mapathon_detail = SystemHealth().monitor_endpoint(endpoint=f'{MAIN_API_URL}/mapathon/detail/', request_type='POST', body=mapathon_testing_query)
raw_data = SystemHealth().monitor_endpoint(endpoint=f'{MAIN_API_URL}/raw-data/current-snapshot/', request_type='POST', body=raw_data_testing_query)

return {
"authentication" : SystemHealthType.HEALTHY.value if authentication == 200 else SystemHealthType.UNHEALTHY.value,
"mapathon_summary" : SystemHealthType.HEALTHY.value if mapathon_summary == 200 else SystemHealthType.UNHEALTHY.value,
"mapathon_detail" : SystemHealthType.HEALTHY.value if mapathon_detail == 200 else SystemHealthType.UNHEALTHY.value,
"raw_data" : SystemHealthType.HEALTHY.value if raw_data == 200 else SystemHealthType.UNHEALTHY.value
}


36 changes: 36 additions & 0 deletions API/test_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
raw_data_testing_query = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think moving these to /src/tests/fixtures will be fruitful

"geometry": {
"type": "Polygon",
"coordinates": [
[
[
83.96919250488281,
28.194446860487773
],
[
83.99751663208006,
28.194446860487773
],
[
83.99751663208006,
28.214869548073377
],
[
83.96919250488281,
28.214869548073377
],
[
83.96919250488281,
28.194446860487773
]
]
]
}
}

mapathon_testing_query = {
"fromTimestamp":"2022-07-22T13:15:00.461",
"toTimestamp":"2022-07-22T14:15:00.461",
"projectIds":[],
"hashtags":[ "missingmaps" ]
}
6 changes: 5 additions & 1 deletion src/config.txt.sample
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ secret_key=PutSomethingRandmHere
env=dev

#############
# OPTIONNAL #
# OPTIONAL #
#############

# If enable this [API_CONFIG] section, remove the previous one
Expand All @@ -49,6 +49,10 @@ env=dev
#env=dev # default is prod , supported values are dev and prod
#shp_limit=6000 # in mb default is 4096

#[API_MONITORING]
#MAIN_API_URL=http://127.0.0.1:8000/v1/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming to API URL ?

#ACCESS_TOKEN=''

#[EXPORT_UPLOAD]
#FILE_UPLOAD_METHOD=disk # options are s3,disk
#AWS_ACCESS_KEY_ID= your id
Expand Down
15 changes: 14 additions & 1 deletion src/galaxy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import os
import sys
import threading
from .config import get_db_connection_params, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, BUCKET_NAME, level, logger as logging, export_path, use_connection_pooling, shp_limit
import requests
from .config import get_db_connection_params, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, BUCKET_NAME, level, logger as logging, export_path, use_connection_pooling, shp_limit, ACCESS_TOKEN
from .validation.models import Source
from psycopg2 import connect, sql
from psycopg2.extras import DictCursor
Expand Down Expand Up @@ -1324,3 +1325,15 @@ def __call__(self, bytes_amount):
percentage = (self._seen_so_far / self._size) * 100
logging.debug("\r%s %s / %s (%.2f%%)", self._filename,
self._seen_so_far, self._size, percentage)


class SystemHealth(object):
@staticmethod
def monitor_endpoint(endpoint, request_type, body=None):
"""returns the response status code for a request made to a particular API endpoint"""
if request_type == 'GET':
get_req = requests.get(endpoint)
return get_req.status_code
if request_type == 'POST':
post_req = requests.post(endpoint, json=body, headers={'access-token':ACCESS_TOKEN})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question , I can see from the website that access_token is not going to expire but have you tried it ? Do they remain same all the time ? Since you are reading it from config , I am just concerned if they will expire or not . Generally access token do expire

return post_req.status_code
3 changes: 3 additions & 0 deletions src/galaxy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
"value not supported for file_upload_method ,switching to default disk method")
use_s3_to_upload = False

MAIN_API_URL = config.get("API_MONITORING", "MAIN_API_URL", fallback="http://127.0.0.1:8000/v1/")
ACCESS_TOKEN = config.get("API_MONITORING", "ACCESS_TOKEN", fallback=None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be checking and raising the error if access_token is not supplied would be fruitful since the monitoring depends on it , API should be able to run without those configuration , you can check if it is supplied or not when endpoint is called !



def get_db_connection_params(dbIdentifier: str) -> dict:
"""Return a python dict that can be passed to psycopg2 connections
Expand Down
10 changes: 10 additions & 0 deletions src/galaxy/validation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,13 @@ class DataOutput(str, Enum):
class DataRecencyParams(BaseModel):
data_source: DataSource
data_output: DataOutput

class SystemHealthType(Enum):
HEALTHY = "healthy"
UNHEALTHY = "unhealthy"

class SystemHealthOutput(BaseModel):
authentication: SystemHealthType
mapathon_summary: SystemHealthType
mapathon_detail: SystemHealthType
raw_data: SystemHealthType