Initial chart import from local infrastructure/

This commit is contained in:
git_admin
2026-04-26 17:46:59 +03:00
parent 043505666a
commit a915b31588
10 changed files with 311 additions and 0 deletions

15
Chart.yaml Normal file
View File

@@ -0,0 +1,15 @@
apiVersion: v2
name: instance-template-v3
description: |
OdooSky v3 — single Helm chart for every Odoo instance.
Variation between instances is expressed via values.yaml only.
No chart variants. No string-templating in Tower.
type: application
version: 0.1.0
appVersion: "1.0"
keywords:
- odoo
- erp
- odoosky
maintainers:
- name: OdooSky platform

36
templates/_helpers.tpl Normal file
View File

@@ -0,0 +1,36 @@
{{/*
Per-instance fully-qualified name. Used as the prefix for every K8s
object in the chart so instances in the same namespace can't collide.
*/}}
{{- define "instance.fullname" -}}
{{- .Values.instance.code | trunc 40 | trimSuffix "-" -}}
{{- end -}}
{{/*
Standard labels applied to every K8s object. Keeps `kubectl get -l`
queries by-instance trivial.
*/}}
{{- define "instance.labels" -}}
app.kubernetes.io/name: odoo
app.kubernetes.io/instance: {{ .Values.instance.code | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Values.odoo.tag | quote }}
odoosky.io/component: instance
{{- end -}}
{{/*
Postgres password. Looks up the existing Secret on upgrades; uses
.Values.postgres.password if set; otherwise generates a 32-char
random string on first install. The lookup ensures `helm upgrade`
does NOT silently rotate the password.
*/}}
{{- define "instance.pgPassword" -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace (printf "%s-pg" .Values.instance.code) -}}
{{- if and $existing $existing.data $existing.data.POSTGRES_PASSWORD -}}
{{- index $existing.data "POSTGRES_PASSWORD" | b64dec -}}
{{- else if .Values.postgres.password -}}
{{- .Values.postgres.password -}}
{{- else -}}
{{- randAlphaNum 32 -}}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,21 @@
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: {{ include "instance.fullname" . }}
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
entryPoints:
- {{ .Values.ingress.entryPoint }}
routes:
- match: Host(`{{ .Values.instance.domain }}`)
kind: Rule
services:
- name: {{ include "instance.fullname" . }}-odoo
port: 8069
tls:
# Shared wildcard cert for *.tenants.odoosky.org. The Secret is
# provisioned ONCE per cluster (see infrastructure/cluster/) and
# mirrored into the chart's release namespace by the cluster
# operator. Instances do NOT issue their own certs.
secretName: {{ .Values.ingress.tlsSecret }}

View File

@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "instance.fullname" . }}-odoo
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
replicas: 1
# ReadWriteOnce filestore volume + Odoo's session locks rule out
# rolling deploys with two pods overlapping. Recreate is safe and
# only causes brief downtime.
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Values.instance.code | quote }}
odoosky.io/role: odoo
template:
metadata:
labels:
{{- include "instance.labels" . | nindent 8 }}
odoosky.io/role: odoo
spec:
containers:
- name: odoo
image: "{{ .Values.odoo.image }}:{{ .Values.odoo.tag }}"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8069
env:
- name: HOST
value: {{ include "instance.fullname" . }}-pg
- name: PORT
value: "5432"
- name: USER
valueFrom:
secretKeyRef:
name: {{ include "instance.fullname" . }}-pg
key: POSTGRES_USER
- name: PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "instance.fullname" . }}-pg
key: POSTGRES_PASSWORD
volumeMounts:
- name: filestore
mountPath: /var/lib/odoo
resources:
{{- toYaml .Values.odoo.resources | nindent 12 }}
# /web/login is the most stable health endpoint across Odoo
# 16/17/18/19 — /web/health is 17+. Use login HTTP 200 as
# readiness signal.
readinessProbe:
httpGet:
path: /web/login
port: 8069
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
tcpSocket:
port: 8069
initialDelaySeconds: 60
periodSeconds: 30
volumes:
- name: filestore
persistentVolumeClaim:
claimName: {{ include "instance.fullname" . }}-odoo

11
templates/odoo-pvc.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "instance.fullname" . }}-odoo
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: {{ .Values.odoo.filestoreSize | quote }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "instance.fullname" . }}-odoo
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
selector:
app.kubernetes.io/instance: {{ .Values.instance.code | quote }}
odoosky.io/role: odoo
ports:
- name: http
port: 8069
targetPort: 8069
type: ClusterIP

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "instance.fullname" . }}-pg
labels:
{{- include "instance.labels" . | nindent 4 }}
type: Opaque
stringData:
POSTGRES_USER: {{ .Values.postgres.user | quote }}
POSTGRES_PASSWORD: {{ include "instance.pgPassword" . | quote }}
POSTGRES_DB: {{ .Values.postgres.database | quote }}

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "instance.fullname" . }}-pg
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
selector:
app.kubernetes.io/instance: {{ .Values.instance.code | quote }}
odoosky.io/role: postgres
ports:
- name: pg
port: 5432
targetPort: 5432
# Headless service — required for StatefulSet stable DNS.
clusterIP: None

View File

@@ -0,0 +1,58 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "instance.fullname" . }}-pg
labels:
{{- include "instance.labels" . | nindent 4 }}
spec:
serviceName: {{ include "instance.fullname" . }}-pg
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Values.instance.code | quote }}
odoosky.io/role: postgres
template:
metadata:
labels:
{{- include "instance.labels" . | nindent 8 }}
odoosky.io/role: postgres
spec:
containers:
- name: postgres
image: "{{ .Values.postgres.image }}:{{ .Values.postgres.tag }}"
imagePullPolicy: IfNotPresent
ports:
- name: pg
containerPort: 5432
envFrom:
- secretRef:
name: {{ include "instance.fullname" . }}-pg
env:
# PGDATA in a sub-dir so the mount-point itself isn't the
# data dir — postgres refuses to init when `lost+found`
# exists at the root of the volume.
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
resources:
{{- toYaml .Values.postgres.resources | nindent 12 }}
readinessProbe:
exec:
command: ["sh", "-c", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
exec:
command: ["sh", "-c", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
initialDelaySeconds: 30
periodSeconds: 15
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: {{ .Values.postgres.storage | quote }}

59
values.yaml Normal file
View File

@@ -0,0 +1,59 @@
# 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
odoo:
image: odoo
tag: "18.0"
# Filestore PVC size (Odoo's /var/lib/odoo).
filestoreSize: 10Gi
resources:
requests:
memory: 512Mi
cpu: 250m
limits:
memory: 2Gi
cpu: "2"
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).
password: ""
storage: 10Gi
resources:
requests:
memory: 256Mi
cpu: 100m
limits:
memory: 1Gi
cpu: "1"
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