Files
instance-template-v3/values.yaml
OdooSky v3 96071aec8e feat(chart): pg password via ExternalSecret/OpenBao (A-Chunk 1)
Per-instance Postgres password sourced from OpenBao via External
Secrets Operator. Dual-mode for the migration window:

  - postgres.passwordVaultPath unset → legacy postgres-secret.yaml
    renders with .Values.postgres.password (helm lookup + random
    fallback, bit-exact existing behaviour for live instances).
  - postgres.passwordVaultPath set → postgres-password-externalsecret.yaml
    renders an ExternalSecret that produces the same <release>-pg
    Secret (POSTGRES_USER/PASSWORD/DB) from OpenBao path
    `tenants/<tenant.id>/instances/<instance.code>/pg`.

Exactly one of the two templates ships per instance (mutually
exclusive `if`s on .Values.postgres.passwordVaultPath). The Postgres
StatefulSet envFroms <release>-pg unchanged.

OpenBao policy already grants the per-cluster ESO read on
`v3/data/tenants/<tenantID>/*` (buildEsoPolicy in tower's
openbao_auth_setup.go) — the new instances/<code>/pg subpath is
covered. No policy change required.

A `required` directive on the ExternalSecret asserts tenant.id is
present when passwordVaultPath is set — fails loud at helm template
time if Tower forgot to populate it.

deletionPolicy: Retain on the ExternalSecret. Postgres PGDATA on
disk hashes to the password in the Secret; an accidental ESO
removal must not cascade into the Secret disappearing.

Chart 0.1.6 → 0.1.7. Verified locally: helm template both modes,
helm lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:56:54 +03:00

246 lines
10 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OdooSky v3 instance-template-v3 — default values.
#
# Per-tenant overlay repos override only the keys that differ from these
# defaults. Keep this file as the single source of truth for what an
# Odoo instance looks like by default; do not duplicate defaults in the
# overlay schema or in Tower-Go.
instance:
# Short slug used in K8s object names + as the Helm release name.
# Must be DNS-safe (lowercase, no underscores, <= 40 chars).
code: demo
# The full HTTPS hostname this instance answers on.
# Tenants live under *.tenants.odoosky.org (covered by wildcard DNS A).
domain: demo.tenants.odoosky.org
# Named size — looked up against the `sizes` table below to derive
# CPU / memory limits + Odoo workers. Per-tenant overlays only need
# `instance.size: medium` (etc); they don't have to know the numbers.
size: small
# tenant — owning tenant identity. Currently only required when
# postgres.passwordVaultPath is set (the ESO path needs to know which
# tenant subtree to read from OpenBao). Tower writes `tenant.id` into
# every new overlay; legacy overlays without ESO leave this empty.
tenant:
id: ""
# The named-size table. Single source of truth for what each instance
# tier actually gets. Adjust here, ALL future instances pick up the
# new defaults on next reconcile. Existing instances keep their
# previously-rendered manifests until ArgoCD re-syncs.
#
# Numbers are TOTAL across Odoo + Postgres containers. Odoo/PG split
# is roughly 70/30 (matches v2 production tuning). Workers follow
# the standard `(cpus*2 - 1)` heuristic capped by RAM headroom.
#
# tier total RAM total CPU PVC (fs/db) workers use case
# tiny 1.5 GB 1.0 5/5 GB 0 sandbox/dev only — NOT recommended for production
# small 3 GB 2.0 10/10 GB 1 515 users — RECOMMENDED MINIMUM
# medium 6 GB 4.0 20/20 GB 2 1550 users
# large 12 GB 8.0 50/50 GB 4 50150 users
# custom operator operator operator operator HA / enterprise — wizard collects all fields
sizes:
tiny:
odoo:
requests: { memory: 384Mi, cpu: 200m }
limits: { memory: 1Gi, cpu: 700m }
postgres:
requests: { memory: 128Mi, cpu: 100m }
limits: { memory: 512Mi, cpu: 300m }
storage:
filestore: 5Gi
database: 5Gi
small:
odoo:
requests: { memory: 1Gi, cpu: 500m }
limits: { memory: 2Gi, cpu: "1" }
postgres:
requests: { memory: 512Mi, cpu: 250m }
limits: { memory: 1Gi, cpu: "1" }
storage:
filestore: 10Gi
database: 10Gi
medium:
odoo:
requests: { memory: 2Gi, cpu: "1" }
limits: { memory: 4Gi, cpu: "2" }
postgres:
requests: { memory: 1Gi, cpu: 500m }
limits: { memory: 2Gi, cpu: "2" }
storage:
filestore: 25Gi
database: 25Gi
large:
odoo:
requests: { memory: 4Gi, cpu: "2" }
limits: { memory: 8Gi, cpu: "5" }
postgres:
requests: { memory: 2Gi, cpu: "1" }
limits: { memory: 4Gi, cpu: "3" }
storage:
filestore: 50Gi
database: 100Gi
# imageMirror — REQUIRED for production. Customer instances must pull
# their Odoo + Postgres images from the OdooSky-controlled registry,
# never from Docker Hub directly. Three reasons:
#
# 1. Determinism. Docker Hub's `odoo:18.0` is a rolling tag — every
# pod restart picks up whatever the latest nightly is. The 2026-05-04
# build shipped a SQL regression (now() - INTERVAL '15 minutes'
# string-quoted) that broke every login on every new pod. We pin
# to a specific date-stamped tag we tested.
# 2. Air-gap. Customers running disconnected clusters can't reach
# Docker Hub; they can reach our registry.
# 3. Rate-limit immunity. Docker Hub anonymous pulls cap at ~100/6h
# per IP. A cluster with 50 instances bouncing pods can hit that.
#
# Pinned tags are tracked in the `odoo-tower/odoosky-odoo` Gitea repo
# (versions.yaml). Bumping that repo + this file is the GitOps path
# for Odoo image updates. See the bump policy in odoosky-odoo/README.md.
imageMirror:
registry: "registry.odoosky.cloud/odoosky/docker-mirror"
# pullSecret — name of a Secret in the instance namespace carrying
# registry credentials. Provisioned per-cluster by cluster-platform-v3.
pullSecret: "docker-mirror-pull"
odoo:
image: odoo
# `tag` may be either a MAJOR reference ("18.0") or a literal pinned
# tag ("18.0-20260421"). When it's a major, the chart resolves it via
# `pinnedTags` below — that's the GitOps-clean path. Per-instance
# overlays should carry the major; the chart owns the exact nightly.
# Literal tags here are an escape hatch for staging tests.
tag: "18.0"
# pinnedTags — major → exact upstream nightly we have tested.
# MUST stay in sync with odoo-tower/odoosky-odoo/versions.yaml; that
# repo is the source of truth + bump policy. To bump:
# 1. Test the candidate nightly (see odoosky-odoo README).
# 2. Mirror it: nerdctl pull → tag → push to docker-mirror.
# 3. Update BOTH versions.yaml AND this map in the same PR.
# 4. ArgoCD reconciles; every instance of that major picks up the
# new image on next pod restart, no overlay edits.
#
# Adding a new major: add an entry here. Tower's renderer writes the
# major as `odoo.tag` — adding "17.0" / "19.0" here lights up that
# major across every instance that asks for it.
pinnedTags:
"19.0": "19.0-20260421"
"18.0": "18.0-20260421"
"17.0": "17.0-20260421"
"16.0": "16.0-20250909" # 16.0 reached EOL upstream Sep 2025; this is the final nightly
# Filestore PVC size (Odoo's /var/lib/odoo).
filestoreSize: 10Gi
# Addons selected for this instance. Each entry is a tagged image in
# the cluster-local registry (deployed by cluster-platform-v3 chart).
# The chart renders one initContainer per entry that copies the
# addon's content into a shared volume; the Odoo container reads from
# /mnt/extra-addons/<code>.
#
# Tower owns this list — it commits new entries to the tenant overlay
# AFTER ensuring the corresponding image exists in the destination
# cluster's registry (spawning a build Job from Gitea source if not).
#
# Schema:
# addons:
# - code: odoosky_demo
# version: "18.0.1.0.0"
# source: platform # platform | tenant
# image: registry.odoosky-system.svc.cluster.local:5000/addons/odoosky_demo
#
# Empty list = no extra addons; only Odoo's built-in modules.
addons: []
# Path inside the Odoo container where addons are materialized.
# Odoo's addons_path includes this dir; one folder per addon code.
# Override only if you need a non-standard layout.
addonsMountPath: /mnt/extra-addons
postgres:
image: postgres
tag: "16-alpine"
user: odoo
database: postgres
# If empty, the chart auto-generates on first install and re-reads
# the existing Secret on subsequent upgrades (lookup pattern).
# Ignored when `passwordVaultPath` is set — ESO sources the password
# from OpenBao instead.
password: ""
# passwordVaultPath — when set, the chart renders an ExternalSecret
# pulling the password from OpenBao at
# `tenants/<tenant.id>/instances/<instance.code>/pg` (field
# `password`). The legacy postgres-secret.yaml template is gated
# off; the ExternalSecret produces the same `<release>-pg` Secret
# shape so postgres-statefulset.yaml is unchanged.
#
# When empty (default), the chart falls back to the legacy
# postgres-secret.yaml path using `.password` above. Tower sets
# this field on every new instance starting v0.77; pre-v0.77
# instances stay on the legacy path until migrated by the one-shot
# tool (Chunk 3 of A-OpenBao).
#
# The actual path resolution is hardcoded in the
# postgres-password-externalsecret.yaml template; this field is
# the on/off toggle. Setting it to any non-empty value is
# equivalent ("use ESO for this instance"); the path string itself
# is currently advisory (it's the OpenBao subtree the operator can
# `bao kv list` to find the password).
passwordVaultPath: ""
# externalSecretsStoreRef — ClusterSecretStore name the
# ExternalSecret references. Provisioned by cluster-platform-v3
# under `openbao-platform` by default. Override only on clusters
# that name the store differently.
externalSecretsStoreRef: openbao-platform
storage: 10Gi
backups:
enabled: true
# Cron schedule for the automatic backup job. Default 03:00 UTC
# daily — quiet hour for most timezones, non-business in EU/US/AS.
schedule: "0 3 * * *"
# How many dumps to retain in S3. The backup job prunes older
# objects matching the instance's prefix on every successful run.
retain: 7
# S3-compatible destination. The endpoint + region + bucket are
# NON-secret and live in this committed values.yaml; the AWS
# credentials live in a K8s Secret named by `credentialsSecret`,
# provisioned out-of-band by Tower's bootstrap script (which reads
# from OpenBao). The chart never sees access/secret keys directly.
s3:
endpoint: https://s3.eu-central-1.s4.mega.io
region: eu-central-1
bucket: odoosky-v3-backups
# Per-instance S3 key prefix. Each instance writes under its own
# code/ subdirectory inside the shared bucket.
prefix: "{{ .Values.instance.code }}"
# Name of the K8s Secret holding AWS_ACCESS_KEY_ID +
# AWS_SECRET_ACCESS_KEY. Mounted via envFrom on the backup Job.
credentialsSecret: s3-backup-creds
ingress:
# Traefik entrypoint name (set on the Traefik install in the
# `traefik` namespace).
entryPoint: websecure
# The pre-provisioned wildcard cert for *.tenants.odoosky.org —
# one Certificate resource issued ONCE in the chart's release
# namespace, then every instance's IngressRoute references the
# resulting Secret. Avoids Let's Encrypt's per-week certificate
# issuance ceiling (50/week/registered-domain) as we scale to
# many tenants.
#
# See infrastructure/cluster/wildcard-cert.yaml for the
# provisioning manifest.
tlsSecret: tenants-wildcard-tls
# useTenantsDefaults — when true, the IngressRoute references the
# cluster-level `tenants-default-retry` Middleware (rendered by the
# cluster-platform-v3 chart in the same `tenants` namespace).
# Default true matches the standard platform shape; flip to false
# only when running on a cluster whose Traefik install isn't
# paired with the platform's defaults Middleware set (e.g. an
# externally-managed Traefik, or cluster-platform-v3's
# `traefik.enabled` is false). Audit B.11.
useTenantsDefaults: true