Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d
This commit is contained in:
commit
1dfbca08a1
528 files changed
+131054
No files matched your search
@@ -0,0 +1,326 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlmodel import SQLModel, create_engine
|
||||
|
||||
from backend.app.connectors import gitea_actions
|
||||
from backend.app.connectors.gitea_actions import GiteaActionsConnector
|
||||
from backend.app.core.config import settings
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(tmp_path, monkeypatch):
|
||||
db_path = tmp_path / "appops_test.db"
|
||||
monkeypatch.setenv("APPOPS_DB_PATH", str(db_path))
|
||||
|
||||
from backend.app.db import session
|
||||
from backend.app.db.init_db import init_db
|
||||
from backend.app.main import app
|
||||
|
||||
engine = create_engine(
|
||||
f"sqlite:///{Path(db_path)}", connect_args={"check_same_thread": False}
|
||||
)
|
||||
session.engine = engine
|
||||
SQLModel.metadata.drop_all(engine)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
init_db()
|
||||
|
||||
with TestClient(app) as client:
|
||||
yield client
|
||||
|
||||
|
||||
def _configure(monkeypatch):
|
||||
monkeypatch.setitem(settings.apis, "GITEA_URL", "https://gitea.test")
|
||||
monkeypatch.setitem(settings.apis, "GITEA_ADMIN_TOKEN", "fixture-secret")
|
||||
monkeypatch.setitem(
|
||||
settings.apis,
|
||||
"GITEA_RUNNER_TARGETS_JSON",
|
||||
'{"unraid-deploy":"itworx-security-runner"}',
|
||||
)
|
||||
monkeypatch.setattr(gitea_actions, "_CACHE", None)
|
||||
|
||||
|
||||
def test_runner_snapshot_recommends_only_approval_gated_restart_for_stalled_queue(
|
||||
monkeypatch,
|
||||
):
|
||||
_configure(monkeypatch)
|
||||
connector = GiteaActionsConnector()
|
||||
created_at = (datetime.now(UTC) - timedelta(minutes=12)).isoformat()
|
||||
|
||||
def fake_request(path, params=None):
|
||||
if path.endswith("/runners"):
|
||||
return {
|
||||
"total_count": 1,
|
||||
"runners": [
|
||||
{
|
||||
"id": 7,
|
||||
"name": "deploy-runner",
|
||||
"status": "offline",
|
||||
"busy": False,
|
||||
"disabled": False,
|
||||
"labels": [{"name": "unraid-deploy"}],
|
||||
}
|
||||
],
|
||||
}
|
||||
if params.get("status") == "queued":
|
||||
return {
|
||||
"total_count": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": 8,
|
||||
"status": "queued",
|
||||
"created_at": created_at,
|
||||
"labels": ["unraid-deploy"],
|
||||
}
|
||||
],
|
||||
}
|
||||
return {"total_count": 0, "jobs": []}
|
||||
|
||||
monkeypatch.setattr(connector, "_request", fake_request)
|
||||
result = connector.snapshot(refresh=True)
|
||||
|
||||
assert result["severity"] == "critical"
|
||||
assert result["queue"]["queued"] == 1
|
||||
assert result["queue"]["oldestQueuedAgeSeconds"] >= 600
|
||||
assert result["queue"]["sampleTruncated"] is False
|
||||
assert result["recommendedAction"] == {
|
||||
"intent": "container.restart",
|
||||
"target": "itworx-security-runner",
|
||||
"requiresApproval": True,
|
||||
"autoExecute": False,
|
||||
"reason": "Gitea Actions queue is stalled without a matching online runner.",
|
||||
}
|
||||
assert "fixture-secret" not in repr(result)
|
||||
|
||||
|
||||
def test_runner_snapshot_is_cached_and_healthy_when_queue_is_clear(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
connector = GiteaActionsConnector()
|
||||
calls = 0
|
||||
|
||||
def fake_request(path, params=None):
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
if path.endswith("/runners"):
|
||||
return {
|
||||
"total_count": 1,
|
||||
"runners": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "shared",
|
||||
"status": "online",
|
||||
"labels": ["ubuntu-latest"],
|
||||
}
|
||||
],
|
||||
}
|
||||
return {"total_count": 0, "jobs": []}
|
||||
|
||||
monkeypatch.setattr(connector, "_request", fake_request)
|
||||
first = connector.snapshot(refresh=True)
|
||||
second = connector.snapshot()
|
||||
|
||||
assert first["severity"] == "healthy"
|
||||
assert first["recommendedAction"] is None
|
||||
assert second["cache"]["hit"] is True
|
||||
assert calls == 4
|
||||
|
||||
|
||||
def test_long_running_shared_job_is_critical_without_ambiguous_restart(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
monkeypatch.setitem(settings.apis, "GITEA_RUNNER_TARGETS_JSON", "{}")
|
||||
connector = GiteaActionsConnector()
|
||||
created_at = (datetime.now(UTC) - timedelta(hours=3)).isoformat()
|
||||
|
||||
def fake_request(path, params=None):
|
||||
if path.endswith("/runners"):
|
||||
return {
|
||||
"total_count": 1,
|
||||
"runners": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "shared",
|
||||
"status": "online",
|
||||
"busy": True,
|
||||
"labels": ["ubuntu-latest"],
|
||||
}
|
||||
],
|
||||
}
|
||||
if params.get("status") == "in_progress":
|
||||
return {
|
||||
"total_count": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": 9,
|
||||
"status": "in_progress",
|
||||
"created_at": created_at,
|
||||
"labels": ["ubuntu-latest"],
|
||||
}
|
||||
],
|
||||
}
|
||||
return {"total_count": 0, "jobs": []}
|
||||
|
||||
monkeypatch.setattr(connector, "_request", fake_request)
|
||||
result = connector.snapshot(refresh=True)
|
||||
|
||||
assert result["severity"] == "critical"
|
||||
assert result["queue"]["oldestInProgressAgeSeconds"] >= 7_200
|
||||
assert result["recommendedAction"] is None
|
||||
|
||||
|
||||
def test_recently_started_old_queued_job_is_not_reported_as_stale(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
connector = GiteaActionsConnector()
|
||||
created_at = (datetime.now(UTC) - timedelta(hours=3)).isoformat()
|
||||
started_at = (datetime.now(UTC) - timedelta(seconds=30)).isoformat()
|
||||
|
||||
def fake_request(path, params=None):
|
||||
if path.endswith("/runners"):
|
||||
return {
|
||||
"total_count": 1,
|
||||
"runners": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "shared",
|
||||
"status": "online",
|
||||
"busy": True,
|
||||
"labels": ["ubuntu-latest"],
|
||||
}
|
||||
],
|
||||
}
|
||||
if params.get("status") == "in_progress":
|
||||
return {
|
||||
"total_count": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": 9,
|
||||
"status": "in_progress",
|
||||
"created_at": created_at,
|
||||
"started_at": started_at,
|
||||
"labels": ["ubuntu-latest"],
|
||||
}
|
||||
],
|
||||
}
|
||||
return {"total_count": 0, "jobs": []}
|
||||
|
||||
monkeypatch.setattr(connector, "_request", fake_request)
|
||||
result = connector.snapshot(refresh=True)
|
||||
|
||||
assert result["severity"] == "healthy"
|
||||
assert 0 <= result["queue"]["oldestInProgressAgeSeconds"] < 120
|
||||
assert result["queue"]["staleActiveRunnerLabels"] == []
|
||||
|
||||
|
||||
def test_long_running_job_recommends_only_exact_mapped_runner(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
connector = GiteaActionsConnector()
|
||||
created_at = (datetime.now(UTC) - timedelta(hours=3)).isoformat()
|
||||
|
||||
def fake_request(path, params=None):
|
||||
if path.endswith("/runners"):
|
||||
return {
|
||||
"total_count": 1,
|
||||
"runners": [
|
||||
{
|
||||
"id": 7,
|
||||
"name": "deploy-runner",
|
||||
"status": "online",
|
||||
"busy": True,
|
||||
"labels": ["unraid-deploy"],
|
||||
}
|
||||
],
|
||||
}
|
||||
if params.get("status") == "in_progress":
|
||||
return {
|
||||
"total_count": 1,
|
||||
"jobs": [
|
||||
{
|
||||
"id": 10,
|
||||
"status": "in_progress",
|
||||
"created_at": created_at,
|
||||
"labels": ["unraid-deploy"],
|
||||
}
|
||||
],
|
||||
}
|
||||
return {"total_count": 0, "jobs": []}
|
||||
|
||||
monkeypatch.setattr(connector, "_request", fake_request)
|
||||
result = connector.snapshot(refresh=True)
|
||||
|
||||
assert result["recommendedAction"] == {
|
||||
"intent": "container.restart",
|
||||
"target": "itworx-security-runner",
|
||||
"requiresApproval": True,
|
||||
"autoExecute": False,
|
||||
"reason": "A Gitea Actions job exceeded the two-hour guardrail on an explicitly mapped runner.",
|
||||
}
|
||||
|
||||
|
||||
def test_runner_status_endpoint_degrades_when_not_configured(api_client, monkeypatch):
|
||||
monkeypatch.setitem(settings.apis, "GITEA_URL", None)
|
||||
monkeypatch.setitem(settings.apis, "GITEA_ADMIN_TOKEN", None)
|
||||
monkeypatch.setattr(gitea_actions, "_CACHE", None)
|
||||
|
||||
response = api_client.get("/api/gitea/actions/status")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "missing_config"
|
||||
assert response.json()["recommendedAction"] is None
|
||||
|
||||
|
||||
def test_default_docker_target_falls_back_to_v1_capability(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
monkeypatch.setitem(settings.apis, "GITEA_RUNNER_TARGETS_JSON", "{}")
|
||||
|
||||
target = GiteaActionsConnector._target_for_label(
|
||||
"docker-validation",
|
||||
[
|
||||
{
|
||||
"name": "itworx-validation-runner-2",
|
||||
"labels": ["linux-validation", "docker-validation"],
|
||||
}
|
||||
],
|
||||
GiteaActionsConnector._restart_targets(),
|
||||
)
|
||||
|
||||
assert target == "itworx-validation-runner-2"
|
||||
|
||||
|
||||
def test_default_docker_target_prefers_v2_specialized_runner(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
monkeypatch.setitem(settings.apis, "GITEA_RUNNER_TARGETS_JSON", "{}")
|
||||
|
||||
target = GiteaActionsConnector._target_for_label(
|
||||
"docker-validation",
|
||||
[
|
||||
{
|
||||
"name": "itworx-validation-runner-2",
|
||||
"labels": ["linux-validation"],
|
||||
},
|
||||
{
|
||||
"name": "itworx-docker-runner",
|
||||
"labels": ["docker-validation"],
|
||||
},
|
||||
],
|
||||
GiteaActionsConnector._restart_targets(),
|
||||
)
|
||||
|
||||
assert target == "itworx-docker-runner"
|
||||
|
||||
|
||||
def test_explicit_runner_target_mapping_remains_authoritative(monkeypatch):
|
||||
_configure(monkeypatch)
|
||||
monkeypatch.setitem(
|
||||
settings.apis,
|
||||
"GITEA_RUNNER_TARGETS_JSON",
|
||||
'{"docker-validation":"operator-selected-container"}',
|
||||
)
|
||||
|
||||
target = GiteaActionsConnector._target_for_label(
|
||||
"docker-validation",
|
||||
[{"name": "some-registration", "labels": ["docker-validation"]}],
|
||||
GiteaActionsConnector._restart_targets(),
|
||||
)
|
||||
|
||||
assert target == "operator-selected-container"
|
||||
Reference in new issue
Block a user