feat(chart): filestore-chown initContainer — self-heal for UID drift on every pod boot

This commit is contained in:
OdooSky v3
2026-05-06 13:29:17 +02:00
parent 93473919ae
commit 09c21d5e55

View File

@@ -36,31 +36,61 @@ spec:
imagePullSecrets: imagePullSecrets:
- name: {{ . }} - name: {{ . }}
{{- end }} {{- end }}
# fsGroup=101 makes the kubelet recursively chown the filestore # fsGroup=101 makes the kubelet recursively chgrp the filestore
# PVC's root inode to gid=101 on attach. Odoo runs as uid 101 # PVC's root inode to gid=101 on attach. The Odoo user is in
# and writes /var/lib/odoo/sessions on first request; without # group 101 (`odoo`) so it inherits group access to the volume.
# this it crashes "Permission denied: '/var/lib/odoo/sessions'" #
# on the first hit because Longhorn-formatted PVCs come up # NOTE: fsGroup ONLY changes group, never owner UID. If a helper
# owned by root:root. (k3s local-path masked this — its hostPath # job (Tower's checkpoint, spawn-seed, import) ever wrote files
# provisioner left the dir world-writable. Real CSI drivers # as a different UID, fsGroup wouldn't fix that. The
# don't.) supplementalGroups + runAsUser kept default so the # filestore-chown initContainer below is what closes the gap —
# rest of the pod (db-init, addon init containers) keeps the # it's the single source of truth for "files in /var/lib/odoo
# behavior they already had. # are owned by 100:101 with mode g+rwX before Odoo runs."
securityContext: securityContext:
fsGroup: 101 fsGroup: 101
# OnRootMismatch: only recursive-chown when the volume root
# ISN'T already gid=101. Saves O(filestore-size) chmod cost
# on every pod restart for instances with millions of files.
# Defaults work for fresh PVCs (root != 101 → chown once).
fsGroupChangePolicy: OnRootMismatch fsGroupChangePolicy: OnRootMismatch
# Bootstrap initContainers always run; addon initContainers only # Bootstrap initContainers always run; addon initContainers only
# run when there are addons. We always need db-init to ensure the # run when there are addons. We always need filestore-chown +
# tenant DB exists + base module is initialized before Odoo's # db-init to ensure the filestore is writable + the tenant DB
# main process starts serving — without this the operator would # exists + base module is initialized before Odoo's main process
# have to click through Odoo's setup wizard manually for every # starts serving — without these the operator would either hit
# new instance, which is exactly the SaaS UX v3 sets out to # Permission denied on first attachment write (filestore-chown
# eliminate. # fixes that) or have to click through Odoo's setup wizard
# manually for every new instance (db-init fixes that).
initContainers: initContainers:
# filestore-chown: load-bearing self-heal. Runs as root, chowns
# /var/lib/odoo to 100:101 (the Odoo runtime user inside the
# official Odoo image: uid 100, gid 101) and chmods g+rwX.
# Idempotent — runs on every pod boot.
#
# Why this exists: Tower's clone helpers (checkpoint Job,
# spawn-seed Job, import Job) used to chown to 101:101 (uid
# 101 doesn't even exist as a user — that's the gid). Files
# ended up owned by a non-existent UID, group=odoo. mode 0755
# gave the running Odoo (uid 100) only group r-x — install
# operations failed with "Permission denied: mkdir
# /var/lib/odoo/filestore/<db>/<hash-prefix>" inside Odoo's
# mail.data load. This initContainer fixes ownership-drift
# regardless of what wrote the files. Defence in depth.
- name: filestore-chown
image: {{ include "instance.odooImage" . | quote }}
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args:
- |
set -eu
# uid 100 = odoo, gid 101 = odoo group (per the Odoo
# base image's /etc/passwd + /etc/group). Hardcoded
# because the chart targets that specific image.
chown -R 100:101 /var/lib/odoo
chmod -R u+rwX,g+rwX /var/lib/odoo
echo "filestore ownership: $(stat -c '%u:%g %a' /var/lib/odoo)"
securityContext:
runAsUser: 0
runAsGroup: 0
volumeMounts:
- name: filestore
mountPath: /var/lib/odoo
# db-init: idempotent on every pod boot. # db-init: idempotent on every pod boot.
# 1. createdb if missing (PG-level) # 1. createdb if missing (PG-level)
# 2. odoo -i base --stop-after-init to install base module # 2. odoo -i base --stop-after-init to install base module