Skip to content

Commit a895b88

Browse files
stubbiclaude
andcommitted
docs(examples): HermesClusterDefaults singleton recipe
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 902288f commit a895b88

2 files changed

Lines changed: 107 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# `HermesClusterDefaults` — cluster-wide defaults
2+
3+
The defaulting webhook fills `nil` fields on every `HermesInstance` from
4+
the cluster-scoped singleton named `cluster`. Explicit values on the
5+
instance always win — `HermesClusterDefaults` never overrides.
6+
7+
Use this to:
8+
9+
- Centralise the operator's image repository + tag for hermes-agent.
10+
- Mandate IRSA / Workload Identity annotations on every ServiceAccount.
11+
- Mandate a default StorageClass + size for the PVC.
12+
- Mandate observability (`serviceMonitor.enabled=true`) and networking
13+
(`networkPolicy.enabled=true`) without having to repeat them on every
14+
instance.
15+
16+
The CR name **must** be `cluster`. The validating webhook rejects any
17+
other name with `WrongName` reason.
18+
19+
## Apply
20+
21+
```bash
22+
kubectl apply -f clusterdefaults.yaml
23+
```
24+
25+
## Verify
26+
27+
```bash
28+
kubectl get hcd cluster -o jsonpath='{.status.conditions[?(@.type=="Active")]}'
29+
# { "status":"True", "reason":"Applied", ... }
30+
31+
# Apply a minimal HermesInstance that omits image, storage, networking —
32+
# they will all be filled by the defaults.
33+
kubectl create namespace agents
34+
kubectl apply -n agents -f - <<'YAML'
35+
apiVersion: hermes.agent/v1
36+
kind: HermesInstance
37+
metadata:
38+
name: defaulted
39+
spec:
40+
config:
41+
raw: |
42+
logging:
43+
level: info
44+
YAML
45+
46+
kubectl get hi defaulted -n agents -o jsonpath='{.spec.image}'
47+
# {"repository":"ghcr.io/stubbi/hermes-agent","tag":"1.4.2"}
48+
```
49+
50+
## What this defaults
51+
52+
| Spec path | Default |
53+
|---|---|
54+
| `spec.image.repository` | `ghcr.io/stubbi/hermes-agent` |
55+
| `spec.image.tag` | `1.4.2` |
56+
| `spec.image.imagePullSecrets[]` | `[{name: ghcr-pull}]` |
57+
| `spec.storage.persistence.storageClassName` | `gp3` |
58+
| `spec.storage.persistence.size` | `10Gi` |
59+
| `spec.security.serviceAccount.annotations` | `{eks.amazonaws.com/role-arn: arn:aws:iam::...}` |
60+
| `spec.observability.serviceMonitor.enabled` | `true` |
61+
| `spec.networking.networkPolicy.enabled` | `true` |
62+
63+
## Important: ordering
64+
65+
The defaulter runs once per admission, *before* the validator. Defaults
66+
filled from `HermesClusterDefaults` are persisted to etcd as part of the
67+
admitted object. Editing the singleton later does not retroactively
68+
re-default existing objects — only new admissions pick up the new
69+
defaults. This is intentional and matches how `LimitRange` works.
70+
71+
To force-resync, re-apply the affected instances with `kubectl replace`.
72+
73+
## Removing the singleton
74+
75+
```bash
76+
kubectl delete hcd cluster
77+
```
78+
79+
Existing `HermesInstance` resources are unaffected (their fields are
80+
already filled). New ones fall back to the operator's built-in fallback
81+
defaults (`ghcr.io/stubbi/hermes-agent:latest`, 10Gi default StorageClass,
82+
no SA annotations).
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
apiVersion: hermes.agent/v1
2+
kind: HermesClusterDefaults
3+
metadata:
4+
name: cluster
5+
spec:
6+
image:
7+
repository: ghcr.io/stubbi/hermes-agent
8+
tag: "1.4.2"
9+
imagePullSecrets:
10+
- name: ghcr-pull
11+
registry:
12+
pullSecretName: ghcr-pull
13+
storage:
14+
storageClassName: gp3
15+
size: 10Gi
16+
security:
17+
serviceAccount:
18+
annotations:
19+
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/hermes
20+
observability:
21+
serviceMonitor:
22+
enabled: true
23+
networking:
24+
networkPolicy:
25+
enabled: true

0 commit comments

Comments
 (0)