Skip to content

Commit 9a87bce

Browse files
committed
feat: Helm chart and Dockerfile for Kubernetes deployment
Adds a Helm chart at `deploy/helm/postgresql-trino-gateway/` and a multi-stage Dockerfile at `docker/Dockerfile`. Patterned on the `stackable-ui` chart layout (the closest sibling — this is an app, not a CRD-bearing operator). Chart highlights: - Service is ClusterIP/TCP/5432; PG protocol is L4, so no ingress template (the README documents port-forward, LoadBalancer, and L4-aware ingress as the three exposure paths). - Probes are TCP socket against the listening port — the gateway has no HTTP health endpoint. - Listener TLS is sourced from either a Stackable SecretClass (`tls.secretClass`, mounted via secret-operator ephemeral volume) or a pre-existing `kubernetes.io/tls` Secret (`tls.existingSecret`). Setting both is rejected at template time. - Five `fail` pre-checks reject misconfigurations at template-time rather than as a CrashLoopBackOff: missing trino.host, both TLS sources set, auth.enabled without TLS, auth.enabled=false without allowInsecureListener (the pod IP is non-loopback in K8s), auth.enabled=true with HTTP Trino but no allowPlaintextAuth. - `shutdownDrainTimeoutSecs=25` < `terminationGracePeriodSeconds=30` so the gateway can abort stuck connections before the kubelet sends SIGKILL. - `helm lint` clean; `helm template` rendering verified for the default, secret-operator, and existing-Secret paths. Dockerfile builds the release binary in a vanilla Rust 1.93 image and packages it into UBI 10 minimal with a non-root stackable user (uid 782252253, gid 574654813 — the SDP convention). The builder image is overridable via `--build-arg BUILDER_IMAGE=` so internal SDP CI can swap in `oci.stackable.tech/sdp/ubi9-rust-builder:latest`.
1 parent 1b90a03 commit 9a87bce

10 files changed

Lines changed: 728 additions & 0 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Patterns to ignore when building packages.
2+
# Uses the same syntax as .gitignore.
3+
4+
# Common backups, editor leftovers, OS noise.
5+
.DS_Store
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
*.swp
14+
*.bak
15+
*.tmp
16+
*.orig
17+
*~
18+
# Helm itself.
19+
.project
20+
.idea/
21+
*.tmproj
22+
.vscode/
23+
# Per-chart README sources, if any.
24+
README.md.gotmpl
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
apiVersion: v2
3+
name: postgresql-trino-gateway
4+
description: PostgreSQL wire-protocol gateway in front of Trino, enabling Power BI DirectQuery and other PG-only clients.
5+
type: application
6+
version: 0.0.0-dev
7+
appVersion: "0.0.0-dev"
8+
home: https://stackable.tech
9+
sources:
10+
- https://github.com/stackabletech/postgresql-trino-gateway
11+
maintainers:
12+
- name: Stackable GmbH
13+
email: info@stackable.tech
14+
url: https://stackable.tech
15+
keywords:
16+
- stackable
17+
- trino
18+
- postgresql
19+
- gateway
20+
- powerbi
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# PostgreSQL-Trino Gateway Helm Chart
2+
3+
Deploys the [PostgreSQL-Trino Gateway](https://github.com/stackabletech/postgresql-trino-gateway) on Kubernetes.
4+
5+
## Quick start
6+
7+
```bash
8+
# Minimal install — gateway forwards to Trino at trino.example.com:8443
9+
helm install gw ./deploy/helm/postgresql-trino-gateway \
10+
--set trino.host=trino.example.com \
11+
--set trino.port=8443
12+
13+
# Verify
14+
kubectl get svc -l app.kubernetes.io/name=postgresql-trino-gateway
15+
16+
# Connect via port-forward
17+
kubectl port-forward svc/gw-postgresql-trino-gateway 5432:5432
18+
psql -h 127.0.0.1 -p 5432 -U trino -d memory
19+
```
20+
21+
## Auth and TLS postures
22+
23+
The gateway has two security planes — a listener side (PG clients →
24+
gateway) and a Trino side (gateway → Trino). The chart exposes the same
25+
matrix the binary's `--auth` / TLS flags do.
26+
27+
| Listener auth | Listener TLS | Posture | When to use |
28+
|---|---|---|---|
29+
| off | n/a | `auth.allowInsecureListener=true` (default) | Trusted network, NetworkPolicy gates access, or Trino itself authenticates |
30+
| on | required | `auth.enabled=true` plus `tls.secretClass` or `tls.existingSecret` | Production; cleartext passwords are forwarded to Trino as Basic auth over TLS |
31+
| on | not set | refused at startup | Chart-level pre-check fails |
32+
33+
Listener TLS materials can come from one of:
34+
35+
- `tls.secretClass` — a Stackable SecretClass; the secret-operator
36+
provisions a per-pod cert at `/stackable/listener-tls/{tls.crt,tls.key}`.
37+
- `tls.existingSecret` — a pre-existing `kubernetes.io/tls` Secret
38+
(with `tls.crt` and `tls.key` keys), mounted at the same path.
39+
40+
Set at most one. Setting both is rejected at template time.
41+
42+
### Trino-side TLS
43+
44+
`trino.ssl=true` connects to Trino over HTTPS using the system trust
45+
store. For private CAs, either set `trino.tlsNoVerify=true` (skips
46+
verification entirely) or rebuild the image with the CA injected into
47+
`/etc/pki/ca-trust/source/anchors/`. First-class custom-CA support is
48+
not configured here.
49+
50+
`trino.allowPlaintextAuth=true` is required when `auth.enabled=true`
51+
and `trino.ssl=false` (cleartext password forwarded to Trino over plain
52+
HTTP). Use only with a loopback or otherwise-trusted Trino.
53+
54+
## Connectivity
55+
56+
The Service is `ClusterIP` on TCP/5432 by default. PG protocol is not
57+
HTTP, so a standard ingress controller does not apply. Three ways to
58+
expose the gateway outside the cluster:
59+
60+
1. `kubectl port-forward` — for development or ad-hoc Power BI Desktop.
61+
2. `service.type=LoadBalancer` — gives the Service an external L4 IP.
62+
3. An L4-aware ingress (e.g. nginx with `tcp-services`) or a
63+
`Gateway` resource using a TCPRoute.
64+
65+
## Probes
66+
67+
Both readiness and liveness use TCP socket probes against the listening
68+
port. The gateway has no HTTP health endpoint — it speaks only PG wire
69+
protocol.
70+
71+
## Graceful shutdown
72+
73+
`terminationGracePeriodSeconds` controls how long Kubernetes waits
74+
after SIGTERM before SIGKILL. `shutdownDrainTimeoutSecs` controls how
75+
long the gateway itself waits for in-flight queries to complete before
76+
aborting them. The chart defaults are 30 and 25 respectively, leaving a
77+
5-second safety margin under the kubelet's SIGKILL.
78+
79+
## Values reference
80+
81+
See `values.yaml` for the full list. Notable groups:
82+
83+
- `image.*` — registry, repo, tag, pull policy, pull secrets.
84+
- `service.*` — type, port, target port, annotations.
85+
- `resources.*` — CPU/memory requests and limits.
86+
- `trino.*` — backend host, port, catalog, schema, user, SSL/auth flags.
87+
- `auth.*` — listener-side authentication enable, insecure-listener opt-in.
88+
- `tls.*` — listener TLS source (secretClass or existingSecret).
89+
- `livenessProbe`, `readinessProbe`, `startupProbe` — TCP socket probes.
90+
- `podSecurityContext`, `securityContext` — non-root, read-only root FS,
91+
drop ALL capabilities.
92+
- `maxConnections` — concurrent connection cap.
93+
- `logLevel``RUST_LOG` value (e.g. `postgresql_trino_gateway=debug`).
94+
95+
## Uninstall
96+
97+
```bash
98+
helm uninstall gw
99+
```
100+
101+
The chart creates only namespaced resources (Deployment, Service,
102+
ServiceAccount, optional Secret pull-through). No CRDs, no
103+
cluster-scoped resources.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{ .Chart.Name }} {{ .Chart.AppVersion }} installed as release {{ .Release.Name }}.
2+
3+
Gateway is listening on port {{ .Values.service.port }}, forwarding to:
4+
Trino: {{ if .Values.trino.ssl }}https{{ else }}http{{ end }}://{{ .Values.trino.host }}:{{ .Values.trino.port }}
5+
Catalog: {{ .Values.trino.catalog }}
6+
Schema: {{ .Values.trino.schema }}
7+
8+
Auth posture:
9+
{{- if .Values.auth.enabled }}
10+
- Cleartext password challenge to PG clients, forwarded to Trino as Basic auth.
11+
{{- if eq (include "postgresql-trino-gateway.tlsEnabled" .) "true" }}
12+
- Listener TLS: enabled (plaintext clients refused at handshake).
13+
{{- else }}
14+
- Listener TLS: NOT configured. The gateway will refuse to start.
15+
{{- end }}
16+
{{- else }}
17+
- No client auth. Every connection runs as Trino user "{{ .Values.trino.user }}".
18+
- Restrict access via NetworkPolicy in this posture.
19+
{{- end }}
20+
21+
Connect from inside the cluster:
22+
23+
psql -h {{ include "postgresql-trino-gateway.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local \
24+
-p {{ .Values.service.port }} \
25+
-U {{ .Values.trino.user }} \
26+
-d {{ .Values.trino.catalog }}
27+
28+
Connect from outside (port-forward):
29+
30+
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "postgresql-trino-gateway.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }}
31+
psql -h 127.0.0.1 -p {{ .Values.service.port }} -U {{ .Values.trino.user }} -d {{ .Values.trino.catalog }}
32+
33+
For Power BI Report Server in DirectQuery mode, point the PostgreSQL
34+
connector at the Service host/port above (or expose via an L4
35+
LoadBalancer if Power BI runs outside the cluster).
36+
37+
Enable verbose logging by setting `logLevel=postgresql_trino_gateway=debug`
38+
or `=trace` in values.yaml.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "postgresql-trino-gateway.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
We truncate at 63 chars because some Kubernetes name fields are limited
11+
to this (by the DNS naming spec).
12+
*/}}
13+
{{- define "postgresql-trino-gateway.fullname" -}}
14+
{{- if .Values.fullnameOverride }}
15+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16+
{{- else }}
17+
{{- $name := default .Chart.Name .Values.nameOverride }}
18+
{{- if contains $name .Release.Name }}
19+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
20+
{{- else }}
21+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22+
{{- end }}
23+
{{- end }}
24+
{{- end }}
25+
26+
{{/*
27+
Create chart name and version as used by the chart label.
28+
*/}}
29+
{{- define "postgresql-trino-gateway.chart" -}}
30+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31+
{{- end }}
32+
33+
{{/*
34+
Common labels
35+
*/}}
36+
{{- define "postgresql-trino-gateway.labels" -}}
37+
helm.sh/chart: {{ include "postgresql-trino-gateway.chart" . }}
38+
{{ include "postgresql-trino-gateway.selectorLabels" . }}
39+
{{- if .Chart.AppVersion }}
40+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41+
{{- end }}
42+
app.kubernetes.io/managed-by: {{ .Release.Service }}
43+
stackable.tech/vendor: stackable
44+
{{- end }}
45+
46+
{{/*
47+
Selector labels
48+
*/}}
49+
{{- define "postgresql-trino-gateway.selectorLabels" -}}
50+
app.kubernetes.io/name: {{ include "postgresql-trino-gateway.name" . }}
51+
app.kubernetes.io/instance: {{ .Release.Name }}
52+
app.kubernetes.io/component: gateway
53+
{{- end }}
54+
55+
{{/*
56+
Service account name to use.
57+
*/}}
58+
{{- define "postgresql-trino-gateway.serviceAccountName" -}}
59+
{{- if .Values.serviceAccount.create }}
60+
{{- default (include "postgresql-trino-gateway.fullname" .) .Values.serviceAccount.name }}
61+
{{- else }}
62+
{{- default "default" .Values.serviceAccount.name }}
63+
{{- end }}
64+
{{- end }}
65+
66+
{{/*
67+
Container image reference.
68+
*/}}
69+
{{- define "postgresql-trino-gateway.image" -}}
70+
{{- if .Values.image.registry }}
71+
{{- printf "%s/%s:%s" .Values.image.registry .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) }}
72+
{{- else }}
73+
{{- printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) }}
74+
{{- end }}
75+
{{- end }}
76+
77+
{{/*
78+
True iff a listener TLS source is configured. Used to gate volume,
79+
mount, and CLI-flag rendering on a single decision.
80+
*/}}
81+
{{- define "postgresql-trino-gateway.tlsEnabled" -}}
82+
{{- if or .Values.tls.secretClass .Values.tls.existingSecret -}}true{{- end -}}
83+
{{- end }}

0 commit comments

Comments
 (0)