|
| 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). |
0 commit comments