fix(hermes): provision Cassandra diagnostics
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 179
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 179
This commit is contained in:
parent
be2aa52d51
commit
c3ad73bd20
52
services/cassandra/hermes-agent-rbac.yaml
Normal file
52
services/cassandra/hermes-agent-rbac.yaml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# services/cassandra/hermes-agent-rbac.yaml
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: hermes-agent-readonly
|
||||||
|
namespace: cassandra
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources:
|
||||||
|
- configmaps
|
||||||
|
- endpoints
|
||||||
|
- events
|
||||||
|
- persistentvolumeclaims
|
||||||
|
- pods
|
||||||
|
- pods/log
|
||||||
|
- replicationcontrollers
|
||||||
|
- services
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["events.k8s.io"]
|
||||||
|
resources: ["events"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["apps"]
|
||||||
|
resources:
|
||||||
|
- daemonsets
|
||||||
|
- deployments
|
||||||
|
- replicasets
|
||||||
|
- statefulsets
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["batch"]
|
||||||
|
resources:
|
||||||
|
- cronjobs
|
||||||
|
- jobs
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
- apiGroups: ["networking.k8s.io"]
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
- networkpolicies
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: hermes-agent-readonly
|
||||||
|
namespace: cassandra
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: hermes-agent
|
||||||
|
namespace: hermes
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: Role
|
||||||
|
name: hermes-agent-readonly
|
||||||
@ -11,6 +11,7 @@ resources:
|
|||||||
- limitrange.yaml
|
- limitrange.yaml
|
||||||
- configmap.yaml
|
- configmap.yaml
|
||||||
- rbac.yaml
|
- rbac.yaml
|
||||||
|
- hermes-agent-rbac.yaml
|
||||||
- artifacts-pvc.yaml
|
- artifacts-pvc.yaml
|
||||||
- postgres-service.yaml
|
- postgres-service.yaml
|
||||||
- postgres-statefulset.yaml
|
- postgres-statefulset.yaml
|
||||||
|
|||||||
@ -215,9 +215,11 @@ data:
|
|||||||
objective's difficulty warrants it; otherwise synthesize at the original
|
objective's difficulty warrants it; otherwise synthesize at the original
|
||||||
effort.
|
effort.
|
||||||
|
|
||||||
This pod has no Kubernetes RBAC. Do not use it for automated test intake or
|
This pod has Cassandra-scoped, read-only Kubernetes access for workloads,
|
||||||
cluster mutation. Triage belongs at triage.hermes.bstein.dev and changes to
|
logs, events, networking, and rollout diagnosis. It cannot read Secrets,
|
||||||
Atlas are delivered through the titan-iac Git/Flux workflow.
|
exec into pods, or mutate the cluster. Triage belongs at
|
||||||
|
triage.hermes.bstein.dev and all Atlas changes are delivered through the
|
||||||
|
titan-iac Git/Flux workflow.
|
||||||
START-HERE.md: |
|
START-HERE.md: |
|
||||||
# Agent Hermes
|
# Agent Hermes
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ spec:
|
|||||||
ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code
|
ai.bstein.dev/execution: Herdr-supervised Codex and Claude Code
|
||||||
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
|
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
|
||||||
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
|
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
|
||||||
ai.bstein.dev/config-rev: "20260809-browser-runtime-complete-resume"
|
ai.bstein.dev/config-rev: "20260809-cassandra-readonly"
|
||||||
vault.hashicorp.com/agent-inject: "true"
|
vault.hashicorp.com/agent-inject: "true"
|
||||||
vault.hashicorp.com/role: hermes-agent
|
vault.hashicorp.com/role: hermes-agent
|
||||||
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens
|
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens
|
||||||
@ -88,6 +88,7 @@ spec:
|
|||||||
/opt/data/home/.claude \
|
/opt/data/home/.claude \
|
||||||
/opt/data/home/.codex \
|
/opt/data/home/.codex \
|
||||||
/opt/data/home/.config/herdr \
|
/opt/data/home/.config/herdr \
|
||||||
|
/opt/data/home/.kube \
|
||||||
/opt/data/herdr \
|
/opt/data/herdr \
|
||||||
/opt/data/logs \
|
/opt/data/logs \
|
||||||
/opt/data/tools/bin \
|
/opt/data/tools/bin \
|
||||||
@ -172,6 +173,14 @@ spec:
|
|||||||
@anthropic-ai/claude-code@2.1.226
|
@anthropic-ai/claude-code@2.1.226
|
||||||
touch "${tools}/.cli-versions-0.147.0-2.1.226"
|
touch "${tools}/.cli-versions-0.147.0-2.1.226"
|
||||||
fi
|
fi
|
||||||
|
kubectl_version="$("${tools}/bin/kubectl" version --client --output=json 2>/dev/null || true)"
|
||||||
|
case "${kubectl_version}" in *\"gitVersion\":\"v1.33.3\"*) kubectl_ready=1 ;; *) kubectl_ready=0 ;; esac
|
||||||
|
if [ "${kubectl_ready}" != "1" ]; then
|
||||||
|
curl -fsSL -o "${tools}/bin/kubectl.tmp" https://dl.k8s.io/release/v1.33.3/bin/linux/arm64/kubectl
|
||||||
|
printf '%s %s\n' 3d514dbae5dc8c09f773df0ef0f5d449dfad05b3aca5c96b13565f886df345fd "${tools}/bin/kubectl.tmp" | sha256sum -c -
|
||||||
|
chmod 0755 "${tools}/bin/kubectl.tmp"
|
||||||
|
mv "${tools}/bin/kubectl.tmp" "${tools}/bin/kubectl"
|
||||||
|
fi
|
||||||
securityContext:
|
securityContext:
|
||||||
allowPrivilegeEscalation: false
|
allowPrivilegeEscalation: false
|
||||||
runAsUser: 10000
|
runAsUser: 10000
|
||||||
@ -276,6 +285,7 @@ spec:
|
|||||||
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
|
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
|
||||||
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
|
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
|
||||||
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
|
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
|
||||||
|
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
|
||||||
- {name: PATH, value: /opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/opt/hermes/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
|
- {name: PATH, value: /opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/opt/hermes/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
|
||||||
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
||||||
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
|
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
|
||||||
@ -292,6 +302,7 @@ spec:
|
|||||||
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||||
- {name: coordinator, mountPath: /opt/data/home/.local/bin/herdr-dispatch, subPath: herdr_dispatch.py, readOnly: true}
|
- {name: coordinator, mountPath: /opt/data/home/.local/bin/herdr-dispatch, subPath: herdr_dispatch.py, readOnly: true}
|
||||||
|
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
|
||||||
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
||||||
startupProbe:
|
startupProbe:
|
||||||
tcpSocket: {port: api}
|
tcpSocket: {port: api}
|
||||||
@ -480,6 +491,7 @@ spec:
|
|||||||
--env HERMES_HOME=/opt/data \
|
--env HERMES_HOME=/opt/data \
|
||||||
--env HERMES_AUTH_FILE=/shared-auth/auth.json \
|
--env HERMES_AUTH_FILE=/shared-auth/auth.json \
|
||||||
--env HOME=/opt/data/home \
|
--env HOME=/opt/data/home \
|
||||||
|
--env KUBECONFIG=/opt/data/home/.kube/config \
|
||||||
--env PYTHONPATH=/opt/hermes \
|
--env PYTHONPATH=/opt/hermes \
|
||||||
--env PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright \
|
--env PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright \
|
||||||
--env AGENT_BROWSER_EXECUTABLE_PATH=/opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell \
|
--env AGENT_BROWSER_EXECUTABLE_PATH=/opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell \
|
||||||
@ -519,6 +531,7 @@ spec:
|
|||||||
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
|
- {name: CLAUDE_CONFIG_DIR, value: /opt/data/home/.claude}
|
||||||
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
|
- {name: HERDR_CONFIG_PATH, value: /opt/data/home/.config/herdr/config.toml}
|
||||||
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
|
- {name: HERDR_SOCKET_PATH, value: /opt/data/herdr/herdr.sock}
|
||||||
|
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
|
||||||
- {name: PYTHONPATH, value: /opt/hermes}
|
- {name: PYTHONPATH, value: /opt/hermes}
|
||||||
- {name: PATH, value: /opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
|
- {name: PATH, value: /opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
|
||||||
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
|
||||||
@ -536,6 +549,7 @@ spec:
|
|||||||
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
|
||||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||||
- {name: coordinator, mountPath: /opt/data/home/.local/bin/herdr-dispatch, subPath: herdr_dispatch.py, readOnly: true}
|
- {name: coordinator, mountPath: /opt/data/home/.local/bin/herdr-dispatch, subPath: herdr_dispatch.py, readOnly: true}
|
||||||
|
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
|
||||||
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
|
||||||
resources:
|
resources:
|
||||||
requests: {cpu: 50m, memory: 128Mi}
|
requests: {cpu: 50m, memory: 128Mi}
|
||||||
@ -577,6 +591,10 @@ spec:
|
|||||||
configMap:
|
configMap:
|
||||||
name: hermes-coordinator
|
name: hermes-coordinator
|
||||||
defaultMode: 0555
|
defaultMode: 0555
|
||||||
|
- name: kubeconfig
|
||||||
|
configMap:
|
||||||
|
name: hermes-agent-kubeconfig
|
||||||
|
defaultMode: 0444
|
||||||
- name: auth-patch
|
- name: auth-patch
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
- name: auto-router-plugin
|
- name: auto-router-plugin
|
||||||
|
|||||||
19
services/hermes/agent-kubeconfig.yaml
Normal file
19
services/hermes/agent-kubeconfig.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# services/hermes/agent-kubeconfig.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Config
|
||||||
|
clusters:
|
||||||
|
- name: atlas
|
||||||
|
cluster:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||||
|
users:
|
||||||
|
- name: hermes-agent
|
||||||
|
user:
|
||||||
|
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
|
contexts:
|
||||||
|
- name: cassandra-readonly
|
||||||
|
context:
|
||||||
|
cluster: atlas
|
||||||
|
user: hermes-agent
|
||||||
|
namespace: cassandra
|
||||||
|
current-context: cassandra-readonly
|
||||||
@ -52,6 +52,12 @@ configMapGenerator:
|
|||||||
- patch_hermes_auth.py=scripts/patch_hermes_auth.py
|
- patch_hermes_auth.py=scripts/patch_hermes_auth.py
|
||||||
options:
|
options:
|
||||||
disableNameSuffixHash: true
|
disableNameSuffixHash: true
|
||||||
|
- name: hermes-agent-kubeconfig
|
||||||
|
namespace: hermes
|
||||||
|
files:
|
||||||
|
- config=agent-kubeconfig.yaml
|
||||||
|
options:
|
||||||
|
disableNameSuffixHash: true
|
||||||
- name: hermes-auto-router-plugin
|
- name: hermes-auto-router-plugin
|
||||||
namespace: hermes
|
namespace: hermes
|
||||||
files:
|
files:
|
||||||
|
|||||||
@ -108,6 +108,20 @@ spec:
|
|||||||
app: hermes-model-gate
|
app: hermes-model-gate
|
||||||
ports:
|
ports:
|
||||||
- {protocol: TCP, port: 8080}
|
- {protocol: TCP, port: 8080}
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 10.43.0.1/32
|
||||||
|
ports:
|
||||||
|
- {protocol: TCP, port: 443}
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.22.11/32
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.22.12/32
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 192.168.22.13/32
|
||||||
|
ports:
|
||||||
|
- {protocol: TCP, port: 6443}
|
||||||
- to:
|
- to:
|
||||||
- namespaceSelector:
|
- namespaceSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|||||||
@ -343,6 +343,53 @@ def test_agent_mounts_auto_router_into_both_hermes_runtimes():
|
|||||||
assert volume["configMap"]["name"] == "hermes-auto-router-plugin"
|
assert volume["configMap"]["name"] == "hermes-auto-router-plugin"
|
||||||
|
|
||||||
|
|
||||||
|
def test_agent_has_cassandra_readonly_kubernetes_context():
|
||||||
|
deployment = yaml.safe_load((HERMES / "agent-deployment.yaml").read_text())
|
||||||
|
pod = deployment["spec"]["template"]["spec"]
|
||||||
|
containers = {container["name"]: container for container in pod["containers"]}
|
||||||
|
|
||||||
|
installer = next(
|
||||||
|
container
|
||||||
|
for container in pod["initContainers"]
|
||||||
|
if container["name"] == "install-agent-tools"
|
||||||
|
)
|
||||||
|
install_command = installer["command"][-1]
|
||||||
|
assert "v1.33.3/bin/linux/arm64/kubectl" in install_command
|
||||||
|
assert "3d514dbae5dc8c09f773df0ef0f5d449" in install_command
|
||||||
|
|
||||||
|
for name in ("hermes", "herdr-server"):
|
||||||
|
env = {item["name"]: item["value"] for item in containers[name]["env"]}
|
||||||
|
assert env["KUBECONFIG"] == "/opt/data/home/.kube/config"
|
||||||
|
mounts = {
|
||||||
|
mount["mountPath"]: mount for mount in containers[name]["volumeMounts"]
|
||||||
|
}
|
||||||
|
assert mounts["/opt/data/home/.kube/config"]["name"] == "kubeconfig"
|
||||||
|
assert mounts["/opt/data/home/.kube/config"]["readOnly"] is True
|
||||||
|
|
||||||
|
config = yaml.safe_load((HERMES / "agent-kubeconfig.yaml").read_text())
|
||||||
|
assert config["current-context"] == "cassandra-readonly"
|
||||||
|
assert config["contexts"][0]["context"]["namespace"] == "cassandra"
|
||||||
|
assert config["users"][0]["user"] == {
|
||||||
|
"tokenFile": "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||||
|
}
|
||||||
|
|
||||||
|
rbac_path = HERMES.parent / "cassandra" / "hermes-agent-rbac.yaml"
|
||||||
|
documents = [document for document in yaml.safe_load_all(rbac_path.read_text()) if document]
|
||||||
|
role = next(document for document in documents if document["kind"] == "Role")
|
||||||
|
binding = next(document for document in documents if document["kind"] == "RoleBinding")
|
||||||
|
assert role["metadata"]["namespace"] == "cassandra"
|
||||||
|
assert binding["metadata"]["namespace"] == "cassandra"
|
||||||
|
assert binding["subjects"] == [
|
||||||
|
{"kind": "ServiceAccount", "name": "hermes-agent", "namespace": "hermes"}
|
||||||
|
]
|
||||||
|
assert "secrets" not in {
|
||||||
|
resource for rule in role["rules"] for resource in rule["resources"]
|
||||||
|
}
|
||||||
|
assert {
|
||||||
|
verb for rule in role["rules"] for verb in rule["verbs"]
|
||||||
|
} == {"get", "list", "watch"}
|
||||||
|
|
||||||
|
|
||||||
def test_agent_reconnect_renders_complete_conversation_history():
|
def test_agent_reconnect_renders_complete_conversation_history():
|
||||||
documents = [
|
documents = [
|
||||||
document
|
document
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user