From 77ddc5cf6276329428f193dabb65438051683f95 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:45:14 +0100 Subject: [PATCH] Add an optional postgres for the catalog, initialized on first use All beamlines share one catalog database, which the postgres image creates on first start. Each tiled pod gets an init container that runs tiled catalog init --if-not-exists, retrying until postgres is up or while another beamline's pod is initializing it. Co-Authored-By: Claude Opus 5.5 --- Charts/tiled/templates/_helpers.tpl | 17 ++++++ Charts/tiled/templates/deployment.yaml | 25 +++++++- Charts/tiled/templates/postgresql.yaml | 82 ++++++++++++++++++++++++++ Charts/tiled/values.yaml | 33 +++++++++++ 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 Charts/tiled/templates/postgresql.yaml diff --git a/Charts/tiled/templates/_helpers.tpl b/Charts/tiled/templates/_helpers.tpl index b3ee1b7..3188c93 100644 --- a/Charts/tiled/templates/_helpers.tpl +++ b/Charts/tiled/templates/_helpers.tpl @@ -72,3 +72,20 @@ Fails rather than truncates, so two beamlines can never collide on one name. {{- end }} {{- $name }} {{- end }} + +{{/* +libpq-style connection env for the bundled postgres, used by the init container +and the tiled config, e.g. postgresql://$PGUSER:$PGPASSWORD@$PGHOST/$PGDATABASE +*/}} +{{- define "tiled.postgresqlEnv" -}} +- name: PGHOST + value: {{ include "tiled.fullname" . }}-postgresql +- name: PGUSER + value: {{ .Values.postgresql.user | quote }} +- name: PGDATABASE + value: {{ .Values.postgresql.database | quote }} +- name: PGPASSWORD + valueFrom: + secretKeyRef: + {{- toYaml .Values.postgresql.passwordSecret | nindent 6 }} +{{- end }} diff --git a/Charts/tiled/templates/deployment.yaml b/Charts/tiled/templates/deployment.yaml index 82ce563..d5e37a8 100644 --- a/Charts/tiled/templates/deployment.yaml +++ b/Charts/tiled/templates/deployment.yaml @@ -39,6 +39,23 @@ spec: {{- $beamlineSC := .securityContext | default dict }} securityContext: {{- toYaml (merge (pick $beamlineSC "supplementalGroups") $.Values.podSecurityContext) | nindent 8 }} + {{- if $.Values.postgresql.enabled }} + # All beamlines share one catalog database, which postgres creates on + # first start. Make its tables, retrying until postgres is up and if + # another beamline's pod is doing the same. A no-op once done. + initContainers: + - name: init-catalog + image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ $.Values.image.pullPolicy }} + securityContext: + {{- toYaml (merge (omit $beamlineSC "supplementalGroups") $.Values.securityContext) | nindent 12 }} + command: + - sh + - -c + - until tiled catalog init --if-not-exists "postgresql://$PGUSER:$PGPASSWORD@$PGHOST/$PGDATABASE"; do sleep 2; done + env: + {{- include "tiled.postgresqlEnv" $ | nindent 12 }} + {{- end }} containers: - name: {{ $.Chart.Name }} securityContext: @@ -67,9 +84,15 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} - {{- with concat $.Values.extraEnvVars (.extraEnvVars | default list) }} + {{- $env := concat $.Values.extraEnvVars (.extraEnvVars | default list) }} + {{- if or $env $.Values.postgresql.enabled }} env: + {{- if $.Values.postgresql.enabled }} + {{- include "tiled.postgresqlEnv" $ | nindent 12 }} + {{- end }} + {{- with $env }} {{- toYaml . | nindent 12 }} + {{- end }} {{- end }} volumes: - name: config diff --git a/Charts/tiled/templates/postgresql.yaml b/Charts/tiled/templates/postgresql.yaml new file mode 100644 index 0000000..6f59bc4 --- /dev/null +++ b/Charts/tiled/templates/postgresql.yaml @@ -0,0 +1,82 @@ +{{- with .Values.postgresql }} +{{- if .enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tiled.fullname" $ }}-postgresql + labels: + {{- include "tiled.labels" $ | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + clusterIP: None + ports: + - port: 5432 + targetPort: postgresql + name: postgresql + selector: + {{- include "tiled.selectorLabels" $ | nindent 4 }} + app.kubernetes.io/component: postgresql +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "tiled.fullname" $ }}-postgresql + labels: + {{- include "tiled.labels" $ | nindent 4 }} + app.kubernetes.io/component: postgresql +spec: + replicas: 1 + serviceName: {{ include "tiled.fullname" $ }}-postgresql + selector: + matchLabels: + {{- include "tiled.selectorLabels" $ | nindent 6 }} + app.kubernetes.io/component: postgresql + template: + metadata: + labels: + {{- include "tiled.labels" $ | nindent 8 }} + app.kubernetes.io/component: postgresql + spec: + securityContext: + {{- toYaml .podSecurityContext | nindent 8 }} + containers: + - name: postgresql + image: "{{ .image.repository }}:{{ .image.tag }}" + imagePullPolicy: {{ .image.pullPolicy }} + securityContext: + {{- toYaml .securityContext | nindent 12 }} + env: + # The image's entrypoint creates this superuser and database on first start + - name: POSTGRES_USER + value: {{ .user | quote }} + - name: POSTGRES_DB + value: {{ .database | quote }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + {{- toYaml .passwordSecret | nindent 18 }} + ports: + - name: postgresql + containerPort: 5432 + readinessProbe: + exec: + command: [pg_isready, -U, {{ .user | quote }}] + resources: + {{- toYaml .resources | nindent 12 }} + volumeMounts: + # postgres 18+ keeps PGDATA in a versioned subdirectory of this + - name: data + mountPath: /var/lib/postgresql + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: [ReadWriteOnce] + {{- with .storageClassName }} + storageClassName: {{ . }} + {{- end }} + resources: + requests: + storage: {{ .storage }} +{{- end }} +{{- end }} diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index 56104b4..967e396 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -39,6 +39,39 @@ config: writable_storage: "/storage/data" init_if_not_exists: true +# A postgres holding one catalog database shared by every beamline. The tiled +# pods initialize it on first start, and get PGHOST, PGUSER, PGPASSWORD and +# PGDATABASE, so point the trees at it with +# uri: "postgresql://$PGUSER:$PGPASSWORD@$PGHOST/$PGDATABASE" +# in place of the default sqlite one. +# The password must be URL-safe, as it is put into the uri as is. +postgresql: + enabled: false + image: + repository: postgres + tag: "18" + pullPolicy: IfNotPresent + user: tiled + database: tiled + # Only read when the data volume is empty; changing it later needs ALTER USER + passwordSecret: + name: tiled-secrets + key: POSTGRES_PASSWORD + storage: 10Gi + storageClassName: "" + resources: {} + # The postgres user of the image, which owns the data directory + podSecurityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + # Restart the tiled and envoy pods when their ConfigMap changes, by putting a # checksum of it in a pod annotation. See # https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments