Files
pragent/k8s/pragent-dashboard.yaml

118 lines
4.4 KiB
YAML

# pragent pilot — central dashboard service.
#
# Read-only overview + per-repo / per-PR drilldown over the same SQLite
# feedback DB the webhook writes. Also mutates `.pr-review.json` on covered
# repos via the Gitea contents API (Tasks C+D in pilot/dashboard.py). Same
# image as the webhook (`pragent-webhook:optin`) — all pilot modules are
# baked in at /app/pilot/.
#
# Routes: GET / (overview), GET /r/<o>/<n> (repo), GET /r/<o>/<n>/<i> (PR),
# GET /r/<o>/<n>/<i>/raw (PR markdown raw), GET /login, GET /static/style.css,
# POST /login, POST /r/<o>/<n>/edit.
#
# Auth: PRAGENT_DASHBOARD_TOKEN in the pragent-webhook Secret, cookie
# `pragent_dash=<token>`, single-user. Empty / unset = no auth (tailnet-only).
#
# NodePort 30082 — only reachable on the Tailscale / LAN side of kubernets
# (100.74.17.70 / 192.168.1.80) until/unconfigured. Mirrors pragent-webhook.yaml
# in every other respect (uid 10001, nodeSelector, /data PVC).
apiVersion: apps/v1
kind: Deployment
metadata:
name: pragent-dashboard
namespace: pragent
labels:
app: pragent-dashboard
spec:
replicas: 1
selector:
matchLabels:
app: pragent-dashboard
template:
metadata:
labels:
app: pragent-dashboard
spec:
# Same node as the webhook — holds the headroom proxy + the /data PVC.
nodeSelector:
kubernetes.io/hostname: kubernets
# Dashboard is read-only over /data and only mutates Gitea (not local
# files), so unprivileged is fine. fsGroup matches the image's USER
# directive (10001) so the RO mount is readable.
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
containers:
- name: dashboard
image: pragent-webhook:optin
imagePullPolicy: Never
workingDir: /app
command: ["python3", "-m", "pilot.dashboard"]
ports:
- name: http
containerPort: 8081
env:
- name: PRAGENT_FEEDBACK_DB
value: /data/feedback.db
- name: PRAGENT_GITEA_API
value: http://gitea-http.gitea.svc.cluster.local:3000
# Dashboard reads DASHBOARD_PORT (not PORT) — verified in
# pilot/dashboard.py:51. Default 8081 if unset.
- name: DASHBOARD_PORT
value: "8081"
# Used by /r/<o>/<n>/edit to PUT updated JSON to the repo's
# contents API. Reuses the same bot token the webhook uses.
- name: PRAGENT_BOT_TOKEN
valueFrom:
secretKeyRef:
name: pragent-webhook
key: PRAGENT_BOT_TOKEN
# Auth cookie value. Add to the pragent-webhook Secret with:
# kubectl patch secret pragent-webhook -n pragent --type=json \
# -p='[{"op":"add","path":"/data/PRAGENT_DASHBOARD_TOKEN","value":"<base64>"}]'
- name: PRAGENT_DASHBOARD_TOKEN
valueFrom:
secretKeyRef:
name: pragent-webhook
key: PRAGENT_DASHBOARD_TOKEN
# /data is read-only — the dashboard doesn't write the SQLite file;
# .pr-review.json mutations go through the Gitea contents API, not
# local fs. RO avoids any chance of two pods racing the same RWO PVC.
volumeMounts:
- name: feedback-data
mountPath: /data
readOnly: true
# No /health route in dashboard.py (returns 404 on unknown paths).
# Probes omitted intentionally — see pilot/dashboard.py:687-721.
# Resources: dashboard is read-heavy + tiny writes. /data RO + no
# subprocess fan-out (no opencode) keeps footprint small.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: feedback-data
persistentVolumeClaim:
claimName: pragent-feedback-data
---
apiVersion: v1
kind: Service
metadata:
name: pragent-dashboard
namespace: pragent
spec:
selector:
app: pragent-dashboard
ports:
- name: http
port: 80
targetPort: http
nodePort: 31540
type: NodePort
# 31540 — auto-allocated at first apply (30082 was already taken by
# habitsnow/habitsnow-proxy). Tailscale / LAN only until a Caddy route is set.