124 lines
4.1 KiB
Python
124 lines
4.1 KiB
Python
"""Sanitized GitOps runtime status for the AIOS Web control plane."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
from dataclasses import dataclass, asdict
|
|
from pathlib import Path
|
|
from typing import Any, Mapping
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class GitOpsStatus:
|
|
available: bool
|
|
service_status: str
|
|
workspace_mode: str
|
|
workspace_branch: str | None
|
|
workspace_ref: str | None
|
|
workspace_dirty: bool | None
|
|
candidate_branch: str
|
|
release_channel: str
|
|
release_version: str
|
|
sync_status: str
|
|
details: Mapping[str, Any]
|
|
|
|
def as_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
def _run_git(worktree: Path, *args: str) -> tuple[int, str, str]:
|
|
process = subprocess.run(
|
|
["git", "-C", str(worktree), *args],
|
|
text=True,
|
|
capture_output=True,
|
|
timeout=5,
|
|
)
|
|
return process.returncode, process.stdout.strip(), process.stderr.strip()
|
|
|
|
|
|
def collect_gitops_status() -> GitOpsStatus:
|
|
"""Return only non-sensitive Git/workspace metadata.
|
|
|
|
Remote URLs, environment values, credentials, filesystem contents and
|
|
command stderr are deliberately excluded from the Web response.
|
|
"""
|
|
|
|
candidate_branch = os.getenv(
|
|
"AIOS_CANDIDATE_BRANCH",
|
|
"integration/aios-v2r1-cto-production-candidate-20260720",
|
|
)
|
|
release_channel = os.getenv("AIOS_RELEASE_CHANNEL", "candidate")
|
|
release_version = os.getenv("AIOS_RELEASE_VERSION", "V2R1C0-candidate")
|
|
sync_status = os.getenv("AIOS_GITOPS_SYNC_STATUS", "unknown")
|
|
worktree = Path(
|
|
os.getenv(
|
|
"AIOS_GITOPS_CONTROLLER_WORKTREE",
|
|
"/data/aios-gitops/worktrees/controller",
|
|
)
|
|
)
|
|
|
|
if not worktree.is_dir():
|
|
return GitOpsStatus(
|
|
available=False,
|
|
service_status="unknown",
|
|
workspace_mode="unavailable",
|
|
workspace_branch=None,
|
|
workspace_ref=None,
|
|
workspace_dirty=None,
|
|
candidate_branch=candidate_branch,
|
|
release_channel=release_channel,
|
|
release_version=release_version,
|
|
sync_status=sync_status,
|
|
details={"reason": "controller_worktree_unavailable"},
|
|
)
|
|
|
|
inside_code, inside, _ = _run_git(worktree, "rev-parse", "--is-inside-work-tree")
|
|
if inside_code != 0 or inside != "true":
|
|
return GitOpsStatus(
|
|
available=False,
|
|
service_status="unknown",
|
|
workspace_mode="invalid",
|
|
workspace_branch=None,
|
|
workspace_ref=None,
|
|
workspace_dirty=None,
|
|
candidate_branch=candidate_branch,
|
|
release_channel=release_channel,
|
|
release_version=release_version,
|
|
sync_status=sync_status,
|
|
details={"reason": "controller_worktree_invalid"},
|
|
)
|
|
|
|
branch_code, branch, _ = _run_git(worktree, "branch", "--show-current")
|
|
ref_code, ref_value, _ = _run_git(worktree, "rev-parse", "HEAD")
|
|
dirty_code, dirty_value, _ = _run_git(worktree, "status", "--porcelain=v1")
|
|
|
|
service_status = "unknown"
|
|
try:
|
|
process = subprocess.run(
|
|
["systemctl", "is-active", "aios-gitops-model-api"],
|
|
text=True,
|
|
capture_output=True,
|
|
timeout=3,
|
|
)
|
|
service_status = process.stdout.strip() or "inactive"
|
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
service_status = "unavailable"
|
|
|
|
branch_value = branch if branch_code == 0 and branch else None
|
|
return GitOpsStatus(
|
|
available=True,
|
|
service_status=service_status,
|
|
workspace_mode="branch" if branch_value else "detached",
|
|
workspace_branch=branch_value,
|
|
workspace_ref=ref_value if ref_code == 0 else None,
|
|
workspace_dirty=bool(dirty_value) if dirty_code == 0 else None,
|
|
candidate_branch=candidate_branch,
|
|
release_channel=release_channel,
|
|
release_version=release_version,
|
|
sync_status=sync_status,
|
|
details={
|
|
"controller_clean": dirty_code == 0 and not dirty_value,
|
|
"controller_detached": branch_code == 0 and not branch,
|
|
},
|
|
)
|