From 0ae16653dd119ab617f57f6cc0a78a4599fbd8ce Mon Sep 17 00:00:00 2001 From: David Grochowski Date: Tue, 28 Jul 2026 10:17:58 -0400 Subject: [PATCH 1/2] feat(chart): add relay pod extension points Amp-Thread-ID: https://ampcode.com/threads/T-019f901d-1917-717b-a490-fec759f1f963 Co-authored-by: Amp Signed-off-by: David Grochowski --- deploy/charts/buzz/Chart.yaml | 4 +- deploy/charts/buzz/README.md | 46 ++++++++ deploy/charts/buzz/templates/deployment.yaml | 21 +++- deploy/charts/buzz/tests/render_test.yaml | 115 +++++++++++++++++++ deploy/charts/buzz/values.schema.json | 27 ++++- deploy/charts/buzz/values.yaml | 15 +++ 6 files changed, 224 insertions(+), 4 deletions(-) diff --git a/deploy/charts/buzz/Chart.yaml b/deploy/charts/buzz/Chart.yaml index 956e085749..9309074895 100644 --- a/deploy/charts/buzz/Chart.yaml +++ b/deploy/charts/buzz/Chart.yaml @@ -7,7 +7,7 @@ description: | PostgreSQL and Redis. Configurable for single-node evaluation (subcharts on) and HA production (external services, existingSecret). type: application -version: 0.1.6 +version: 0.1.7 appVersion: "0.1.0" home: https://github.com/block/buzz sources: @@ -24,7 +24,7 @@ maintainers: annotations: artifacthub.io/changes: | - kind: added - description: Optional READ_DATABASE_URL env (secretKeyRef) enabling relay read-replica routing; absent key preserves prior behavior. + description: Generic init-container, volume, volume-mount, command, and args extension points for the relay Pod. artifacthub.io/license: Apache-2.0 # Optional eval-only subcharts. Production deploys disable both and point diff --git a/deploy/charts/buzz/README.md b/deploy/charts/buzz/README.md index 4cf4b22b24..97af633f60 100644 --- a/deploy/charts/buzz/README.md +++ b/deploy/charts/buzz/README.md @@ -52,6 +52,52 @@ See: The chart fails at `helm install` / `helm template` time with a clear message if any of these are missing or malformed (see `templates/_validate.tpl`). +## Relay Pod extensions + +The chart exposes narrow extension points for init containers, volumes, relay +volume mounts, and image command/argument overrides. For example, an init +container can copy a wrapper binary into a shared volume and make that wrapper +the relay entrypoint: + +```yaml +extraInitContainers: + - name: install-wrapper + image: example.com/wrapper-init:v1 + args: [/opt/wrapper/wrapper] + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + resources: + requests: + cpu: 10m + memory: 16Mi + volumeMounts: + - name: wrapper + mountPath: /opt/wrapper + +extraVolumes: + - name: wrapper + emptyDir: {} + +relay: + command: [/opt/wrapper/wrapper] + args: [/usr/local/bin/buzz-relay] + extraVolumeMounts: + - name: wrapper + mountPath: /opt/wrapper +``` + +These values are raw Kubernetes fragments rendered with `toYaml`, not `tpl`. +The chart does not validate cross-field relationships: extension names must not +collide with chart-owned containers or volumes, mounts must reference existing +volumes, and each init container must define an appropriate security context +and resources. Empty `relay.command` and `relay.args` arrays preserve the image +defaults; non-empty values override its entrypoint and arguments respectively. + ## Device pairing relay The chart can run Buzz's stateless pairing WebSocket relay as an independent diff --git a/deploy/charts/buzz/templates/deployment.yaml b/deploy/charts/buzz/templates/deployment.yaml index f8d67de31d..bf2df4c2c8 100644 --- a/deploy/charts/buzz/templates/deployment.yaml +++ b/deploy/charts/buzz/templates/deployment.yaml @@ -55,13 +55,14 @@ spec: topologySpreadConstraints: {{- toYaml . | nindent 8 }} {{- end }} + {{- if or .Values.minio.enabled .Values.extraInitContainers }} + initContainers: {{- if .Values.minio.enabled }} # Quickstart only: the bundled MinIO bucket is created by a concurrent # init Job (templates/quickstart-minio-init.yaml). The relay's A3 S3 # conformance probe is startup-fatal, so without this gate the relay Pods # CrashLoopBackOff (with growing backoff) until the bucket appears. Block # relay start until the bucket exists — deterministic, no crash-loops. - initContainers: - name: wait-for-bucket image: {{ .Values.minio.mcImage | quote }} securityContext: @@ -90,12 +91,24 @@ spec: done echo "bucket {{ .Values.s3.bucket }} present" {{- end }} + {{- with .Values.extraInitContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} containers: - name: relay image: {{ include "buzz.image" . }} imagePullPolicy: {{ .Values.image.pullPolicy }} securityContext: {{- toYaml .Values.relay.containerSecurityContext | nindent 12 }} + {{- with .Values.relay.command }} + command: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.relay.args }} + args: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - { name: app, containerPort: 3000, protocol: TCP } - { name: health, containerPort: {{ .Values.service.healthPort }}, protocol: TCP } @@ -225,6 +238,9 @@ spec: volumeMounts: - { name: git-repos, mountPath: {{ .Values.persistence.git.mountPath | quote }} } - { name: git-pack-cache, mountPath: {{ .Values.git.packCachePath | quote }} } + {{- with .Values.relay.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} volumes: - name: git-repos @@ -238,3 +254,6 @@ spec: - name: git-pack-cache emptyDir: sizeLimit: {{ .Values.git.packCacheVolumeSize | quote }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/deploy/charts/buzz/tests/render_test.yaml b/deploy/charts/buzz/tests/render_test.yaml index c50a960d26..3e044f5d7c 100644 --- a/deploy/charts/buzz/tests/render_test.yaml +++ b/deploy/charts/buzz/tests/render_test.yaml @@ -165,3 +165,118 @@ tests: - hasDocuments: count: 0 template: templates/pvc-git.yaml + + - it: preserves image defaults when Pod extensions are empty + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + externalRedis.url: redis://h:6379 + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + asserts: + - notExists: + path: spec.template.spec.initContainers + template: templates/deployment.yaml + - notExists: + path: spec.template.spec.containers[0].command + template: templates/deployment.yaml + - notExists: + path: spec.template.spec.containers[0].args + template: templates/deployment.yaml + + - it: appends generic Pod extensions and overrides the relay command + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + externalPostgresql.url: postgres://u:p@h:5432/d + externalRedis.url: redis://h:6379 + s3.endpoint: http://minio:9000 + s3.accessKey: a + s3.secretKey: s + relay.command: + - /opt/wrapper/wrapper + relay.args: + - /usr/local/bin/buzz-relay + relay.extraVolumeMounts: + - name: wrapper + mountPath: /opt/wrapper + extraInitContainers: + - name: install-wrapper + image: example.com/wrapper-init:v1 + args: + - /opt/wrapper/wrapper + env: + - name: LITERAL_TEMPLATE + value: '{{ .Release.Name }}' + securityContext: + runAsNonRoot: true + resources: + requests: + cpu: 10m + memory: 16Mi + volumeMounts: + - name: wrapper + mountPath: /opt/wrapper + extraVolumes: + - name: wrapper + emptyDir: {} + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: install-wrapper + template: templates/deployment.yaml + - equal: + path: spec.template.spec.initContainers[0].securityContext.runAsNonRoot + value: true + template: templates/deployment.yaml + # Extension fragments are deliberately rendered with toYaml, not tpl. + - equal: + path: spec.template.spec.initContainers[0].env[0].value + value: '{{ .Release.Name }}' + template: templates/deployment.yaml + - equal: + path: spec.template.spec.containers[0].command + value: + - /opt/wrapper/wrapper + template: templates/deployment.yaml + - equal: + path: spec.template.spec.containers[0].args + value: + - /usr/local/bin/buzz-relay + template: templates/deployment.yaml + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: wrapper + mountPath: /opt/wrapper + template: templates/deployment.yaml + - contains: + path: spec.template.spec.volumes + content: + name: wrapper + emptyDir: {} + template: templates/deployment.yaml + + - it: appends generic init containers after the bundled MinIO readiness gate + release: + name: rel + set: + relayUrl: wss://buzz.example.com + ownerPubkey: "0000000000000000000000000000000000000000000000000000000000000000" + postgresql.enabled: true + redis.enabled: true + minio.enabled: true + extraInitContainers: + - name: install-wrapper + image: example.com/wrapper-init:v1 + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: wait-for-bucket + template: templates/deployment.yaml + - equal: + path: spec.template.spec.initContainers[1].name + value: install-wrapper + template: templates/deployment.yaml diff --git a/deploy/charts/buzz/values.schema.json b/deploy/charts/buzz/values.schema.json index 203fd9b69b..53bb29bb60 100644 --- a/deploy/charts/buzz/values.schema.json +++ b/deploy/charts/buzz/values.schema.json @@ -72,9 +72,34 @@ "type": "array", "items": { "type": "string" } }, - "ephemeralTtlOverride": { "type": "integer", "minimum": 0 } + "ephemeralTtlOverride": { "type": "integer", "minimum": 0 }, + "command": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional relay container entrypoint override. Empty preserves the image default." + }, + "args": { + "type": "array", + "items": { "type": "string" }, + "description": "Optional relay container arguments override. Empty preserves the image default." + }, + "extraVolumeMounts": { + "type": "array", + "items": { "type": "object" }, + "description": "Raw Kubernetes volumeMount fragments appended to the relay container." + } } }, + "extraInitContainers": { + "type": "array", + "items": { "type": "object" }, + "description": "Raw Kubernetes init-container fragments appended to the relay Pod." + }, + "extraVolumes": { + "type": "array", + "items": { "type": "object" }, + "description": "Raw Kubernetes volume fragments appended to the relay Pod." + }, "service": { "type": "object", "additionalProperties": true, diff --git a/deploy/charts/buzz/values.yaml b/deploy/charts/buzz/values.yaml index 21548f3651..8ac5086e27 100644 --- a/deploy/charts/buzz/values.yaml +++ b/deploy/charts/buzz/values.yaml @@ -185,9 +185,24 @@ relay: readOnlyRootFilesystem: false # git writes need a writable repo path terminationGracePeriodSeconds: 60 + # Optional image entrypoint/arguments overrides. Empty arrays preserve the + # relay image's defaults. Consumers own compatibility with the selected image. + command: [] + args: [] + # Appended to the chart-owned relay mounts. Names must match extraVolumes (or + # another volume supplied by the platform) and must not collide with built-ins. + extraVolumeMounts: [] + extraEnv: [] extraEnvFrom: [] +# ── Pod extensions ────────────────────────────────────────────────────────── +# Raw Kubernetes fragments appended to the relay Pod. They are rendered with +# toYaml, not tpl. Init containers must define their own securityContext and +# resources; names must not collide with chart-owned containers or volumes. +extraInitContainers: [] +extraVolumes: [] + # ── Device pairing relay ───────────────────────────────────────────────────── # Optional, stateless NIP-AB relay. When enabled, the main relay advertises # pairingRelay.url in NIP-11 and Buzz clients use it instead of the legacy From f7d3bb7e36557ee0222a526266e064804af3119e Mon Sep 17 00:00:00 2001 From: David Grochowski Date: Tue, 28 Jul 2026 11:29:21 -0400 Subject: [PATCH 2/2] docs(chart): distinguish pod extensions from manifests Amp-Thread-ID: https://ampcode.com/threads/T-019f901d-1917-717b-a490-fec759f1f963 Co-authored-by: Amp Signed-off-by: David Grochowski --- deploy/charts/buzz/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/deploy/charts/buzz/README.md b/deploy/charts/buzz/README.md index 97af633f60..7e75d81a2e 100644 --- a/deploy/charts/buzz/README.md +++ b/deploy/charts/buzz/README.md @@ -55,9 +55,14 @@ The chart fails at `helm install` / `helm template` time with a clear message if ## Relay Pod extensions The chart exposes narrow extension points for init containers, volumes, relay -volume mounts, and image command/argument overrides. For example, an init -container can copy a wrapper binary into a shared volume and make that wrapper -the relay entrypoint: +volume mounts, and image command/argument overrides. `extraManifests` creates +independent Kubernetes resources but cannot modify the chart-managed relay +Deployment. These extension values insert fields into that Deployment, avoiding +duplication of its environment, probes, security context, secrets, and +chart-owned volumes. + +For example, an init container can copy a wrapper binary into a shared volume +and make that wrapper the relay entrypoint: ```yaml extraInitContainers: