SysOps: deploy-all — 2026-06-09 10:41 UTC

This commit is contained in:
sysops
2026-06-09 10:41:13 +00:00
parent 69fe67cc0e
commit 21ea3a2c81
82 changed files with 8906 additions and 981 deletions
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Generate Herman daily CEO briefing with full activity documentation."""
from __future__ import annotations
import json
import sys
import urllib.request
def main() -> int:
url = "http://127.0.0.1:8600/api/herman/briefing"
req = urllib.request.Request(url, data=b"{}", headers={"Content-Type": "application/json"}, method="POST")
try:
with urllib.request.urlopen(req, timeout=180) as resp:
data = json.loads(resp.read().decode("utf-8"))
print("Herman briefing generated:", (data.get("generated_at") or "")[:19])
print("Stats pending approvals:", (data.get("stats") or {}).get("pending_approvals"))
return 0
except Exception as exc:
print("Herman briefing failed:", exc, file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Scheduled platform update briefing for Herman — documents all recent platform work."""
from __future__ import annotations
import json
import sys
import urllib.request
from datetime import datetime, timezone
PLATFORM_UPDATE = """
Platform update Foodlinkk Command Center:
1. Projecten — unified opslag voor foto's, packaging, agent-output met klantkoppeling
2. Herman dashboard — widgets slepen, 5 viz-modi, agent goedkeuringen op dashboard
3. IT Ops — Dell R340 topologie, SysOps dagelijkse backup + update-scan via approval
4. Agent assets — marketing publish, research briefs, approvals → auto project koppeling
5. Viz engine — Marketing, Agents, IT Ops pagina's met 5 visualisatiestijlen
6. SysOps workflow — vraagt nu eerst goedkeuring voor update-scan vóór uitvoering
7. Herman briefing — documenteert dagelijks alle agent-acties in activiteitenlog
"""
def main() -> int:
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
print(f"=== Herman platform update briefing {now} ===")
try:
req = urllib.request.Request(
"http://127.0.0.1:8600/api/herman/briefing",
data=b"{}",
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=180) as resp:
briefing = json.loads(resp.read().decode("utf-8"))
print("Briefing OK — generated_at:", (briefing.get("generated_at") or "")[:19])
except Exception as exc:
print("Briefing generation failed:", exc, file=sys.stderr)
return 1
print(PLATFORM_UPDATE)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Maak SysOps Gitea-gebruiker aan (login: sysops) en geef write op backup-repo.
set -euo pipefail
GITEA_URL="${GITEA_URL:-http://127.0.0.1:3001}"
ADMIN_USER="${GITEA_ADMIN_USER:-aissa}"
ADMIN_PASS="${GITEA_ADMIN_PASS:-Foodlinkk#2026}"
SYSOPS_USER="${GITEA_SYSOPS_USER:-sysops}"
SYSOPS_PASS="${GITEA_SYSOPS_PASS:-Foodlinkk#SysOps2026}"
SYSOPS_EMAIL="${GITEA_SYSOPS_EMAIL:-sysops@foodlinkk.local}"
REPO_OWNER="${GITEA_REPO_OWNER:-aissa}"
REPO_NAME="${GITEA_REPO_NAME:-foodlinkk-command-center}"
CONTAINER="${GITEA_CONTAINER:-foodlinkk_gitea}"
echo "=== Gitea SysOps setup ($SYSOPS_USER) ==="
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
if ! docker exec "$CONTAINER" gitea admin user list 2>/dev/null | grep -q "^${SYSOPS_USER} "; then
echo "Creating user $SYSOPS_USER..."
docker exec "$CONTAINER" gitea admin user create \
--username "$SYSOPS_USER" \
--password "$SYSOPS_PASS" \
--email "$SYSOPS_EMAIL" \
--must-change-password=false 2>/dev/null || true
else
echo "User $SYSOPS_USER exists — updating password..."
docker exec "$CONTAINER" gitea admin user change-password \
--username "$SYSOPS_USER" \
--password "$SYSOPS_PASS" 2>/dev/null || true
fi
else
echo "Gitea container not running — skip user create (will retry on deploy)"
fi
enc_pass() {
python3 -c "import urllib.parse; print(urllib.parse.quote('''$1''', safe=''))"
}
SYSOPS_PASS_ENC=$(enc_pass "$SYSOPS_PASS")
AUTH=$(printf '%s:%s' "$ADMIN_USER" "$ADMIN_PASS" | base64 -w0 2>/dev/null || printf '%s:%s' "$ADMIN_USER" "$ADMIN_PASS" | base64)
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-X PUT "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/collaborators/${SYSOPS_USER}" \
-H "Authorization: Basic ${AUTH}" \
-H "Content-Type: application/json" \
-d '{"permission":"write"}' 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "204" ] || [ "$HTTP_CODE" = "201" ]; then
echo "Collaborator $SYSOPS_USER → write on ${REPO_OWNER}/${REPO_NAME}"
elif [ "$HTTP_CODE" = "422" ]; then
echo "Collaborator already configured"
else
echo "Note: collaborator API returned HTTP $HTTP_CODE (repo may not exist yet)"
fi
REMOTE="http://${SYSOPS_USER}:${SYSOPS_PASS_ENC}@gitea:3001/${REPO_OWNER}/${REPO_NAME}.git"
echo "GITEA_BACKUP_REMOTE=$REMOTE"
echo "=== SysOps kan pushen als $SYSOPS_USER ==="
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""SysOps daily — approval requests for update scan + Gitea backup (no auto-scan)."""
from __future__ import annotations
import json
import sys
import urllib.request
from datetime import datetime, timezone
COCKPIT_URL = "http://127.0.0.1:8600"
def _post(url: str, payload: dict | None = None, timeout: int = 60) -> dict:
data = json.dumps(payload or {}).encode("utf-8")
req = urllib.request.Request(
url,
data=data,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=timeout) as resp:
return json.loads(resp.read().decode("utf-8"))
def _get(url: str, timeout: int = 30) -> dict:
with urllib.request.urlopen(url, timeout=timeout) as resp:
return json.loads(resp.read().decode("utf-8"))
def _already_pending(action_type: str) -> bool:
try:
data = _get(f"{COCKPIT_URL}/api/agents/approvals?status=pending&limit=50")
return any(
i.get("agent_key") == "sysops" and i.get("action_type") == action_type
for i in (data.get("items") or [])
)
except Exception:
return False
def main() -> int:
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
results: dict = {"date": today, "steps": []}
if not _already_pending("maintenance_scan"):
try:
scan_req = _post(
f"{COCKPIT_URL}/api/agents/requests",
{
"agent_key": "sysops",
"action_type": "maintenance_scan",
"title": f"Mag ik een update-scan uitvoeren op VM106? — {today}",
"query_payload": {
"scope": "vm106",
"checks": ["apt upgrades", "disk usage", "docker health", "proxmox reachability"],
"host": "10.4.7.18",
},
},
)
results["steps"].append({"maintenance_scan_request": scan_req})
print("Maintenance scan approval request:", (scan_req.get("request") or {}).get("id"))
except Exception as exc:
results["steps"].append({"maintenance_scan_error": str(exc)})
print("Maintenance scan request failed:", exc, file=sys.stderr)
else:
print("Maintenance scan request already pending — skipped")
results["steps"].append({"maintenance_scan_request": "already_pending"})
if not _already_pending("config_backup"):
try:
backup_req = _post(
f"{COCKPIT_URL}/api/agents/requests",
{
"agent_key": "sysops",
"action_type": "config_backup",
"title": f"Dagelijkse config backup naar Gitea — {today}",
"query_payload": {
"target": "gitea",
"repo": "aissa/foodlinkk-command-center",
"paths": ["docker-compose.yml", "migrations", "monitoring"],
},
},
)
results["steps"].append({"backup_request": backup_req})
print("Backup approval request:", (backup_req.get("request") or {}).get("id"))
except Exception as exc:
results["steps"].append({"backup_error": str(exc)})
print("Backup request failed:", exc, file=sys.stderr)
return 1
else:
print("Backup request already pending — skipped")
results["steps"].append({"backup_request": "already_pending"})
print(json.dumps(results, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())