From bd1e68bd4a29d494027f6e0e8191eecffe7c6a51 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Tue, 13 Oct 2026 10:08:52 +0000 Subject: [PATCH 1/7] Add envoy proxy for per-beamline ingress routing Ported from DiamondLightSource/diamond-tiled#2. The Kyverno cluster policy disallows regex ingress paths, so "/" routes to an envoy Service that matches beamline names in the path and forwards to each beamline's own tiled Service and Deployment. Paths move from helm/tiled to Charts/tiled. The TILED_SINGLE_USER_API_KEY env var is kept, as optional, rather than removed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CCtPFejfd3WP589miPqaL3 --- Charts/tiled/templates/NOTES.txt | 4 +- Charts/tiled/templates/deployment.yaml | 67 +++++++++++--------- Charts/tiled/templates/envoy-configmap.yaml | 65 +++++++++++++++++++ Charts/tiled/templates/envoy-deployment.yaml | 61 ++++++++++++++++++ Charts/tiled/templates/envoy-service.yaml | 19 ++++++ Charts/tiled/templates/ingress.yaml | 16 ++--- Charts/tiled/templates/service.yaml | 15 +++-- Charts/tiled/values.yaml | 50 ++++++++++++++- 8 files changed, 249 insertions(+), 48 deletions(-) create mode 100644 Charts/tiled/templates/envoy-configmap.yaml create mode 100644 Charts/tiled/templates/envoy-deployment.yaml create mode 100644 Charts/tiled/templates/envoy-service.yaml diff --git a/Charts/tiled/templates/NOTES.txt b/Charts/tiled/templates/NOTES.txt index 2560662..3d8bef3 100644 --- a/Charts/tiled/templates/NOTES.txt +++ b/Charts/tiled/templates/NOTES.txt @@ -1,8 +1,8 @@ 1. Get the application URL by running these commands: {{- if .Values.ingress.enabled }} {{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- range $.Values.beamlines }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}/api/v1/search/{{ .name }} {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} diff --git a/Charts/tiled/templates/deployment.yaml b/Charts/tiled/templates/deployment.yaml index 7021db6..5298602 100644 --- a/Charts/tiled/templates/deployment.yaml +++ b/Charts/tiled/templates/deployment.yaml @@ -1,67 +1,73 @@ +{{- range .Values.beamlines }} +--- apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "tiled.fullname" . }} + name: {{ include "tiled.fullname" $ }}-{{ .name }} labels: - {{- include "tiled.labels" . | nindent 4 }} + {{- include "tiled.labels" $ | nindent 4 }} + tiled.io/beamline: {{ .name }} spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} + {{- if not $.Values.autoscaling.enabled }} + replicas: {{ $.Values.replicaCount }} {{- end }} selector: matchLabels: - {{- include "tiled.selectorLabels" . | nindent 6 }} + {{- include "tiled.selectorLabels" $ | nindent 6 }} + tiled.io/beamline: {{ .name }} template: metadata: annotations: - {{- if .Values.rollOnConfigChange }} - checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- if $.Values.rollOnConfigChange }} + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") $ | sha256sum }} {{- end }} - {{- with .Values.podAnnotations }} + {{- with $.Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "tiled.labels" . | nindent 8 }} - {{- with .Values.podLabels }} + {{- include "tiled.labels" $ | nindent 8 }} + tiled.io/beamline: {{ .name }} + {{- with $.Values.podLabels }} {{- toYaml . | nindent 8 }} {{- end }} spec: - {{- with .Values.imagePullSecrets }} + {{- with $.Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "tiled.serviceAccountName" . }} + serviceAccountName: {{ include "tiled.serviceAccountName" $ }} + {{- $beamlineSC := .securityContext | default dict }} securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- toYaml (merge (pick $beamlineSC "supplementalGroups") $.Values.podSecurityContext) | nindent 8 }} containers: - - name: {{ .Chart.Name }} + - name: {{ $.Chart.Name }} securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- with .Values.command }} + {{- toYaml (merge (omit $beamlineSC "supplementalGroups") $.Values.securityContext) | nindent 12 }} + image: "{{ $.Values.image.repository }}:{{ $.Values.image.tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ $.Values.image.pullPolicy }} + {{- with $.Values.command }} command: {{- toYaml . | nindent 12 }} {{- end }} - {{- with .Values.args }} + {{- with $.Values.args }} args: {{- toYaml . | nindent 12 }} {{- end }} ports: - name: http - containerPort: {{ .Values.service.port }} + containerPort: {{ .port | default $.Values.service.port }} protocol: TCP livenessProbe: - {{- toYaml .Values.livenessProbe | nindent 12 }} + {{- toYaml (merge (.livenessProbe | default dict) $.Values.livenessProbe) | nindent 12 }} readinessProbe: - {{- toYaml .Values.readinessProbe | nindent 12 }} + {{- toYaml (merge (.readinessProbe | default dict) $.Values.readinessProbe) | nindent 12 }} resources: - {{- toYaml .Values.resources | nindent 12 }} + {{- toYaml $.Values.resources | nindent 12 }} volumeMounts: - {{- with .Values.volumeMounts }} + {{- with $.Values.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} - {{- with .Values.extraEnvVars }} + {{- with $.Values.extraEnvVars }} env: {{- toYaml . | nindent 12 }} {{- end }} @@ -70,19 +76,20 @@ spec: projected: sources: - configMap: - name: {{ include "tiled.fullname" . }}-config - {{- with .Values.volumes }} + name: {{ include "tiled.fullname" $ }}-config + {{- with $.Values.volumes }} {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.nodeSelector }} + {{- with $.Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.affinity }} + {{- with $.Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} - {{- with .Values.tolerations }} + {{- with $.Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} +{{- end }} diff --git a/Charts/tiled/templates/envoy-configmap.yaml b/Charts/tiled/templates/envoy-configmap.yaml new file mode 100644 index 0000000..2f24d36 --- /dev/null +++ b/Charts/tiled/templates/envoy-configmap.yaml @@ -0,0 +1,65 @@ +{{- if .Values.envoy.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tiled.fullname" . }}-envoy-config + labels: + {{- include "tiled.labels" . | nindent 4 }} + app.kubernetes.io/component: envoy-proxy +data: + envoy.yaml: | + admin: + address: + socket_address: { address: 0.0.0.0, port_value: {{ .Values.envoy.adminPort }} } + static_resources: + listeners: + - name: listener_http + address: + socket_address: { address: 0.0.0.0, port_value: {{ .Values.envoy.listenerPort }} } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: tiled_backend + domains: ["*"] + routes: + {{- range .Values.beamlines }} + {{- if ne .name $.Values.envoy.defaultBeamline }} + - match: + safe_regex: + google_re2: {} + regex: "^/.*/{{ .name }}(/.*)?$" + route: + cluster: {{ .name }}_cluster + {{- end }} + {{- end }} + - match: + prefix: "/" + route: + cluster: {{ .Values.envoy.defaultBeamline }}_cluster + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + {{- range .Values.beamlines }} + - name: {{ .name }}_cluster + connect_timeout: 5s + type: STRICT_DNS + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: {{ .name }}_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: {{ include "tiled.fullname" $ }}-{{ .name }}.{{ $.Release.Namespace }}.svc.cluster.local + port_value: {{ .port | default $.Values.service.port }} + {{- end }} +{{- end }} diff --git a/Charts/tiled/templates/envoy-deployment.yaml b/Charts/tiled/templates/envoy-deployment.yaml new file mode 100644 index 0000000..504a58c --- /dev/null +++ b/Charts/tiled/templates/envoy-deployment.yaml @@ -0,0 +1,61 @@ +{{- if .Values.envoy.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tiled.fullname" . }}-envoy + labels: + {{- include "tiled.labels" . | nindent 4 }} + app.kubernetes.io/component: envoy-proxy +spec: + replicas: {{ .Values.envoy.replicaCount }} + selector: + matchLabels: + {{- include "tiled.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: envoy-proxy + template: + metadata: + labels: + {{- include "tiled.labels" . | nindent 8 }} + app.kubernetes.io/component: envoy-proxy + annotations: + checksum/config: {{ include (print $.Template.BasePath "/envoy-configmap.yaml") . | sha256sum }} + spec: + securityContext: + {{- toYaml .Values.envoy.podSecurityContext | nindent 8 }} + containers: + - name: envoy + image: "{{ .Values.envoy.image.repository }}:{{ .Values.envoy.image.tag }}" + imagePullPolicy: {{ .Values.envoy.image.pullPolicy }} + securityContext: + {{- toYaml .Values.envoy.securityContext | nindent 12 }} + command: + - envoy + args: + - -c + - /etc/envoy/envoy.yaml + ports: + - name: http + containerPort: {{ .Values.envoy.listenerPort }} + protocol: TCP + - name: admin + containerPort: {{ .Values.envoy.adminPort }} + protocol: TCP + readinessProbe: + httpGet: + path: /ready + port: admin + livenessProbe: + httpGet: + path: /ready + port: admin + volumeMounts: + - name: envoy-config + mountPath: /etc/envoy + readOnly: true + resources: + {{- toYaml .Values.envoy.resources | nindent 12 }} + volumes: + - name: envoy-config + configMap: + name: {{ include "tiled.fullname" . }}-envoy-config +{{- end }} diff --git a/Charts/tiled/templates/envoy-service.yaml b/Charts/tiled/templates/envoy-service.yaml new file mode 100644 index 0000000..439db5d --- /dev/null +++ b/Charts/tiled/templates/envoy-service.yaml @@ -0,0 +1,19 @@ +{{- if .Values.envoy.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tiled.fullname" . }}-envoy + labels: + {{- include "tiled.labels" . | nindent 4 }} + app.kubernetes.io/component: envoy-proxy +spec: + type: {{ .Values.envoy.service.type }} + ports: + - port: {{ .Values.envoy.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "tiled.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: envoy-proxy +{{- end }} diff --git a/Charts/tiled/templates/ingress.yaml b/Charts/tiled/templates/ingress.yaml index 64cc215..a78ae3a 100644 --- a/Charts/tiled/templates/ingress.yaml +++ b/Charts/tiled/templates/ingress.yaml @@ -24,20 +24,16 @@ spec: {{- end }} {{- end }} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host | quote }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- with .pathType }} - pathType: {{ . }} - {{- end }} + - path: / + pathType: Prefix backend: service: - name: {{ include "tiled.fullname" $ }} + name: {{ include "tiled.fullname" $ }}-envoy port: - number: {{ $.Values.service.port }} - {{- end }} + number: {{ $.Values.envoy.service.port }} {{- end }} {{- end }} diff --git a/Charts/tiled/templates/service.yaml b/Charts/tiled/templates/service.yaml index 5feb1af..81d09b6 100644 --- a/Charts/tiled/templates/service.yaml +++ b/Charts/tiled/templates/service.yaml @@ -1,15 +1,20 @@ +{{- range .Values.beamlines }} +--- apiVersion: v1 kind: Service metadata: - name: {{ include "tiled.fullname" . }} + name: {{ include "tiled.fullname" $ }}-{{ .name }} labels: - {{- include "tiled.labels" . | nindent 4 }} + {{- include "tiled.labels" $ | nindent 4 }} + tiled.io/beamline: {{ .name }} spec: - type: {{ .Values.service.type }} + type: {{ $.Values.service.type }} ports: - - port: {{ .Values.service.port }} + - port: {{ .port | default $.Values.service.port }} targetPort: http protocol: TCP name: http selector: - {{- include "tiled.selectorLabels" . | nindent 4 }} + {{- include "tiled.selectorLabels" $ | nindent 4 }} + tiled.io/beamline: {{ .name }} +{{- end }} diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index 2ca1612..6d8f8ac 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -2,6 +2,9 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +beamlines: + - name: default + # The tiled server config, rendered into a ConfigMap mounted at /deploy/config. config: # Opt in to the dls-tiled plugins by listing their routers, e.g. the ViSR binning @@ -87,8 +90,12 @@ service: ingress: enabled: false className: "" + # A single "/" path (plain "Prefix" pathType) is forwarded to the envoy + # proxy Service (see .Values.envoy below), which performs the actual + # per-beamline regex routing. Cluster policy (Kyverno) disallows + # "ImplementationSpecific"/regex paths directly on Ingress resources, + # so that routing logic lives in envoy instead of here. annotations: {} - # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local @@ -100,6 +107,45 @@ ingress: # hosts: # - chart-example.local +# Envoy proxy that performs regex-based routing to the per-beamline +# Services, matching requests where the beamline name appears as a path +# segment anywhere in the URL (e.g. /api/v1/search/p51/cm44254/cm44254-1/data) and +# forwarding the complete, unmodified request to that beamline's own +# Service. This exists because the Ingress resource itself can't do regex +# path matching under the cluster's Kyverno policy (see ingress.annotations +# comment above). Any request that doesn't match a beamline's name-in-path +# regex (e.g. the UI at "/") falls through to the defaultBeamline's Service. +envoy: + enabled: true + replicaCount: 1 + image: + repository: envoyproxy/envoy + tag: v1.29-latest + pullPolicy: IfNotPresent + listenerPort: 10000 + adminPort: 9901 + # Name of the entry in .Values.beamlines that catch-all (unmatched) requests + # are routed to. + defaultBeamline: default + service: + type: ClusterIP + port: 8080 + resources: {} + # Run as a fixed non-root user and bypass the image's default + # docker-entrypoint.sh (which chowns /dev/stdout and /dev/stderr while + # dropping from root to a non-root user) via `command: [envoy]` in the + # Deployment. That chown fails under the cluster's restricted pod + # permissions, so envoy is started directly as this user instead. + podSecurityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little @@ -113,6 +159,8 @@ resources: {} # memory: 128Mi # This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ +# Each beamline in .Values.beamlines may set its own livenessProbe/readinessProbe +# to override these defaults (same merge pattern as beamlines[].securityContext). livenessProbe: httpGet: path: /healthz From 28ef386ac2d2aa86cbba837e41c0680ade7c8f4c Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:05:29 +0100 Subject: [PATCH 2/7] add giles suggestions --- Charts/tiled/templates/NOTES.txt | 15 +---- Charts/tiled/templates/_helpers.tpl | 12 ++++ Charts/tiled/templates/deployment.yaml | 13 +++-- Charts/tiled/templates/envoy-configmap.yaml | 42 ++++++++++++-- Charts/tiled/templates/envoy-deployment.yaml | 27 ++++++--- Charts/tiled/templates/envoy-pdb.yaml | 15 +++++ Charts/tiled/templates/hpa.yaml | 22 ++++--- Charts/tiled/templates/ingress.yaml | 4 +- Charts/tiled/templates/service.yaml | 4 +- .../templates/tests/test-connection.yaml | 2 +- Charts/tiled/values.yaml | 57 ++++++++++++++----- 11 files changed, 155 insertions(+), 58 deletions(-) create mode 100644 Charts/tiled/templates/envoy-pdb.yaml diff --git a/Charts/tiled/templates/NOTES.txt b/Charts/tiled/templates/NOTES.txt index 3d8bef3..f3fb01c 100644 --- a/Charts/tiled/templates/NOTES.txt +++ b/Charts/tiled/templates/NOTES.txt @@ -5,18 +5,7 @@ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}/api/v1/search/{{ .name }} {{- end }} {{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tiled.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tiled.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tiled.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tiled.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") +{{- else }} echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "tiled.fullname" . }}-envoy 8080:{{ .Values.envoy.service.port }} {{- end }} diff --git a/Charts/tiled/templates/_helpers.tpl b/Charts/tiled/templates/_helpers.tpl index 2312e52..b3ee1b7 100644 --- a/Charts/tiled/templates/_helpers.tpl +++ b/Charts/tiled/templates/_helpers.tpl @@ -60,3 +60,15 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Name of a beamline's Deployment and Service. Called with (dict "root" $ "beamline" .). +Fails rather than truncates, so two beamlines can never collide on one name. +*/}} +{{- define "tiled.beamlineFullname" -}} +{{- $name := printf "%s-%s" (include "tiled.fullname" .root) .beamline.name }} +{{- if gt (len $name) 63 }} +{{- fail (printf "%q is longer than 63 characters, shorten the release or beamline name" $name) }} +{{- end }} +{{- $name }} +{{- end }} diff --git a/Charts/tiled/templates/deployment.yaml b/Charts/tiled/templates/deployment.yaml index 5298602..f643050 100644 --- a/Charts/tiled/templates/deployment.yaml +++ b/Charts/tiled/templates/deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "tiled.fullname" $ }}-{{ .name }} + name: {{ include "tiled.beamlineFullname" (dict "root" $ "beamline" .) }} labels: {{- include "tiled.labels" $ | nindent 4 }} tiled.io/beamline: {{ .name }} @@ -55,7 +55,7 @@ spec: {{- end }} ports: - name: http - containerPort: {{ .port | default $.Values.service.port }} + containerPort: 8000 protocol: TCP livenessProbe: {{- toYaml (merge (.livenessProbe | default dict) $.Values.livenessProbe) | nindent 12 }} @@ -63,12 +63,15 @@ spec: {{- toYaml (merge (.readinessProbe | default dict) $.Values.readinessProbe) | nindent 12 }} resources: {{- toYaml $.Values.resources | nindent 12 }} + {{- with concat $.Values.volumeMounts (.volumeMounts | default list) }} volumeMounts: - {{- with $.Values.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} - {{- with $.Values.extraEnvVars }} env: + # Lets the tiled config refer to the beamline, e.g. a tree at /$TILED_BEAMLINE + - name: TILED_BEAMLINE + value: {{ .name | quote }} + {{- with concat $.Values.extraEnvVars (.extraEnvVars | default list) }} {{- toYaml . | nindent 12 }} {{- end }} volumes: @@ -77,7 +80,7 @@ spec: sources: - configMap: name: {{ include "tiled.fullname" $ }}-config - {{- with $.Values.volumes }} + {{- with concat $.Values.volumes (.volumes | default list) }} {{- toYaml . | nindent 8 }} {{- end }} {{- with $.Values.nodeSelector }} diff --git a/Charts/tiled/templates/envoy-configmap.yaml b/Charts/tiled/templates/envoy-configmap.yaml index 2f24d36..924b44a 100644 --- a/Charts/tiled/templates/envoy-configmap.yaml +++ b/Charts/tiled/templates/envoy-configmap.yaml @@ -1,4 +1,10 @@ {{- if .Values.envoy.enabled }} +{{- /* A bad config stops envoy starting and takes the whole ingress down, so fail the render instead */}} +{{- $names := list }} +{{- range .Values.beamlines }}{{ $names = append $names .name }}{{ end }} +{{- if not (has .Values.envoy.defaultBeamline $names) }} +{{- fail (printf "envoy.defaultBeamline %q must be one of beamlines %v" .Values.envoy.defaultBeamline $names) }} +{{- end }} apiVersion: v1 kind: ConfigMap metadata: @@ -8,9 +14,17 @@ metadata: app.kubernetes.io/component: envoy-proxy data: envoy.yaml: | + # Only reachable from inside the pod (kubectl exec), so other pods can't + # e.g. POST /quitquitquit admin: address: - socket_address: { address: 0.0.0.0, port_value: {{ .Values.envoy.adminPort }} } + socket_address: { address: 127.0.0.1, port_value: {{ .Values.envoy.adminPort }} } + # The beamline regexes list every tiled endpoint, which is over RE2's default limit + layered_runtime: + layers: + - name: static + static_layer: + re2.max_program_size.error_level: 1000 static_resources: listeners: - name: listener_http @@ -22,26 +36,44 @@ data: typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http + normalize_path: true + merge_slashes: true + # For the websockets of /api/v1/stream/single/... + upgrade_configs: + - upgrade_type: websocket + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog route_config: name: local_route virtual_hosts: - name: tiled_backend domains: ["*"] routes: + - match: + path: /envoy-ready + direct_response: + status: 200 {{- range .Values.beamlines }} {{- if ne .name $.Values.envoy.defaultBeamline }} + # The beamline is the first segment of the tiled path, + # e.g. /api/v1/search/{{ .name }}/... or /zarr/v2/{{ .name }}/... - match: safe_regex: - google_re2: {} - regex: "^/.*/{{ .name }}(/.*)?$" + regex: {{ printf "^/(%s)/%s(/.*)?$" (join "|" $.Values.envoy.pathPrefixes) (regexQuoteMeta .name) | quote }} route: cluster: {{ .name }}_cluster + # No timeout: large array/asset reads and slow searches + # can take longer than envoy's 15s default + timeout: 0s {{- end }} {{- end }} - match: prefix: "/" route: cluster: {{ .Values.envoy.defaultBeamline }}_cluster + timeout: 0s http_filters: - name: envoy.filters.http.router typed_config: @@ -59,7 +91,7 @@ data: - endpoint: address: socket_address: - address: {{ include "tiled.fullname" $ }}-{{ .name }}.{{ $.Release.Namespace }}.svc.cluster.local - port_value: {{ .port | default $.Values.service.port }} + address: {{ include "tiled.beamlineFullname" (dict "root" $ "beamline" .) }}.{{ $.Release.Namespace }}.svc.cluster.local + port_value: {{ $.Values.service.port }} {{- end }} {{- end }} diff --git a/Charts/tiled/templates/envoy-deployment.yaml b/Charts/tiled/templates/envoy-deployment.yaml index 504a58c..2a2695c 100644 --- a/Charts/tiled/templates/envoy-deployment.yaml +++ b/Charts/tiled/templates/envoy-deployment.yaml @@ -20,6 +20,10 @@ spec: annotations: checksum/config: {{ include (print $.Template.BasePath "/envoy-configmap.yaml") . | sha256sum }} spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} securityContext: {{- toYaml .Values.envoy.podSecurityContext | nindent 8 }} containers: @@ -37,17 +41,14 @@ spec: - name: http containerPort: {{ .Values.envoy.listenerPort }} protocol: TCP - - name: admin - containerPort: {{ .Values.envoy.adminPort }} - protocol: TCP readinessProbe: httpGet: - path: /ready - port: admin + path: /envoy-ready + port: http livenessProbe: httpGet: - path: /ready - port: admin + path: /envoy-ready + port: http volumeMounts: - name: envoy-config mountPath: /etc/envoy @@ -58,4 +59,16 @@ spec: - name: envoy-config configMap: name: {{ include "tiled.fullname" . }}-envoy-config + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} diff --git a/Charts/tiled/templates/envoy-pdb.yaml b/Charts/tiled/templates/envoy-pdb.yaml new file mode 100644 index 0000000..0592a44 --- /dev/null +++ b/Charts/tiled/templates/envoy-pdb.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.envoy.enabled (gt (int .Values.envoy.replicaCount) 1) }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "tiled.fullname" . }}-envoy + labels: + {{- include "tiled.labels" . | nindent 4 }} + app.kubernetes.io/component: envoy-proxy +spec: + maxUnavailable: 1 + selector: + matchLabels: + {{- include "tiled.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: envoy-proxy +{{- end }} diff --git a/Charts/tiled/templates/hpa.yaml b/Charts/tiled/templates/hpa.yaml index 26e05d5..ed17b86 100644 --- a/Charts/tiled/templates/hpa.yaml +++ b/Charts/tiled/templates/hpa.yaml @@ -1,32 +1,36 @@ {{- if .Values.autoscaling.enabled }} +{{- range .Values.beamlines }} +--- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: {{ include "tiled.fullname" . }} + name: {{ include "tiled.beamlineFullname" (dict "root" $ "beamline" .) }} labels: - {{- include "tiled.labels" . | nindent 4 }} + {{- include "tiled.labels" $ | nindent 4 }} + tiled.io/beamline: {{ .name }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "tiled.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} + name: {{ include "tiled.beamlineFullname" (dict "root" $ "beamline" .) }} + minReplicas: {{ $.Values.autoscaling.minReplicas }} + maxReplicas: {{ $.Values.autoscaling.maxReplicas }} metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- if $.Values.autoscaling.targetCPUUtilizationPercentage }} - type: Resource resource: name: cpu target: type: Utilization - averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + averageUtilization: {{ $.Values.autoscaling.targetCPUUtilizationPercentage }} {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- if $.Values.autoscaling.targetMemoryUtilizationPercentage }} - type: Resource resource: name: memory target: type: Utilization - averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + averageUtilization: {{ $.Values.autoscaling.targetMemoryUtilizationPercentage }} {{- end }} {{- end }} +{{- end }} diff --git a/Charts/tiled/templates/ingress.yaml b/Charts/tiled/templates/ingress.yaml index a78ae3a..cb87c88 100644 --- a/Charts/tiled/templates/ingress.yaml +++ b/Charts/tiled/templates/ingress.yaml @@ -24,8 +24,8 @@ spec: {{- end }} {{- end }} rules: - {{- range $host := .Values.ingress.hosts }} - - host: {{ $host | quote }} + {{- range .Values.ingress.hosts }} + - host: {{ . | quote }} http: paths: - path: / diff --git a/Charts/tiled/templates/service.yaml b/Charts/tiled/templates/service.yaml index 81d09b6..338401f 100644 --- a/Charts/tiled/templates/service.yaml +++ b/Charts/tiled/templates/service.yaml @@ -3,14 +3,14 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "tiled.fullname" $ }}-{{ .name }} + name: {{ include "tiled.beamlineFullname" (dict "root" $ "beamline" .) }} labels: {{- include "tiled.labels" $ | nindent 4 }} tiled.io/beamline: {{ .name }} spec: type: {{ $.Values.service.type }} ports: - - port: {{ .port | default $.Values.service.port }} + - port: {{ $.Values.service.port }} targetPort: http protocol: TCP name: http diff --git a/Charts/tiled/templates/tests/test-connection.yaml b/Charts/tiled/templates/tests/test-connection.yaml index 54d3daa..bb75378 100644 --- a/Charts/tiled/templates/tests/test-connection.yaml +++ b/Charts/tiled/templates/tests/test-connection.yaml @@ -11,5 +11,5 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include "tiled.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "tiled.fullname" . }}-envoy:{{ .Values.envoy.service.port }}/healthz'] restartPolicy: Never diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index 6d8f8ac..915b830 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -2,6 +2,19 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# One tiled Deployment and Service per beamline. Each pod gets TILED_BEAMLINE=, +# and may add its own securityContext (supplementalGroups for POSIX access to the +# beamline data), livenessProbe, readinessProbe, extraEnvVars, volumes and +# volumeMounts, which are merged with or appended to the global values below, e.g. +# - name: p51 +# securityContext: +# supplementalGroups: [12345] +# volumes: +# - name: data +# hostPath: {path: /dls/p51/data} +# volumeMounts: +# - name: data +# mountPath: /dls/p51/data beamlines: - name: default @@ -13,8 +26,15 @@ config: # - dls_tiled.visr:visr_router authentication: allow_anonymous_access: false + # Envoy sends /...//... to that beamline's pod, so each pod serves its + # tree at /. Use a per-beamline catalog in production, e.g. + # uri: postgresql://.../$TILED_BEAMLINE + # To let tokens issued by one pod verify on the others (all /api/v1/auth/* traffic + # goes to envoy.defaultBeamline), share the signing keys, e.g. + # secret_keys: ["$TILED_SECRET_KEY"] + # with TILED_SECRET_KEY added to extraEnvVars from a Secret. trees: - - path: / + - path: /$TILED_BEAMLINE tree: catalog args: uri: "sqlite:////storage/catalog.db" @@ -84,6 +104,7 @@ service: # This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types type: ClusterIP # This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports + # The Service port; tiled itself always listens on 8000 (the image CMD) port: 8000 # This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/ @@ -98,18 +119,15 @@ ingress: annotations: {} # kubernetes.io/tls-acme: "true" hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific + - chart-example.local tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local # Envoy proxy that performs regex-based routing to the per-beamline -# Services, matching requests where the beamline name appears as a path -# segment anywhere in the URL (e.g. /api/v1/search/p51/cm44254/cm44254-1/data) and +# Services, matching requests where the beamline name is the first segment of +# the tiled path (e.g. /api/v1/search/p51/cm44254/cm44254-1/data) and # forwarding the complete, unmodified request to that beamline's own # Service. This exists because the Ingress resource itself can't do regex # path matching under the cluster's Kyverno policy (see ingress.annotations @@ -117,13 +135,26 @@ ingress: # regex (e.g. the UI at "/") falls through to the defaultBeamline's Service. envoy: enabled: true - replicaCount: 1 + # Envoy is in front of every beamline, so run more than one; a + # PodDisruptionBudget is added when this is above 1 + replicaCount: 2 image: repository: envoyproxy/envoy - tag: v1.29-latest + tag: v1.39.4 pullPolicy: IfNotPresent listenerPort: 10000 + # Bound to 127.0.0.1, use with kubectl exec adminPort: 9901 + # The tiled endpoints, before the tiled path, that envoy routes on. Add any + # plugin routers enabled in config.routers (mounted at /custom and /api/v1). + pathPrefixes: + - api/v1/(search|distinct|metadata|register|data_source|revisions) + - api/v1/(array|ragged)/(block|full) + - api/v1/(table/partition|table/full|container/full|node/full) + - api/v1/(awkward/buffers|awkward/full|asset/bytes|asset/manifest) + - api/v1/stream/(single|close) + - (api/v1|custom)/binned + - zarr/v[23] # Name of the entry in .Values.beamlines that catch-all (unmatched) requests # are routed to. defaultBeamline: default @@ -131,11 +162,9 @@ envoy: type: ClusterIP port: 8080 resources: {} - # Run as a fixed non-root user and bypass the image's default - # docker-entrypoint.sh (which chowns /dev/stdout and /dev/stderr while - # dropping from root to a non-root user) via `command: [envoy]` in the - # Deployment. That chown fails under the cluster's restricted pod - # permissions, so envoy is started directly as this user instead. + # Run as a fixed non-root user. The Deployment starts envoy directly with + # `command: [envoy]`, skipping the image's docker-entrypoint.sh, which only + # matters when starting as root. podSecurityContext: runAsNonRoot: true runAsUser: 1000 From 44b4645839edb73aa9dab56529acf1ac71af5aa8 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:15:48 +0100 Subject: [PATCH 3/7] Gate the envoy config checksum on rollOnConfigChange Co-Authored-By: Claude Opus 5.5 --- Charts/tiled/templates/envoy-deployment.yaml | 2 ++ Charts/tiled/values.yaml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Charts/tiled/templates/envoy-deployment.yaml b/Charts/tiled/templates/envoy-deployment.yaml index 2a2695c..2d79ea5 100644 --- a/Charts/tiled/templates/envoy-deployment.yaml +++ b/Charts/tiled/templates/envoy-deployment.yaml @@ -17,8 +17,10 @@ spec: labels: {{- include "tiled.labels" . | nindent 8 }} app.kubernetes.io/component: envoy-proxy + {{- if .Values.rollOnConfigChange }} annotations: checksum/config: {{ include (print $.Template.BasePath "/envoy-configmap.yaml") . | sha256sum }} + {{- end }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index 915b830..fc25187 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -41,8 +41,8 @@ config: writable_storage: "/storage/data" init_if_not_exists: true -# Restart the tiled pods when their ConfigMap changes, by putting a checksum of -# it in a pod annotation. See +# 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 rollOnConfigChange: true From 650050b420f92d268cb229ec4685108a4e6726e7 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:52:34 +0100 Subject: [PATCH 4/7] Share one postgres catalog between the beamlines and mount /dls read only Every beamline pod now uses the catalog at TILED_CATALOG_URI, from the tiled-secrets secret, and mounts all of /dls read only rather than a beamline specific path. Access to each beamline's data is still limited by its supplementalGroups. Co-Authored-By: Claude Opus 5.5 --- Charts/tiled/values.yaml | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index fc25187..ad81fdd 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -9,12 +9,6 @@ # - name: p51 # securityContext: # supplementalGroups: [12345] -# volumes: -# - name: data -# hostPath: {path: /dls/p51/data} -# volumeMounts: -# - name: data -# mountPath: /dls/p51/data beamlines: - name: default @@ -27,8 +21,9 @@ config: authentication: allow_anonymous_access: false # Envoy sends /...//... to that beamline's pod, so each pod serves its - # tree at /. Use a per-beamline catalog in production, e.g. - # uri: postgresql://.../$TILED_BEAMLINE + # tree at /. Every pod uses the same postgres catalog, whose uri + # (postgresql://user:password@host/database) is TILED_CATALOG_URI, from + # extraEnvVars. # To let tokens issued by one pod verify on the others (all /api/v1/auth/* traffic # goes to envoy.defaultBeamline), share the signing keys, e.g. # secret_keys: ["$TILED_SECRET_KEY"] @@ -37,7 +32,7 @@ config: - path: /$TILED_BEAMLINE tree: catalog args: - uri: "sqlite:////storage/catalog.db" + uri: "$TILED_CATALOG_URI" writable_storage: "/storage/data" init_if_not_exists: true @@ -207,8 +202,13 @@ autoscaling: targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 -# Additional volumes on the output Deployment definition. -volumes: [] +# Additional volumes on the output Deployment definition. Every beamline pod +# mounts all of /dls, and reads what its securityContext lets it. +volumes: + - name: dls + hostPath: + path: /dls + type: Directory # Additional volumeMounts on the output Deployment definition. @@ -216,6 +216,9 @@ volumeMounts: - name: config mountPath: "/deploy/config" readOnly: true + - name: dls + mountPath: /dls + readOnly: true nodeSelector: {} @@ -224,9 +227,14 @@ tolerations: [] affinity: {} -# Additional envVars to mount to the pod. The API key secret is optional so the -# chart installs without it; create a secret named tiled-secrets to set the key. +# Additional envVars to mount to the pod, from a secret named tiled-secrets. The +# API key is optional, the catalog uri is required. extraEnvVars: +- name: TILED_CATALOG_URI + valueFrom: + secretKeyRef: + name: tiled-secrets + key: TILED_CATALOG_URI - name: TILED_SINGLE_USER_API_KEY valueFrom: secretKeyRef: From 60c05ae08d8b157a4ad02ba906e65ca19fdeacd8 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:52:47 +0100 Subject: [PATCH 5/7] Revert "Share one postgres catalog between the beamlines and mount /dls read only" This reverts commit 650050b420f92d268cb229ec4685108a4e6726e7. --- Charts/tiled/values.yaml | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index ad81fdd..fc25187 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -9,6 +9,12 @@ # - name: p51 # securityContext: # supplementalGroups: [12345] +# volumes: +# - name: data +# hostPath: {path: /dls/p51/data} +# volumeMounts: +# - name: data +# mountPath: /dls/p51/data beamlines: - name: default @@ -21,9 +27,8 @@ config: authentication: allow_anonymous_access: false # Envoy sends /...//... to that beamline's pod, so each pod serves its - # tree at /. Every pod uses the same postgres catalog, whose uri - # (postgresql://user:password@host/database) is TILED_CATALOG_URI, from - # extraEnvVars. + # tree at /. Use a per-beamline catalog in production, e.g. + # uri: postgresql://.../$TILED_BEAMLINE # To let tokens issued by one pod verify on the others (all /api/v1/auth/* traffic # goes to envoy.defaultBeamline), share the signing keys, e.g. # secret_keys: ["$TILED_SECRET_KEY"] @@ -32,7 +37,7 @@ config: - path: /$TILED_BEAMLINE tree: catalog args: - uri: "$TILED_CATALOG_URI" + uri: "sqlite:////storage/catalog.db" writable_storage: "/storage/data" init_if_not_exists: true @@ -202,13 +207,8 @@ autoscaling: targetCPUUtilizationPercentage: 80 # targetMemoryUtilizationPercentage: 80 -# Additional volumes on the output Deployment definition. Every beamline pod -# mounts all of /dls, and reads what its securityContext lets it. -volumes: - - name: dls - hostPath: - path: /dls - type: Directory +# Additional volumes on the output Deployment definition. +volumes: [] # Additional volumeMounts on the output Deployment definition. @@ -216,9 +216,6 @@ volumeMounts: - name: config mountPath: "/deploy/config" readOnly: true - - name: dls - mountPath: /dls - readOnly: true nodeSelector: {} @@ -227,14 +224,9 @@ tolerations: [] affinity: {} -# Additional envVars to mount to the pod, from a secret named tiled-secrets. The -# API key is optional, the catalog uri is required. +# Additional envVars to mount to the pod. The API key secret is optional so the +# chart installs without it; create a secret named tiled-secrets to set the key. extraEnvVars: -- name: TILED_CATALOG_URI - valueFrom: - secretKeyRef: - name: tiled-secrets - key: TILED_CATALOG_URI - name: TILED_SINGLE_USER_API_KEY valueFrom: secretKeyRef: From f09a1133fe1e31991358e87844cf23b88f5b91d5 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:53:06 +0100 Subject: [PATCH 6/7] Remove the TILED_BEAMLINE env var and serve the tree at / Envoy forwards the path unchanged, so the beamline is the first node under the tree's root rather than the tree's mount point. Co-Authored-By: Claude Opus 5.5 --- Charts/tiled/templates/deployment.yaml | 5 +---- Charts/tiled/values.yaml | 14 ++++++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Charts/tiled/templates/deployment.yaml b/Charts/tiled/templates/deployment.yaml index f643050..82ce563 100644 --- a/Charts/tiled/templates/deployment.yaml +++ b/Charts/tiled/templates/deployment.yaml @@ -67,11 +67,8 @@ spec: volumeMounts: {{- toYaml . | nindent 12 }} {{- end }} - env: - # Lets the tiled config refer to the beamline, e.g. a tree at /$TILED_BEAMLINE - - name: TILED_BEAMLINE - value: {{ .name | quote }} {{- with concat $.Values.extraEnvVars (.extraEnvVars | default list) }} + env: {{- toYaml . | nindent 12 }} {{- end }} volumes: diff --git a/Charts/tiled/values.yaml b/Charts/tiled/values.yaml index fc25187..56104b4 100644 --- a/Charts/tiled/values.yaml +++ b/Charts/tiled/values.yaml @@ -2,10 +2,9 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. -# One tiled Deployment and Service per beamline. Each pod gets TILED_BEAMLINE=, -# and may add its own securityContext (supplementalGroups for POSIX access to the -# beamline data), livenessProbe, readinessProbe, extraEnvVars, volumes and -# volumeMounts, which are merged with or appended to the global values below, e.g. +# One tiled Deployment and Service per beamline. Each may add its own +# securityContext (supplementalGroups for POSIX access to the beamline data), +# livenessProbe, readinessProbe, extraEnvVars, volumes and volumeMounts, which are merged with or appended to the global values below, e.g. # - name: p51 # securityContext: # supplementalGroups: [12345] @@ -26,15 +25,14 @@ config: # - dls_tiled.visr:visr_router authentication: allow_anonymous_access: false - # Envoy sends /...//... to that beamline's pod, so each pod serves its - # tree at /. Use a per-beamline catalog in production, e.g. - # uri: postgresql://.../$TILED_BEAMLINE + # Envoy sends /...//... to that beamline's pod, unchanged, so the + # beamline is the first node under the tree's root. # To let tokens issued by one pod verify on the others (all /api/v1/auth/* traffic # goes to envoy.defaultBeamline), share the signing keys, e.g. # secret_keys: ["$TILED_SECRET_KEY"] # with TILED_SECRET_KEY added to extraEnvVars from a Secret. trees: - - path: /$TILED_BEAMLINE + - path: / tree: catalog args: uri: "sqlite:////storage/catalog.db" From 8061bff78f9f27a798ddd29a9523f54c647251e4 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh <26975142+ZohebShaikh@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:18:06 +0100 Subject: [PATCH 7/7] add docs for postgres --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 828956e..0b8a89f 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,43 @@ Plugins are off by default; see the `config.routers` comment in `Charts/tiled/values.yaml`. The `TILED_SINGLE_USER_API_KEY` environment variable is read from the optional secret `tiled-secrets`. +### Postgres databases + +Tiled uses postgres for the catalog (`tiled`), the authentication database +(`auth`) and tabular data storage (`data`). Until this is done by an init +container in the chart, create and initialize them by hand. + +Create the databases from the postgres pod: + +```bash +kubectl exec -it tiled-testing-postgres-77669bf4c5-t8jmc -n tiled-staging -- psql -U postgres +``` + +```sql +CREATE DATABASE tiled; +CREATE DATABASE auth; +CREATE DATABASE data; +``` + +Then start a throwaway tiled pod, exec into it and create the tables: + +```bash +kubectl run tiled-init -n tiled-staging --image=ghcr.io/bluesky/tiled:0.2.18 \ + --restart=Never --command \ + -- sleep infinity +kubectl exec -it tiled-init -n tiled-staging -- bash +``` + +```bash +export POSTGRESQL_PASSWORD="password" + +tiled catalog init --if-not-exists postgresql+asyncpg://postgres:${POSTGRESQL_PASSWORD}@tiled-testing-postgres:5432/tiled + +tiled admin initialize-database postgresql+asyncpg://postgres:${POSTGRESQL_PASSWORD}@tiled-testing-postgres:5432/auth + +kubectl delete pod tiled-init -n tiled-staging --now +``` + ## Logging tiled's default logging config has no handler for the plugin loggers, so their