add readiness probe

This commit is contained in:
Waylon S. Walker 2025-04-09 14:31:46 -05:00
parent e181f57a91
commit 008bd81a33
5 changed files with 359 additions and 2 deletions

View file

@ -7,6 +7,7 @@ from htmx_patterns.config import get_config
from htmx_patterns.infinite.router import infinite_router
from htmx_patterns.toast.router import toast_router
from htmx_patterns.websocket.router import websocket_router
from htmx_patterns.zpages.router import zpages_router
def set_prefers(
@ -44,6 +45,7 @@ app.include_router(infinite_router)
app.include_router(boosted_router)
app.include_router(toast_router)
app.include_router(websocket_router)
app.include_router(zpages_router)
@app.get("/")

View file

@ -15,6 +15,7 @@ from sqlmodel import Session
from typing import TYPE_CHECKING
from htmx_patterns import models
from htmx_patterns.__about__ import __version__
if TYPE_CHECKING:
from sqlalchemy.engine import Engine
@ -84,7 +85,7 @@ def get_templates(config: BaseSettings) -> Jinja2Templates:
).strftime("%B %d, %Y")
templates.env.globals["url_for"] = url_for_query
templates.env.globals["config"] = config
console.print(f'Using environment: {os.environ.get("ENV")}')
console.print(f"Using environment: {os.environ.get('ENV')}")
# if os.environ.get("ENV") in ["dev", "qa", "prod"]:
# templates.env.globals["url_for"] = https_url_for
@ -100,6 +101,7 @@ class Config(BaseSettings):
the_templates: Optional[Jinja2Templates] = Field(None, exclude=True)
api_server: ApiServer = ApiServer()
database_url: str = "sqlite:///database.db"
app_version: str = __version__
@property
def templates(self) -> Jinja2Templates:

View file

@ -0,0 +1,91 @@
from datetime import datetime, timezone
from kubernetes import client, config as kubernetes_config
from kubernetes.config.config_exception import ConfigException
import os
from pydantic import BaseModel
from fastapi import APIRouter
from htmx_patterns.config import get_config
zpages_router = APIRouter(tags=["zpages"])
config = get_config()
def format_uptime(seconds: int) -> str:
minutes, sec = divmod(seconds, 60)
hours, min = divmod(minutes, 60)
days, hr = divmod(hours, 24)
parts = []
if days:
parts.append(f"{days}d")
if hr:
parts.append(f"{hr}h")
if min:
parts.append(f"{min}m")
parts.append(f"{sec}s")
return " ".join(parts)
class PodInfo(BaseModel):
pod_name: str
namespace: str
node_name: str
container_image: str
start_time: str
pod_uptime: str
class Ready(BaseModel):
ready: bool
timestamp: str
app_version: str
pod_info: PodInfo
def get_pod_info():
pod_name = os.getenv("KUBERNETES_POD_NAME")
namespace = os.getenv("KUBERNETES_POD_NAMESPACE")
try:
kubernetes_config.load_incluster_config()
except ConfigException:
return PodInfo(
pod_name="unknown",
namespace="unknown",
node_name="unknown",
pod_ip="unknown",
container_image="unknown",
start_time="unknown",
pod_uptime="unknown",
)
v1 = client.CoreV1Api()
pod = v1.read_namespaced_pod(name=pod_name, namespace=namespace)
start_time = pod.status.start_time
now = datetime.now(timezone.utc)
uptime_seconds = int((now - start_time).total_seconds())
return PodInfo(
pod_name=pod.metadata.name,
namespace=pod.metadata.namespace,
node_name=pod.spec.node_name,
container_image=pod.spec.containers[0].image,
start_time=pod.status.start_time,
pod_uptime=format_uptime(uptime_seconds),
)
@zpages_router.get("/readyz")
async def root() -> Ready:
pod_info = get_pod_info()
ready = Ready(
ready=True,
timestamp=datetime.now(timezone.utc).isoformat(),
app_version=config.app_version,
pod_info=pod_info,
)
return ready