diff --git a/atv/__init__.py b/atv/__init__.py index e69de29..5c4105c 100644 --- a/atv/__init__.py +++ b/atv/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.1" diff --git a/atv/settings.py b/atv/settings.py index b3b28c4..56c16e7 100644 --- a/atv/settings.py +++ b/atv/settings.py @@ -1,5 +1,6 @@ import os import subprocess +from datetime import datetime import environ import sentry_sdk @@ -65,11 +66,11 @@ TOKEN_AUTH_REQUIRE_SCOPE=(bool, False), TOKEN_AUTH_AUTHSERVER_URL=(str, ""), CLAMAV_HOST=(str, "atv-clamav"), + OPENSHIFT_BUILD_COMMIT=(str, ""), ) if os.path.exists(env_file): env.read_env(env_file) - VERSION = env("VERSION") if VERSION is None: try: @@ -282,3 +283,10 @@ # Malware Protection CLAMAV_HOST = env("CLAMAV_HOST") + + +# get build time from a file in docker image +APP_BUILDTIME = datetime.fromtimestamp(os.path.getmtime(__file__)) + +# get build commit from Openshift variable +BUILD_COMMIT = env("OPENSHIFT_BUILD_COMMIT") diff --git a/atv/urls.py b/atv/urls.py index d6002ea..5963362 100644 --- a/atv/urls.py +++ b/atv/urls.py @@ -1,6 +1,11 @@ +import threading +import time + +import pyclamd from django.conf import settings from django.contrib import admin -from django.http import HttpResponse +from django.db import connection +from django.http import HttpResponse, JsonResponse from django.urls import include, path from drf_spectacular.views import ( SpectacularAPIView, @@ -9,6 +14,7 @@ ) from rest_framework_extensions.routers import ExtendedSimpleRouter +from atv import __version__ from documents.api import AttachmentViewSet, DocumentViewSet from documents.api.viewsets import ( DocumentMetadataViewSet, @@ -68,9 +74,65 @@ # Kubernetes liveness & readiness probes # +# Global variable to store the health check results +health_status = { + "db": {"message": "Initializing"}, + "clamav": {"message": "Initializing"}, +} + + +def check_db_connection(): + global health_status + while True: + try: + with connection.cursor() as cursor: + cursor.execute("SELECT 1") + health_status["db"] = {"message": "OK"} + except Exception as ex: + health_status["db"] = {"error": str(ex)} + time.sleep(300) # Sleep for 5 minutes + + +def check_clamav_connection(): + global health_status + while True: + try: + cd = pyclamd.ClamdNetworkSocket(host=settings.CLAMAV_HOST) + cd.ping() + health_status["clamav"] = {"message": "OK"} + except Exception as ex: + health_status["clamav"] = {"error": str(ex)} + time.sleep(300) # Sleep for 5 minutes + + +# Start the health check threads +threading.Thread(target=check_db_connection, daemon=True).start() +threading.Thread(target=check_clamav_connection, daemon=True).start() + def healthz(*args, **kwargs): - return HttpResponse(status=200) + response_data = { + "packageVersion": __version__, + "commitHash": settings.BUILD_COMMIT, + "buildTime": settings.APP_BUILDTIME, + "status": {"message": {}, "error": {}}, + } + + for key, status in health_status.items(): + if "message" in status and status["message"] == "OK": + response_data["status"]["message"][key] = status["message"] + else: + response_data["status"]["error"][key] = status.get("error", "Unknown error") + + status_code = ( + 200 + if all( + "message" in status and status["message"] == "OK" + for status in health_status.values() + ) + else 200 + ) + return JsonResponse(response_data, status=status_code) def readiness(*args, **kwargs):