diff --git a/kubernetes/apps/trmnl/renderer.yaml b/kubernetes/apps/trmnl/renderer.yaml index 50dc8f59..0ee7b019 100644 --- a/kubernetes/apps/trmnl/renderer.yaml +++ b/kubernetes/apps/trmnl/renderer.yaml @@ -35,6 +35,13 @@ data: # example value "og_1bit" matches no model on this instance (the # 2-bit OG model here is id 11, name "og_plus"). Getting this wrong # shows up as Terminus rejecting the pushed screen with a 422. + # + # All three auth fields below are placeholders the init container + # replaces — including obtained_at, which must end up a real epoch-MS + # stamp. The 0 here is inert only because it is always overwritten; + # left as 0 the scheduler reads the tokens as ancient and refuses to + # push at all ("expired beyond the refresh window"), with no render + # attempt and no error beyond one startup warning. schedules.template.json: | [ { @@ -145,10 +152,20 @@ spec: - -c - | set -eu + # obtained_at is stamped at write time, in MILLISECONDS. + # It is not cosmetic: the scheduler derives token age from + # it, and a literal 0 in the template made the app treat + # freshly-minted tokens as ancient and refuse to push — + # "BYOS tokens ... expired beyond the refresh window" + # — with no render attempt and no error, just a warning at + # startup. Stamping it here is honest enough: stock + # Terminus disables session expiry and issues effectively + # permanent tokens, so age is not load-bearing. jq --arg at "$BYOS_ACCESS_TOKEN" \ --arg rt "$BYOS_REFRESH_TOKEN" \ '(.[].webhook_format.byosConfig.auth.access_token) = $at - | (.[].webhook_format.byosConfig.auth.refresh_token) = $rt' \ + | (.[].webhook_format.byosConfig.auth.refresh_token) = $rt + | (.[].webhook_format.byosConfig.auth.obtained_at) = (now * 1000 | floor)' \ /template/schedules.template.json > /data/schedules.json echo "wrote /data/schedules.json ($(wc -c < /data/schedules.json) bytes)" env: @@ -208,6 +225,21 @@ spec: value: "http://home-assistant-bgp.home-assistant.svc.cluster.local:8123" - name: TZ value: "Europe/Berlin" + # Chromium will not start without a writable HOME. Running + # as uid 1000 with no matching passwd entry leaves HOME="/", + # which is root-owned 0755, and the browser dies on launch + # with + # chrome_crashpad_handler: --database is required + # because crashpad cannot create its database directory. + # + # Diagnosed on the running pod, not guessed: HOME=/, uid=1000, + # / not writable, /tmp writable. Crash dumps are throwaway, so + # /tmp is the right home for them rather than the PVC. + # + # This is a consequence of OUR non-root securityContext, not + # of the image — so it must move together with runAsUser. + - name: HOME + value: "/tmp" # Dedicated long-lived token, NOT the operator's personal # one, so the renderer's access can be revoked on its own. # It is injected into the headless browser's localStorage, @@ -220,6 +252,16 @@ spec: volumeMounts: - name: data mountPath: /data + # Bounded scratch backing HOME. Chromium writes crashpad + # state and profile data here on every launch, and it now + # launches once a minute for the life of the pod — on the + # container's writable layer that is unbounded growth + # against node disk, with no ephemeral-storage limit to + # catch it. A sizeLimit converts a slow, cluster-wide + # node-disk-pressure problem into a bounded and clearly + # attributable eviction of this pod. + - name: tmp + mountPath: /tmp # HTTP probes here, unlike terminus.yaml's TCP-only ones, because # /health is confirmed rather than assumed: the upstream image's # own Dockerfile declares @@ -260,6 +302,12 @@ spec: # mounted either). Those flags are baked into the app's own # PUPPETEER_ARGS, not something we pass, so this holds as long as # the pinned tag does — worth re-checking on any bump. + # + # Confirmed on the running pod: the capability drop is genuinely + # not what broke the browser. The first deploy DID crash it, but + # on a writable-HOME problem caused by runAsUser — see the HOME + # env above. Do not "fix" a future Chromium launch failure by + # loosening this block before checking HOME and /tmp. securityContext: allowPrivilegeEscalation: false capabilities: @@ -271,6 +319,9 @@ spec: - name: schedule-template configMap: name: trmnl-renderer-schedule + - name: tmp + emptyDir: + sizeLimit: 512Mi --- apiVersion: v1 kind: Service