feat: Implement Phase 2 dashboard for K8s agent system

Lightweight Go-based dashboard for Raspberry Pi cluster:

Backend:
- chi router with REST API
- Embedded static file serving
- JSON file-based state storage
- Health checks and CORS support

Frontend:
- Responsive dark theme UI
- Status view with nodes, alerts, ArgoCD apps
- Pending actions with approve/reject
- Action history and audit trail
- Workflow listing and manual triggers

Deployment:
- Multi-stage Dockerfile (small Alpine image)
- Kubernetes manifests with Pi 3 tolerations
- Resource limits: 32-64Mi memory, 10-100m CPU
- ArgoCD application manifest
- Kustomize configuration

API endpoints:
- GET /api/status - Cluster status
- GET/POST /api/pending - Action management
- GET /api/history - Action audit trail
- GET/POST /api/workflows - Workflow management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OpenCode Test
2025-12-26 11:34:36 -08:00
parent a80f714fc2
commit 5646508adb
18 changed files with 1712 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: k8s-agent-dashboard
namespace: argocd
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/part-of: k8s-agent-system
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
# Update this to your GitOps repo
repoURL: https://gitea.example.com/user/gitops-repo.git
targetRevision: HEAD
path: apps/k8s-agent-dashboard
destination:
server: https://kubernetes.default.svc
namespace: k8s-agent
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# Health checks
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas

View File

@@ -0,0 +1,90 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-agent-dashboard
namespace: k8s-agent
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/component: dashboard
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: k8s-agent-dashboard
template:
metadata:
labels:
app.kubernetes.io/name: k8s-agent-dashboard
spec:
# Target Pi 3 node (lightweight workload)
tolerations:
- key: "node-type"
operator: "Equal"
value: "pi3"
effect: "NoSchedule"
nodeSelector:
kubernetes.io/arch: arm64
# Security context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: dashboard
image: ghcr.io/will/k8s-agent-dashboard:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
args:
- "--port"
- "8080"
- "--data"
- "/data"
# Resource limits for Pi 3 (1GB RAM)
resources:
requests:
memory: "32Mi"
cpu: "10m"
limits:
memory: "64Mi"
cpu: "100m"
# Health checks
livenessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 5
periodSeconds: 30
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
# Volume mount for persistent data
volumeMounts:
- name: data
mountPath: /data
# Security
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumes:
- name: data
persistentVolumeClaim:
claimName: k8s-agent-dashboard-data

View File

@@ -0,0 +1,29 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: k8s-agent-dashboard
namespace: k8s-agent
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/component: dashboard
annotations:
# Adjust annotations based on your ingress controller
# nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx # or traefik, etc.
rules:
- host: k8s-agent.local # Adjust to your domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: k8s-agent-dashboard
port:
name: http
# Uncomment for TLS
# tls:
# - hosts:
# - k8s-agent.local
# secretName: k8s-agent-dashboard-tls

View File

@@ -0,0 +1,19 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: k8s-agent
resources:
- namespace.yaml
- pvc.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
commonLabels:
app.kubernetes.io/part-of: k8s-agent-system
app.kubernetes.io/managed-by: argocd
images:
- name: ghcr.io/will/k8s-agent-dashboard
newTag: latest

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: k8s-agent
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/part-of: k8s-agent-system

16
dashboard/deploy/pvc.yaml Normal file
View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: k8s-agent-dashboard-data
namespace: k8s-agent
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/component: dashboard
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
# Adjust storageClassName based on your cluster
# storageClassName: local-path

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: k8s-agent-dashboard
namespace: k8s-agent
labels:
app.kubernetes.io/name: k8s-agent-dashboard
app.kubernetes.io/component: dashboard
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
selector:
app.kubernetes.io/name: k8s-agent-dashboard