kin: KIN-071 Добавить тип проекта: development / operations / research. Для operations: вместо path к локальной папке — ssh-доступ (host, user, key, proxy or jump). При создании operations-проекта запускается sysadmin-агент который подключается по SSH, обходит сервер, составляет карту: какие сервисы запущены (docker ps, systemctl), какие конфиги где лежат, какие порты открыты, какие версии. Результат сохраняется в decisions и modules как база знаний по серверу. Код не хранится локально — агенты работают через SSH. PM для operations вызывает sysadmin/debugger, не architect/frontend_dev.
This commit is contained in:
parent
d9172fc17c
commit
75fee86110
4 changed files with 371 additions and 0 deletions
|
|
@ -1916,3 +1916,113 @@ class TestSaveSysadminOutput:
|
|||
from agents.runner import _save_sysadmin_output
|
||||
result = _save_sysadmin_output(ops_conn, "srv", "SRV-001", {"raw_output": ""})
|
||||
assert result["decisions_added"] == 0
|
||||
|
||||
def test_full_sysadmin_output_format_saves_docker_and_systemctl_as_decisions(self, ops_conn):
|
||||
"""KIN-071: полный формат вывода sysadmin (docker ps + systemctl) → decisions + modules."""
|
||||
from agents.runner import _save_sysadmin_output
|
||||
# Симуляция реального вывода sysadmin-агента после docker ps и systemctl
|
||||
output = {
|
||||
"status": "done",
|
||||
"summary": "Ubuntu 22.04, nginx + postgres + app in docker",
|
||||
"os": "Ubuntu 22.04 LTS, kernel 5.15.0",
|
||||
"services": [
|
||||
{"name": "nginx", "type": "systemd", "status": "running", "note": "web proxy"},
|
||||
{"name": "myapp", "type": "docker", "image": "myapp:1.2.3", "ports": ["80:8080"]},
|
||||
{"name": "postgres", "type": "docker", "image": "postgres:15", "ports": ["5432:5432"]},
|
||||
],
|
||||
"open_ports": [
|
||||
{"port": 80, "proto": "tcp", "process": "nginx"},
|
||||
{"port": 5432, "proto": "tcp", "process": "postgres"},
|
||||
],
|
||||
"decisions": [
|
||||
{
|
||||
"type": "gotcha",
|
||||
"title": "nginx proxies to docker app on 8080",
|
||||
"description": "nginx.conf proxy_pass http://localhost:8080",
|
||||
"tags": ["nginx", "docker"],
|
||||
},
|
||||
{
|
||||
"type": "decision",
|
||||
"title": "postgres data on /var/lib/postgresql",
|
||||
"description": "Volume mount /var/lib/postgresql/data persists DB",
|
||||
"tags": ["postgres", "storage"],
|
||||
},
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "nginx",
|
||||
"type": "service",
|
||||
"path": "/etc/nginx",
|
||||
"description": "Reverse proxy",
|
||||
"owner_role": "sysadmin",
|
||||
},
|
||||
{
|
||||
"name": "myapp",
|
||||
"type": "docker",
|
||||
"path": "/opt/myapp",
|
||||
"description": "Main application container",
|
||||
},
|
||||
{
|
||||
"name": "postgres",
|
||||
"type": "docker",
|
||||
"path": "/var/lib/postgresql",
|
||||
"description": "Database",
|
||||
},
|
||||
],
|
||||
}
|
||||
result = _save_sysadmin_output(ops_conn, "srv", "SRV-001", {"raw_output": json.dumps(output)})
|
||||
|
||||
assert result["decisions_added"] == 2
|
||||
assert result["modules_added"] == 3
|
||||
|
||||
decisions = models.get_decisions(ops_conn, "srv")
|
||||
d_titles = {d["title"] for d in decisions}
|
||||
assert "nginx proxies to docker app on 8080" in d_titles
|
||||
assert "postgres data on /var/lib/postgresql" in d_titles
|
||||
|
||||
modules = models.get_modules(ops_conn, "srv")
|
||||
m_names = {m["name"] for m in modules}
|
||||
assert {"nginx", "myapp", "postgres"} == m_names
|
||||
|
||||
def test_invalid_decision_type_normalized_to_decision(self, ops_conn):
|
||||
"""KIN-071: тип 'workaround' не входит в VALID_DECISION_TYPES → нормализуется в 'decision'."""
|
||||
from agents.runner import _save_sysadmin_output
|
||||
output = {
|
||||
"decisions": [
|
||||
{
|
||||
"type": "workaround",
|
||||
"title": "Use /proc/net for port list",
|
||||
"description": "ss not installed, fallback to /proc/net/tcp",
|
||||
"tags": ["networking"],
|
||||
},
|
||||
],
|
||||
"modules": [],
|
||||
}
|
||||
_save_sysadmin_output(ops_conn, "srv", "SRV-001", {"raw_output": json.dumps(output)})
|
||||
decisions = models.get_decisions(ops_conn, "srv")
|
||||
assert len(decisions) == 1
|
||||
assert decisions[0]["type"] == "decision"
|
||||
|
||||
def test_decision_missing_title_skipped(self, ops_conn):
|
||||
"""KIN-071: decision без title пропускается."""
|
||||
from agents.runner import _save_sysadmin_output
|
||||
output = {
|
||||
"decisions": [
|
||||
{"type": "gotcha", "title": "", "description": "Something"},
|
||||
],
|
||||
"modules": [],
|
||||
}
|
||||
result = _save_sysadmin_output(ops_conn, "srv", "SRV-001", {"raw_output": json.dumps(output)})
|
||||
assert result["decisions_added"] == 0
|
||||
|
||||
def test_module_missing_name_skipped(self, ops_conn):
|
||||
"""KIN-071: module без name пропускается."""
|
||||
from agents.runner import _save_sysadmin_output
|
||||
output = {
|
||||
"decisions": [],
|
||||
"modules": [
|
||||
{"name": "", "type": "service", "path": "/etc/something"},
|
||||
],
|
||||
}
|
||||
result = _save_sysadmin_output(ops_conn, "srv", "SRV-001", {"raw_output": json.dumps(output)})
|
||||
assert result["modules_added"] == 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue