feat: FastAPI console with dashboard, Pulp client, healthz (stages 0-1)
- config: PULP_* env + 사내 CA(verify=) 대응 - pulp_client: Basic Auth get/post/patch, status/list_repos/get_version - BFF 라우트: GET / (대시보드), GET /repos (조각), GET /healthz - views: Pulp JSON → 표시 모델 (GPG 검증 배지 3단계, 요약 집계) - HTMX + Jinja 템플릿, vendored pico.css/htmx.js (CDN 의존 0) - 브라우저는 Pulp 직접 호출 안 함 — 모두 BFF 경유 (스펙 §2) - 테스트: pulp_client(respx), 라우트(dependency_overrides), views 순수함수 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
68
app/pulp_client.py
Normal file
68
app/pulp_client.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Pulp REST API 호출 래퍼 (스펙 §7).
|
||||
|
||||
브라우저가 아니라 이 BFF 가 Basic Auth 를 쥐고 Pulp 를 호출한다(스펙 §2).
|
||||
객체는 이름이 아니라 ``pulp_href``(경로 문자열) 기준으로 참조한다(스펙 §4).
|
||||
|
||||
폴링은 화면(HTMX)이 담당하므로 여기서는 task 단발 조회만 둔다. sync/deploy 등
|
||||
도메인 메서드는 이후 단계에서 추가한다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from .config import Settings, get_settings, tls_verify
|
||||
|
||||
API_PREFIX = "/pulp/api/v3"
|
||||
|
||||
|
||||
class PulpClient:
|
||||
def __init__(self, settings: Settings | None = None) -> None:
|
||||
self.settings = settings or get_settings()
|
||||
self._client = httpx.Client(
|
||||
base_url=self.settings.pulp_base_url.rstrip("/"),
|
||||
auth=(self.settings.pulp_username, self.settings.pulp_password),
|
||||
verify=tls_verify(self.settings),
|
||||
timeout=30.0,
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
def __enter__(self) -> PulpClient:
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc: object) -> None:
|
||||
self.close()
|
||||
|
||||
# --- 저수준 호출 (Basic Auth 포함) ---------------------------------
|
||||
def get(self, path: str, **kwargs: Any) -> dict[str, Any]:
|
||||
resp = self._client.get(path, **kwargs)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def post(self, path: str, json: Any | None = None, **kwargs: Any) -> dict[str, Any]:
|
||||
resp = self._client.post(path, json=json, **kwargs)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def patch(self, href: str, json: Any | None = None, **kwargs: Any) -> dict[str, Any]:
|
||||
resp = self._client.patch(href, json=json, **kwargs)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
# --- 도메인 헬퍼 ----------------------------------------------------
|
||||
def status(self) -> dict[str, Any]:
|
||||
"""헬스체크: GET /pulp/api/v3/status/"""
|
||||
return self.get(f"{API_PREFIX}/status/")
|
||||
|
||||
def list_repos(self) -> list[dict[str, Any]]:
|
||||
"""RPM 저장소 목록: GET /pulp/api/v3/repositories/rpm/rpm/ → results."""
|
||||
data = self.get(f"{API_PREFIX}/repositories/rpm/rpm/")
|
||||
return data.get("results", [])
|
||||
|
||||
def get_version(self, version_href: str) -> dict[str, Any]:
|
||||
"""RepositoryVersion 단건 조회 (href 기준). content_summary 등 포함."""
|
||||
return self.get(version_href)
|
||||
Reference in New Issue
Block a user