From be138885dd17272aa4e9f3575fc92d8db0d296d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Hord=C3=A9?= Date: Wed, 12 Mar 2025 13:52:15 +0100 Subject: [PATCH] first commit --- Chart.yaml | 28 + README.md | 274 +++++ templates/NOTES.txt | 5 + templates/_helpers.tpl | 62 ++ templates/analytics/_helpers.tpl | 43 + templates/analytics/deployment.yaml | 179 ++++ templates/analytics/gcloud.secret.yaml | 11 + templates/analytics/service.yaml | 17 + templates/analytics/serviceaccount.yaml | 14 + templates/auth/_helpers.tpl | 43 + templates/auth/deployment.yaml | 181 ++++ templates/auth/service.yaml | 17 + templates/auth/serviceaccount.yaml | 14 + templates/db/_helpers.tpl | 43 + templates/db/deployment.yaml | 173 ++++ templates/db/initdb.config.yaml | 243 +++++ templates/db/migration.config.yaml | 10 + templates/db/service.yaml | 17 + templates/db/serviceaccount.yaml | 14 + templates/db/volume.yaml | 25 + templates/functions/_helpers.tpl | 43 + templates/functions/deployment.yaml | 151 +++ templates/functions/functions.config.yaml | 104 ++ templates/functions/service.yaml | 17 + templates/functions/serviceaccount.yaml | 14 + templates/imgproxy/_helpers.tpl | 43 + templates/imgproxy/deployment.yaml | 91 ++ templates/imgproxy/service.yaml | 17 + templates/imgproxy/serviceaccount.yaml | 14 + templates/imgproxy/volume.yaml | 25 + templates/kong/_helpers.tpl | 43 + templates/kong/config.yaml | 233 +++++ templates/kong/deployment.yaml | 141 +++ templates/kong/ingress.yaml | 57 + templates/kong/service.yaml | 17 + templates/kong/serviceaccount.yaml | 14 + templates/meta/_helpers.tpl | 43 + templates/meta/deployment.yaml | 114 ++ templates/meta/service.yaml | 17 + templates/meta/serviceaccount.yaml | 14 + templates/minio/_helpers.tpl | 43 + templates/minio/deployment.yaml | 100 ++ templates/minio/service.yaml | 17 + templates/minio/serviceaccount.yaml | 14 + templates/minio/volume.yaml | 25 + templates/realtime/_helpers.tpl | 43 + templates/realtime/deployment.yaml | 155 +++ templates/realtime/service.yaml | 17 + templates/realtime/serviceaccount.yaml | 14 + templates/rest/_helpers.tpl | 43 + templates/rest/deployment.yaml | 134 +++ templates/rest/service.yaml | 17 + templates/rest/serviceaccount.yaml | 14 + templates/secrets/_helpers.tpl | 57 + templates/secrets/analytics.yaml | 17 + templates/secrets/dashboard.yaml | 19 + templates/secrets/db.yaml | 18 + templates/secrets/jwt.yaml | 19 + templates/secrets/s3.yaml | 19 + templates/secrets/smtp.yaml | 19 + templates/storage/_helpers.tpl | 43 + templates/storage/deployment.yaml | 245 +++++ templates/storage/service.yaml | 17 + templates/storage/serviceaccount.yaml | 14 + templates/storage/volume.yaml | 25 + templates/studio/_helpers.tpl | 43 + templates/studio/deployment.yaml | 115 ++ templates/studio/service.yaml | 17 + templates/studio/serviceaccount.yaml | 14 + templates/test/analytics.yaml | 26 + templates/test/auth.yaml | 26 + templates/test/db.yaml | 44 + templates/test/imgproxy.yaml | 26 + templates/test/kong.yaml | 40 + templates/test/meta.yaml | 26 + templates/test/minio.yaml | 25 + templates/test/realtime.yaml | 26 + templates/test/rest.yaml | 26 + templates/test/secretrefs.yaml | 79 ++ templates/test/storage.yaml | 26 + templates/test/studio.yaml | 26 + templates/vector/_helpers.tpl | 43 + templates/vector/config.yaml | 255 +++++ templates/vector/deployment.yaml | 108 ++ templates/vector/service.yaml | 17 + templates/vector/serviceaccount.yaml | 41 + values.example.yaml | 158 +++ values.yaml | 1152 +++++++++++++++++++++ 88 files changed, 6132 insertions(+) create mode 100644 Chart.yaml create mode 100644 README.md create mode 100644 templates/NOTES.txt create mode 100644 templates/_helpers.tpl create mode 100644 templates/analytics/_helpers.tpl create mode 100644 templates/analytics/deployment.yaml create mode 100644 templates/analytics/gcloud.secret.yaml create mode 100644 templates/analytics/service.yaml create mode 100644 templates/analytics/serviceaccount.yaml create mode 100644 templates/auth/_helpers.tpl create mode 100644 templates/auth/deployment.yaml create mode 100644 templates/auth/service.yaml create mode 100644 templates/auth/serviceaccount.yaml create mode 100644 templates/db/_helpers.tpl create mode 100644 templates/db/deployment.yaml create mode 100644 templates/db/initdb.config.yaml create mode 100644 templates/db/migration.config.yaml create mode 100644 templates/db/service.yaml create mode 100644 templates/db/serviceaccount.yaml create mode 100644 templates/db/volume.yaml create mode 100644 templates/functions/_helpers.tpl create mode 100644 templates/functions/deployment.yaml create mode 100644 templates/functions/functions.config.yaml create mode 100644 templates/functions/service.yaml create mode 100644 templates/functions/serviceaccount.yaml create mode 100644 templates/imgproxy/_helpers.tpl create mode 100644 templates/imgproxy/deployment.yaml create mode 100644 templates/imgproxy/service.yaml create mode 100644 templates/imgproxy/serviceaccount.yaml create mode 100644 templates/imgproxy/volume.yaml create mode 100644 templates/kong/_helpers.tpl create mode 100644 templates/kong/config.yaml create mode 100644 templates/kong/deployment.yaml create mode 100644 templates/kong/ingress.yaml create mode 100644 templates/kong/service.yaml create mode 100644 templates/kong/serviceaccount.yaml create mode 100644 templates/meta/_helpers.tpl create mode 100644 templates/meta/deployment.yaml create mode 100644 templates/meta/service.yaml create mode 100644 templates/meta/serviceaccount.yaml create mode 100644 templates/minio/_helpers.tpl create mode 100644 templates/minio/deployment.yaml create mode 100644 templates/minio/service.yaml create mode 100644 templates/minio/serviceaccount.yaml create mode 100644 templates/minio/volume.yaml create mode 100644 templates/realtime/_helpers.tpl create mode 100644 templates/realtime/deployment.yaml create mode 100644 templates/realtime/service.yaml create mode 100644 templates/realtime/serviceaccount.yaml create mode 100644 templates/rest/_helpers.tpl create mode 100644 templates/rest/deployment.yaml create mode 100644 templates/rest/service.yaml create mode 100644 templates/rest/serviceaccount.yaml create mode 100644 templates/secrets/_helpers.tpl create mode 100644 templates/secrets/analytics.yaml create mode 100644 templates/secrets/dashboard.yaml create mode 100644 templates/secrets/db.yaml create mode 100644 templates/secrets/jwt.yaml create mode 100644 templates/secrets/s3.yaml create mode 100644 templates/secrets/smtp.yaml create mode 100644 templates/storage/_helpers.tpl create mode 100644 templates/storage/deployment.yaml create mode 100644 templates/storage/service.yaml create mode 100644 templates/storage/serviceaccount.yaml create mode 100644 templates/storage/volume.yaml create mode 100644 templates/studio/_helpers.tpl create mode 100644 templates/studio/deployment.yaml create mode 100644 templates/studio/service.yaml create mode 100644 templates/studio/serviceaccount.yaml create mode 100644 templates/test/analytics.yaml create mode 100644 templates/test/auth.yaml create mode 100644 templates/test/db.yaml create mode 100644 templates/test/imgproxy.yaml create mode 100644 templates/test/kong.yaml create mode 100644 templates/test/meta.yaml create mode 100644 templates/test/minio.yaml create mode 100644 templates/test/realtime.yaml create mode 100644 templates/test/rest.yaml create mode 100644 templates/test/secretrefs.yaml create mode 100644 templates/test/storage.yaml create mode 100644 templates/test/studio.yaml create mode 100644 templates/vector/_helpers.tpl create mode 100644 templates/vector/config.yaml create mode 100644 templates/vector/deployment.yaml create mode 100644 templates/vector/service.yaml create mode 100644 templates/vector/serviceaccount.yaml create mode 100644 values.example.yaml create mode 100644 values.yaml diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..92a3b4d --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,28 @@ +apiVersion: v2 +name: supabase +description: The open source Firebase alternative. + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.3 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +# Not used because too difficult to follow correctly +# appVersion: "1.16.0" + +# A URL to an SVG or PNG image to be used as an icon +icon: https://avatars.githubusercontent.com/u/54469796?s=280&v=4 diff --git a/README.md b/README.md new file mode 100644 index 0000000..28c5966 --- /dev/null +++ b/README.md @@ -0,0 +1,274 @@ +# Supabase for Kubernetes with Helm 3 + +This directory contains the configurations and scripts required to run Supabase inside a Kubernetes cluster. + +## Disclamer + +We use [supabase/postgres](https://hub.docker.com/r/supabase/postgres) to create and manage the Postgres database. This permit you to use replication if needed but you'll have to use the Postgres image provided Supabase or build your own on top of it. You can also choose to use other databases provider like [StackGres](https://stackgres.io/) or [Postgres Operator](https://github.com/zalando/postgres-operator). + +For the moment we are using a root container to permit the installation of the missing `pgjwt` and `wal2json` extension inside the `initdbScripts`. This is considered a security issue, but you can use your own Postgres image instead with the extension already installed to prevent this. We provide an example of `Dockerfile`for this purpose, you can use [ours](https://hub.docker.com/r/tdeoliv/supabase-bitnami-postgres) or build and host it on your own. + +The database configuration we provide is an example using only one master. If you want to go to production, we highly recommend you to use a replicated database. + +## Quickstart + +> For this section we're using Minikube and Docker to create a Kubernetes cluster + +```bash +# Clone Repository +git clone https://github.com/supabase-community/supabase-kubernetes + +# Switch to charts directory +cd supabase-kubernetes/charts/supabase/ + +# Install the chart +helm install demo -f values.example.yaml . +``` + +The first deployment can take some time to complete (especially auth service). You can view the status of the pods using: + +```bash +kubectl get pod -l app.kubernetes.io/instance=demo + +NAME READY STATUS RESTARTS AGE +demo-supabase-analytics-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-auth-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-db-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-functions-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-imgproxy-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-kong-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-meta-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-realtime-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-rest-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +demo-supabase-storage-xxxxxxxxxx-xxxxx 1/1 Running 0 47s +``` + +### Access with Minikube + +Assuming that you have enabled Minikube ingress addon, note down the Minikube IP address: +```shell +minikube ip +``` +Then, add the IP into your `/etc/hosts` file: +```bash +# This will redirect request for example.com to the minikube IP + example.com +``` +Open http://example.com in your browser. + +### Uninstall + +```Bash +# Uninstall Helm chart +helm uninstall demo + +# Backup and/or remove any Persistent Volume Claims that have keep annotation +kubectl delete pvc demo-supabase-storage-pvc +``` + +## Customize + +You should consider to adjust the following values in `values.yaml`: + +- `RELEASE_NAME`: Name used for helm release +- `STUDIO.EXAMPLE.COM` URL to Studio + +If you want to use mail, consider to adjust the following values in `values.yaml`: + +- `SMTP_ADMIN_MAIL` +- `SMTP_HOST` +- `SMTP_PORT` +- `SMTP_SENDER_NAME` + +### JWT Secret + +We encourage you to use your own JWT keys by generating a new Kubernetes secret and reference it in `values.yaml`: + +```yaml +secret: + jwt: + anonKey: + serviceKey: + secret: +``` + +> 32 characters long secret can be generated with `openssl rand 64 | base64` +> You can use the [JWT Tool](https://supabase.com/docs/guides/hosting/overview#api-keys) to generate anon and service keys. + +### SMTP Secret + +Connection credentials for the SMTP mail server will also be provided via Kubernetes secret referenced in `values.yaml`: + +```yaml +secret: + smtp: + username: + password: +``` + +### DB Secret + +DB credentials will also be stored in a Kubernetes secret and referenced in `values.yaml`: + +```yaml +secret: + db: + username: + password: + database: +``` + +The secret can be created with kubectl via command-line: + +> If you depend on database providers like [StackGres](https://stackgres.io/), [Postgres Operator](https://github.com/zalando/postgres-operator) or self-hosted Postgres instance, fill in the secret above and modify any relevant Postgres attributes such as port or hostname (e.g. `PGPORT`, `DB_HOST`) for any relevant deployments. Refer to [values.yaml](values.yaml) for more details. + +### Dashboard secret + +By default, a username and password is required to access the Supabase Studio dashboard. Simply change them at: + +```yaml +secret: + dashboard: + username: supabase + password: this_password_is_insecure_and_should_be_updated +``` + +### Analytics secret + +A new logflare secret API key is required for securing communication between all of the Supabase services. To set the secret, generate a new 32 characters long secret similar to the step [above](#jwt-secret). + +```yaml +secret: + analytics: + apiKey: your-super-secret-with-at-least-32-characters-long-logflare-key +``` + +### S3 secret + +Supabase storage supports the use of S3 object-storage. To enable S3 for Supabase storage: + +1. Set S3 key ID and access key: + ```yaml + secret: + s3: + keyId: your-s3-key-id + accessKey: your-s3-access-key + ``` +2. Set storage S3 environment variables: + ```yaml + storage: + environment: + # Set S3 endpoint if using external object-storage + # GLOBAL_S3_ENDPOINT: http://minio:9000 + STORAGE_BACKEND: s3 + GLOBAL_S3_PROTOCOL: http + GLOBAL_S3_FORCE_PATH_STYLE: true + AWS_DEFAULT_REGION: stub + ``` +3. (Optional) Enable internal minio deployment + ```yaml + minio: + enabled: true + ``` + +## How to use in Production + +We didn't provide a complete configuration to go production because of the multiple possibility. + +But here are the important points you have to think about: + +- Use a replicated version of the Postgres database. +- Add SSL to the Postgres database. +- Add SSL configuration to the ingresses endpoints using either the `cert-manager` or a LoadBalancer provider. +- Change the domain used in the ingresses endpoints. +- Generate a new secure JWT Secret. + +### Migration + +Migration from local development is made easy by adding migration scripts at `db.config` field. This will apply all of the migration scripts during the database initialization. For example: + +```yaml +db: + config: + 20230101000000_profiles.sql: | + create table profiles ( + id uuid references auth.users not null, + updated_at timestamp with time zone, + username text unique, + avatar_url text, + website text, + + primary key (id), + unique(username), + constraint username_length check (char_length(username) >= 3) + ); +``` + +To make copying scripts easier, use this handy bash script: + +```bash +#!/bin/bash + +for file in $1/*; do + clipboard+=" $(basename $file): |\n" + clipboard+=$(cat $file | awk '{print " "$0}') + clipboard+="\n" +done + +echo -e "$clipboard" +``` + +and pipe it to your system clipboard handler: + +```shell +# Using xclip as an example +./script.sh supabase/migrations | xclip -sel clipboard +``` + +## Troubleshooting + +### Ingress Controller and Ingress Class + +Depending on your Kubernetes version you might want to fill the `className` property instead of the `kubernetes.io/ingress.class` annotations. For example: + +```yml +kong: + ingress: + enabled: 'true' + className: "nginx" + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +``` + +### Testing suite + +Before creating a merge request, you can test the charts locally by using [helm/chart-testing](https://github.com/helm/chart-testing). If you have Docker and a Kubernetes environment to test with, simply run: + +```shell +# Run chart-testing (lint) +docker run -it \ + --workdir=/data \ + --volume $(pwd)/charts/supabase:/data \ + quay.io/helmpack/chart-testing:v3.7.1 \ + ct lint --validate-maintainers=false --chart-dirs . --charts . +# Run chart-testing (install) +docker run -it \ + --network host \ + --workdir=/data \ + --volume ~/.kube/config:/root/.kube/config:ro \ + --volume $(pwd)/charts/supabase:/data \ + quay.io/helmpack/chart-testing:v3.7.1 \ + ct install --chart-dirs . --charts . +``` + +### Version compatibility + +#### `0.0.x` to `0.1.x` + +* `supabase/postgres` is updated from `14.1` to `15.1`, which warrants backing up all your data before proceeding to update to the next major version. +* Intialization scripts for `supabase/postgres` has been reworked and matched closely to the [Docker Compose](https://github.com/supabase/supabase/blob/master/docker/docker-compose.yml) version. Further tweaks to the scripts are needed to ensure backward-compatibility. +* Migration scripts are now exposed at `db.config`, which will be mounted at `/docker-entrypoint-initdb.d/migrations/`. Simply copy your migration files from your local project's `supabase/migration` and populate the `db.config`. +* Ingress are now limited to `kong` & `db` services. This is by design to limit entry to the stack through secure `kong` service. +* `kong.yaml` has been modified to follow [Docker kong.yaml](https://github.com/supabase/supabase/blob/master/docker/volumes/api/kong.yml) template. +* `supabase/storage` does not comes with pre-populated `/var/lib/storage`, therefore an `emptyDir` will be created if persistence is disabled. This might be incompatible with previous version if the persistent storage location is set to location other than specified above. +* `supabase/vector` requires read access to the `/var/log/pods` directory. When run in a Kubernetes cluster this can be provided with a [hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath) volume. diff --git a/templates/NOTES.txt b/templates/NOTES.txt new file mode 100644 index 0000000..f06631a --- /dev/null +++ b/templates/NOTES.txt @@ -0,0 +1,5 @@ +--- +Thank you for installing {{ .Chart.Name }}! +{{ if .Values.kong.ingress.enabled }} +Visit the Studio dashboard at http://{{ (index .Values.kong.ingress.hosts 0).host }} +{{- end }} diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..995bb4f --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "supabase.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "supabase.labels" -}} +helm.sh/chart: {{ include "supabase.chart" . }} +{{ include "supabase.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "supabase.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/analytics/_helpers.tpl b/templates/analytics/_helpers.tpl new file mode 100644 index 0000000..d960a1d --- /dev/null +++ b/templates/analytics/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.analytics.name" -}} +{{- default (print .Chart.Name "-analytics") .Values.analytics.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.analytics.fullname" -}} +{{- if .Values.analytics.fullnameOverride }} +{{- .Values.analytics.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-analytics") .Values.analytics.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.analytics.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.analytics.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.analytics.serviceAccountName" -}} +{{- if .Values.analytics.serviceAccount.create }} +{{- default (include "supabase.analytics.fullname" .) .Values.analytics.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.analytics.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/analytics/deployment.yaml b/templates/analytics/deployment.yaml new file mode 100644 index 0000000..3c4f585 --- /dev/null +++ b/templates/analytics/deployment.yaml @@ -0,0 +1,179 @@ +{{- if .Values.analytics.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.analytics.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.analytics.autoscaling.enabled }} + replicas: {{ .Values.analytics.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.analytics.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.analytics.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.analytics.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.analytics.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.analytics.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.analytics.podSecurityContext | nindent 8 }} + initContainers: + - name: init-db + image: postgres:15-alpine + imagePullPolicy: IfNotPresent + env: + - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.analytics.environment.DB_HOST | quote }} + {{- end }} + - name: DB_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: DB_PORT + value: {{ .Values.analytics.environment.DB_PORT | quote }} + command: ["/bin/sh", "-c"] + args: + - | + until pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER); do + echo "Waiting for database to start..." + sleep 2 + done + - echo "Database is ready" + containers: + - name: {{ include "supabase.analytics.name" $ }} + securityContext: + {{- toYaml .Values.analytics.securityContext | nindent 12 }} + image: "{{ .Values.analytics.image.repository }}:{{ .Values.analytics.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.analytics.image.pullPolicy }} + env: + {{- range $key, $value := .Values.analytics.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOSTNAME + value: {{ include "supabase.db.fullname" . }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password_encoded + {{- end }} + - name: DB_DATABASE + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: LOGFLARE_API_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.analytics.secretRef }} + name: {{ .Values.secret.analytics.secretRef }} + key: {{ .Values.secret.analytics.secretRefKey.apiKey | default "apiKey" }} + {{- else }} + name: {{ include "supabase.secret.analytics" . }} + key: apiKey + {{- end }} + {{- if .Values.analytics.bigQuery.enabled }} + - name: GOOGLE_PROJECT_ID + value: {{ .Values.analytics.bigQuery.projectId | quote }} + - name: GOOGLE_PROJECT_NUMBER + value: {{ .Values.analytics.bigQuery.projectNumber | quote }} + {{- else }} + - name: POSTGRES_BACKEND_URL + value: $(DB_DRIVER)://$(DB_USERNAME):$(DB_PASSWORD_ENC)@$(DB_HOSTNAME):$(DB_PORT)/$(DB_DATABASE) + - name: POSTGRES_BACKEND_SCHEMA + value: $(DB_SCHEMA) + - name: LOGFLARE_FEATURE_FLAG_OVERRIDE + value: $(FEATURE_FLAG_OVERRIDE) + {{- end }} + {{- with .Values.analytics.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.analytics.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.analytics.service.port }} + protocol: TCP + {{- if or (.Values.analytics.bigQuery.enabled) (.Values.analytics.volumes) }} + volumeMounts: + {{- with .Values.analytics.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.analytics.bigQuery.enabled }} + - mountPath: /opt/app/rel/logflare/bin/gcloud.json + name: gcloud-credentials + subPath: gcloud.json + {{- end }} + {{- end }} + {{- with .Values.analytics.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or (.Values.analytics.bigQuery.enabled) (.Values.analytics.volumes) }} + volumes: + {{- with .Values.analytics.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.analytics.bigQuery.enabled }} + - name: gcloud-credentials + secret: + secretName: {{ include "supabase.analytics.fullname" . }}-gcloud + {{- end }} + {{- end }} + {{- with .Values.analytics.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.analytics.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.analytics.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/analytics/gcloud.secret.yaml b/templates/analytics/gcloud.secret.yaml new file mode 100644 index 0000000..71a645a --- /dev/null +++ b/templates/analytics/gcloud.secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.analytics.bigQuery.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.analytics.fullname" . }}-gcloud + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: + gcloud.json: {{ .Values.analytics.bigQuery.gcloudJson | b64enc }} +{{- end }} \ No newline at end of file diff --git a/templates/analytics/service.yaml b/templates/analytics/service.yaml new file mode 100644 index 0000000..d8f43cb --- /dev/null +++ b/templates/analytics/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.analytics.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.analytics.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.analytics.service.type }} + ports: + - port: {{ .Values.analytics.service.port }} + targetPort: 4000 + protocol: TCP + name: http + selector: + {{- include "supabase.analytics.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/analytics/serviceaccount.yaml b/templates/analytics/serviceaccount.yaml new file mode 100644 index 0000000..73c72f2 --- /dev/null +++ b/templates/analytics/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.analytics.enabled -}} +{{- if .Values.analytics.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.analytics.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.analytics.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/auth/_helpers.tpl b/templates/auth/_helpers.tpl new file mode 100644 index 0000000..73935b5 --- /dev/null +++ b/templates/auth/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.auth.name" -}} +{{- default (print .Chart.Name "-auth") .Values.auth.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.auth.fullname" -}} +{{- if .Values.auth.fullnameOverride }} +{{- .Values.auth.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-auth") .Values.auth.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.auth.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.auth.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.auth.serviceAccountName" -}} +{{- if .Values.auth.serviceAccount.create }} +{{- default (include "supabase.auth.fullname" .) .Values.auth.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.auth.serviceAccount.name }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/auth/deployment.yaml b/templates/auth/deployment.yaml new file mode 100644 index 0000000..8f3bba3 --- /dev/null +++ b/templates/auth/deployment.yaml @@ -0,0 +1,181 @@ +{{- if .Values.auth.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.auth.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.auth.autoscaling.enabled }} + replicas: {{ .Values.auth.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.auth.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.auth.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.auth.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.auth.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.auth.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.auth.podSecurityContext | nindent 8 }} + initContainers: + - name: init-db + image: postgres:15-alpine + imagePullPolicy: IfNotPresent + env: + - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} + - name: DB_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: DB_PORT + value: {{ .Values.auth.environment.DB_PORT | quote }} + command: ["/bin/sh", "-c"] + args: + - | + until pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER); do + echo "Waiting for database to start..." + sleep 2 + done + - echo "Database is ready" + containers: + - name: {{ include "supabase.auth.name" $ }} + securityContext: + {{- toYaml .Values.auth.securityContext | nindent 12 }} + image: "{{ .Values.auth.image.repository }}:{{ .Values.auth.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.auth.image.pullPolicy }} + env: + {{- range $key, $value := .Values.auth.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password_encoded + {{- end }} + - name: DB_NAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: GOTRUE_DB_DATABASE_URL + value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD_ENC)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?search_path=auth&sslmode=$(DB_SSL) + - name: GOTRUE_DB_DRIVER + value: $(DB_DRIVER) + - name: GOTRUE_JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + - name: GOTRUE_SMTP_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.smtp.secretRef }} + name: {{ .Values.secret.smtp.secretRef }} + key: {{ .Values.secret.smtp.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.smtp" . }} + key: username + {{- end }} + - name: GOTRUE_SMTP_PASS + valueFrom: + secretKeyRef: + {{- if .Values.secret.smtp.secretRef }} + name: {{ .Values.secret.smtp.secretRef }} + key: {{ .Values.secret.smtp.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.smtp" . }} + key: password + {{- end }} + {{- with .Values.auth.envFrom }} + envFrom: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.auth.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.auth.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 9999 + protocol: TCP + {{- with .Values.auth.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.auth.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.auth.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.auth.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.auth.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.auth.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/auth/service.yaml b/templates/auth/service.yaml new file mode 100644 index 0000000..c45d25c --- /dev/null +++ b/templates/auth/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.auth.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.auth.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.auth.service.type }} + ports: + - port: {{ .Values.auth.service.port }} + targetPort: 9999 + protocol: TCP + name: http + selector: + {{- include "supabase.auth.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/auth/serviceaccount.yaml b/templates/auth/serviceaccount.yaml new file mode 100644 index 0000000..fc0c570 --- /dev/null +++ b/templates/auth/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.auth.enabled -}} +{{- if .Values.auth.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.auth.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.auth.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/db/_helpers.tpl b/templates/db/_helpers.tpl new file mode 100644 index 0000000..596ea41 --- /dev/null +++ b/templates/db/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.db.name" -}} +{{- default (print .Chart.Name "-db") .Values.db.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.db.fullname" -}} +{{- if .Values.db.fullnameOverride }} +{{- .Values.db.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-db") .Values.db.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.db.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.db.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.db.serviceAccountName" -}} +{{- if .Values.db.serviceAccount.create }} +{{- default (include "supabase.db.fullname" .) .Values.db.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.db.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/db/deployment.yaml b/templates/db/deployment.yaml new file mode 100644 index 0000000..314842e --- /dev/null +++ b/templates/db/deployment.yaml @@ -0,0 +1,173 @@ +{{- if .Values.db.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.db.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.db.autoscaling.enabled }} + replicas: {{ .Values.db.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.db.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.db.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.db.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.db.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.db.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.db.podSecurityContext | nindent 8 }} + initContainers: + - name: init-db + image: "{{ .Values.db.image.repository }}:{{ .Values.db.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c"] + args: + - | + echo "Copying init scripts into existing image script directory..." + cp -r /docker-entrypoint-initdb.d/* /initdb.d/ + cp /custom-init-scripts/98-webhooks.sql /initdb.d/init-scripts/ + cp /custom-init-scripts/99-roles.sql /initdb.d/init-scripts/ + cp /custom-init-scripts/99-logs.sql /initdb.d/migrations/ + cp /custom-init-scripts/99-realtime.sql /initdb.d/migrations/ + + echo "Copying user-defined migration scripts..." + cp /custom-migrations/* /initdb.d/migrations/ || echo "Skip migrations" + echo "Initialization scripts are ready" + volumeMounts: + - mountPath: /custom-init-scripts + name: custom-init-scripts + - mountPath: /custom-migrations + name: custom-migrations + - mountPath: /initdb.d + name: initdb-scripts-data + containers: + - name: {{ include "supabase.db.name" $ }} + securityContext: + {{- toYaml .Values.db.securityContext | nindent 12 }} + image: "{{ .Values.db.image.repository }}:{{ .Values.db.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.db.image.pullPolicy }} + env: + {{- range $key, $value := .Values.db.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: PGPASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: PGDATABASE + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + {{- with .Values.db.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.db.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 9999 + protocol: TCP + volumeMounts: + - mountPath: /docker-entrypoint-initdb.d + name: initdb-scripts-data + {{- with .Values.db.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.db.persistence.enabled }} + - mountPath: /var/lib/postgresql/data + name: postgres-volume + subPath: postgres-data + {{- end }} + {{- with .Values.db.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: initdb-scripts-data + emptyDir: + medium: "" + - name: custom-init-scripts + configMap: + name: {{ include "supabase.db.fullname" . }}-initdb + - name: custom-migrations + configMap: + name: {{ include "supabase.db.fullname" . }}-migrations + {{- with .Values.db.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.db.persistence.enabled }} + - name: postgres-volume + persistentVolumeClaim: + claimName: {{ include "supabase.db.fullname" . }}-pvc + {{- end }} + {{- with .Values.db.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.db.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.db.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/db/initdb.config.yaml b/templates/db/initdb.config.yaml new file mode 100644 index 0000000..25f61ad --- /dev/null +++ b/templates/db/initdb.config.yaml @@ -0,0 +1,243 @@ +{{- if .Values.db.enabled -}} +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "supabase.db.fullname" . }}-initdb + labels: + {{- include "supabase.labels" . | nindent 4 }} +data: + 99-jwt.sql: | + \set jwt_secret `echo "$JWT_SECRET"` + \set jwt_exp `echo "$JWT_EXP"` + + ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :jwt_secret; + ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :jwt_exp; + 99-logs.sql: | + \set pguser `echo "$POSTGRES_USER"` + + create schema if not exists _analytics; + alter schema _analytics owner to :pguser; + 99-realtime.sql: | + \set pguser `echo "$POSTGRES_USER"` + + create schema if not exists _realtime; + alter schema _realtime owner to :pguser; + 99-roles.sql: | + -- NOTE: change to your own passwords for production environments + \set pgpass `echo "$POSTGRES_PASSWORD"` + + ALTER USER authenticator WITH PASSWORD :'pgpass'; + ALTER USER pgbouncer WITH PASSWORD :'pgpass'; + ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass'; + ALTER USER supabase_functions_admin WITH PASSWORD :'pgpass'; + ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; + 98-webhooks.sql: | + BEGIN; + -- Create pg_net extension + CREATE EXTENSION IF NOT EXISTS pg_net SCHEMA extensions; + -- Create supabase_functions schema + CREATE SCHEMA supabase_functions AUTHORIZATION supabase_admin; + GRANT USAGE ON SCHEMA supabase_functions TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON FUNCTIONS TO postgres, anon, authenticated, service_role; + ALTER DEFAULT PRIVILEGES IN SCHEMA supabase_functions GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role; + -- supabase_functions.migrations definition + CREATE TABLE supabase_functions.migrations ( + version text PRIMARY KEY, + inserted_at timestamptz NOT NULL DEFAULT NOW() + ); + -- Initial supabase_functions migration + INSERT INTO supabase_functions.migrations (version) VALUES ('initial'); + -- supabase_functions.hooks definition + CREATE TABLE supabase_functions.hooks ( + id bigserial PRIMARY KEY, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamptz NOT NULL DEFAULT NOW(), + request_id bigint + ); + CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + CREATE FUNCTION supabase_functions.http_request() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END + $function$; + -- Supabase super admin + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_functions_admin' + ) + THEN + CREATE USER supabase_functions_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; + END IF; + END + $$; + GRANT ALL PRIVILEGES ON SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA supabase_functions TO supabase_functions_admin; + GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA supabase_functions TO supabase_functions_admin; + ALTER USER supabase_functions_admin SET search_path = "supabase_functions"; + ALTER table "supabase_functions".migrations OWNER TO supabase_functions_admin; + ALTER table "supabase_functions".hooks OWNER TO supabase_functions_admin; + ALTER function "supabase_functions".http_request() OWNER TO supabase_functions_admin; + GRANT supabase_functions_admin TO postgres; + -- Remove unused supabase_pg_net_admin role + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_roles + WHERE rolname = 'supabase_pg_net_admin' + ) + THEN + REASSIGN OWNED BY supabase_pg_net_admin TO supabase_admin; + DROP OWNED BY supabase_pg_net_admin; + DROP ROLE supabase_pg_net_admin; + END IF; + END + $$; + -- pg_net grants when extension is already enabled + DO + $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_extension + WHERE extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END + $$; + -- Event trigger for pg_net + CREATE OR REPLACE FUNCTION extensions.grant_pg_net_access() + RETURNS event_trigger + LANGUAGE plpgsql + AS $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; + END; + $$; + COMMENT ON FUNCTION extensions.grant_pg_net_access IS 'Grants access to pg_net'; + DO + $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_event_trigger + WHERE evtname = 'issue_pg_net_access' + ) THEN + CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end WHEN TAG IN ('CREATE EXTENSION') + EXECUTE PROCEDURE extensions.grant_pg_net_access(); + END IF; + END + $$; + INSERT INTO supabase_functions.migrations (version) VALUES ('20210809183423_update_grants'); + ALTER function supabase_functions.http_request() SECURITY DEFINER; + ALTER function supabase_functions.http_request() SET search_path = supabase_functions; + REVOKE ALL ON FUNCTION supabase_functions.http_request() FROM PUBLIC; + GRANT EXECUTE ON FUNCTION supabase_functions.http_request() TO postgres, anon, authenticated, service_role; + COMMIT; +{{- end }} \ No newline at end of file diff --git a/templates/db/migration.config.yaml b/templates/db/migration.config.yaml new file mode 100644 index 0000000..77acec5 --- /dev/null +++ b/templates/db/migration.config.yaml @@ -0,0 +1,10 @@ +{{- if .Values.db.enabled -}} +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "supabase.db.fullname" . }}-migrations + labels: + {{- include "supabase.labels" . | nindent 4 }} +data: + {{- toYaml .Values.db.config | nindent 2 }} +{{- end }} \ No newline at end of file diff --git a/templates/db/service.yaml b/templates/db/service.yaml new file mode 100644 index 0000000..c8dc9fa --- /dev/null +++ b/templates/db/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.db.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.db.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.db.service.type }} + ports: + - port: {{ .Values.db.service.port }} + targetPort: 5432 + protocol: TCP + name: http + selector: + {{- include "supabase.db.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/db/serviceaccount.yaml b/templates/db/serviceaccount.yaml new file mode 100644 index 0000000..204e327 --- /dev/null +++ b/templates/db/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.db.enabled -}} +{{- if .Values.db.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.db.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.db.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/db/volume.yaml b/templates/db/volume.yaml new file mode 100644 index 0000000..a02bc33 --- /dev/null +++ b/templates/db/volume.yaml @@ -0,0 +1,25 @@ +{{- if .Values.db.enabled -}} +{{- if .Values.db.persistence.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "supabase.db.fullname" . }}-pvc + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.db.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.db.persistence.storageClassName }} + storageClassName: {{ .Values.db.persistence.storageClassName }} + {{- end }} + accessModes: + {{- range .Values.db.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.db.persistence.size | quote }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/functions/_helpers.tpl b/templates/functions/_helpers.tpl new file mode 100644 index 0000000..10e6bea --- /dev/null +++ b/templates/functions/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.functions.name" -}} +{{- default (print .Chart.Name "-functions") .Values.functions.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.functions.fullname" -}} +{{- if .Values.functions.fullnameOverride }} +{{- .Values.functions.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-functions") .Values.functions.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.functions.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.functions.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.functions.serviceAccountName" -}} +{{- if .Values.functions.serviceAccount.create }} +{{- default (include "supabase.functions.fullname" .) .Values.functions.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.functions.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/functions/deployment.yaml b/templates/functions/deployment.yaml new file mode 100644 index 0000000..2272689 --- /dev/null +++ b/templates/functions/deployment.yaml @@ -0,0 +1,151 @@ +{{- if .Values.functions.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.functions.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.functions.autoscaling.enabled }} + replicas: {{ .Values.functions.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.functions.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.functions.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.functions.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.functions.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.functions.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.functions.podSecurityContext | nindent 8 }} + containers: + - args: + - start + - --main-service + - /home/deno/functions/main + name: {{ include "supabase.functions.name" $ }} + securityContext: + {{- toYaml .Values.functions.securityContext | nindent 12 }} + image: "{{ .Values.functions.image.repository }}:{{ .Values.functions.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.functions.image.pullPolicy }} + env: + {{- range $key, $value := .Values.functions.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: DB_HOSTNAME + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . }} + {{- else }} + value: $(DB_HOST) + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password_encoded + {{- end }} + - name: DB_DATABASE + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + - name: SUPABASE_ANON_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.anonKey | default "anonKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: anonKey + {{- end }} + - name: SUPABASE_SERVICE_ROLE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.serviceKey | default "serviceKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: serviceKey + {{- end }} + - name: POSTGRES_BACKEND_URL + value: $(DB_DRIVER)://$(DB_USERNAME):$(DB_PASSWORD_ENC)@$(DB_HOSTNAME):$(DB_PORT)/$(DB_DATABASE)?search_path=auth&sslmode=$(DB_SSL) + {{- with .Values.functions.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.functions.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- with .Values.functions.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - mountPath: /home/deno/functions/main + name: functions-main + {{- with .Values.functions.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + {{- with .Values.functions.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + - name: functions-main + configMap: + name: {{ include "supabase.functions.fullname" . }}-main + {{- with .Values.functions.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.functions.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.functions.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/functions/functions.config.yaml b/templates/functions/functions.config.yaml new file mode 100644 index 0000000..8aed927 --- /dev/null +++ b/templates/functions/functions.config.yaml @@ -0,0 +1,104 @@ +{{- if .Values.functions.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "supabase.functions.fullname" . }}-main + labels: + {{- include "supabase.labels" . | nindent 4 }} +data: + index.ts: | + import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' + import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts' + + console.log('main function started') + + const JWT_SECRET = Deno.env.get('JWT_SECRET') + const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true' + + function getAuthToken(req: Request) { + const authHeader = req.headers.get('authorization') + if (!authHeader) { + throw new Error('Missing authorization header') + } + const [bearer, token] = authHeader.split(' ') + if (bearer !== 'Bearer') { + throw new Error(`Auth header is not 'Bearer {token}'`) + } + return token + } + + async function verifyJWT(jwt: string): Promise { + const encoder = new TextEncoder() + const secretKey = encoder.encode(JWT_SECRET) + try { + await jose.jwtVerify(jwt, secretKey) + } catch (err) { + console.error(err) + return false + } + return true + } + + serve(async (req: Request) => { + if (req.method !== 'OPTIONS' && VERIFY_JWT) { + try { + const token = getAuthToken(req) + const isValidJWT = await verifyJWT(token) + + if (!isValidJWT) { + return new Response(JSON.stringify({ msg: 'Invalid JWT' }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } catch (e) { + console.error(e) + return new Response(JSON.stringify({ msg: e.toString() }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }) + } + } + + const url = new URL(req.url) + const { pathname } = url + const path_parts = pathname.split('/') + const service_name = path_parts[1] + + if (!service_name || service_name === '') { + const error = { msg: 'missing function name in request' } + return new Response(JSON.stringify(error), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }) + } + + const servicePath = `/home/deno/functions/${service_name}` + console.error(`serving the request with ${servicePath}`) + + const memoryLimitMb = 150 + const workerTimeoutMs = 1 * 60 * 1000 + const noModuleCache = false + const importMapPath = null + const envVarsObj = Deno.env.toObject() + const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]]) + + try { + const worker = await EdgeRuntime.userWorkers.create({ + servicePath, + memoryLimitMb, + workerTimeoutMs, + noModuleCache, + importMapPath, + envVars, + }) + return await worker.fetch(req) + } catch (e) { + const error = { msg: e.toString() } + return new Response(JSON.stringify(error), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }) + } + }) +{{- end }} \ No newline at end of file diff --git a/templates/functions/service.yaml b/templates/functions/service.yaml new file mode 100644 index 0000000..f70f1e8 --- /dev/null +++ b/templates/functions/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.functions.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.functions.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.functions.service.type }} + ports: + - port: {{ .Values.functions.service.port }} + targetPort: 9000 + protocol: TCP + name: http + selector: + {{- include "supabase.functions.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/functions/serviceaccount.yaml b/templates/functions/serviceaccount.yaml new file mode 100644 index 0000000..f8e60ab --- /dev/null +++ b/templates/functions/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.functions.enabled -}} +{{- if .Values.functions.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.functions.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.functions.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/imgproxy/_helpers.tpl b/templates/imgproxy/_helpers.tpl new file mode 100644 index 0000000..c37ad02 --- /dev/null +++ b/templates/imgproxy/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.imgproxy.name" -}} +{{- default (print .Chart.Name "-imgproxy") .Values.imgproxy.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.imgproxy.fullname" -}} +{{- if .Values.imgproxy.fullnameOverride }} +{{- .Values.imgproxy.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-imgproxy") .Values.imgproxy.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.imgproxy.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.imgproxy.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.imgproxy.serviceAccountName" -}} +{{- if .Values.imgproxy.serviceAccount.create }} +{{- default (include "supabase.imgproxy.fullname" .) .Values.imgproxy.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.imgproxy.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/imgproxy/deployment.yaml b/templates/imgproxy/deployment.yaml new file mode 100644 index 0000000..ad89b03 --- /dev/null +++ b/templates/imgproxy/deployment.yaml @@ -0,0 +1,91 @@ +{{- if .Values.imgproxy.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.imgproxy.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.imgproxy.autoscaling.enabled }} + replicas: {{ .Values.imgproxy.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.imgproxy.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.imgproxy.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.imgproxy.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imgproxy.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.imgproxy.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.imgproxy.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.imgproxy.name" $ }} + securityContext: + {{- toYaml .Values.imgproxy.securityContext | nindent 12 }} + image: "{{ .Values.imgproxy.image.repository }}:{{ .Values.imgproxy.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.imgproxy.image.pullPolicy }} + env: + {{- range $key, $value := .Values.imgproxy.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- with .Values.imgproxy.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.imgproxy.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- if or (.Values.imgproxy.persistence.enabled) (.Values.imgproxy.volumes) }} + volumeMounts: + {{- with .Values.imgproxy.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.imgproxy.persistence.enabled }} + - mountPath: /var/lib/storage + name: imgproxy-volume + {{- end }} + {{- end }} + {{- with .Values.imgproxy.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or (.Values.imgproxy.persistence.enabled) (.Values.imgproxy.volumes) }} + volumes: + {{- with .Values.imgproxy.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.imgproxy.persistence.enabled }} + - name: imgproxy-volume + persistentVolumeClaim: + claimName: {{ include "supabase.imgproxy.fullname" . }}-pvc + {{- end }} + {{- end }} + {{- with .Values.imgproxy.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imgproxy.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.imgproxy.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/imgproxy/service.yaml b/templates/imgproxy/service.yaml new file mode 100644 index 0000000..109f3d5 --- /dev/null +++ b/templates/imgproxy/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.imgproxy.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.imgproxy.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.imgproxy.service.type }} + ports: + - port: {{ .Values.imgproxy.service.port }} + targetPort: 5001 + protocol: TCP + name: http + selector: + {{- include "supabase.imgproxy.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/imgproxy/serviceaccount.yaml b/templates/imgproxy/serviceaccount.yaml new file mode 100644 index 0000000..04e62b9 --- /dev/null +++ b/templates/imgproxy/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.imgproxy.enabled -}} +{{- if .Values.imgproxy.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.imgproxy.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.imgproxy.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/imgproxy/volume.yaml b/templates/imgproxy/volume.yaml new file mode 100644 index 0000000..34204bd --- /dev/null +++ b/templates/imgproxy/volume.yaml @@ -0,0 +1,25 @@ +{{- if .Values.imgproxy.enabled -}} +{{- if .Values.imgproxy.persistence.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "supabase.imgproxy.fullname" . }}-pvc + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.imgproxy.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.imgproxy.persistence.storageClassName }} + storageClassName: {{ .Values.imgproxy.persistence.storageClassName }} + {{- end }} + accessModes: + {{- range .Values.imgproxy.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.imgproxy.persistence.size | quote }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/kong/_helpers.tpl b/templates/kong/_helpers.tpl new file mode 100644 index 0000000..5589efa --- /dev/null +++ b/templates/kong/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.kong.name" -}} +{{- default (print .Chart.Name "-kong") .Values.kong.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.kong.fullname" -}} +{{- if .Values.kong.fullnameOverride }} +{{- .Values.kong.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-kong") .Values.kong.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.kong.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.kong.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.kong.serviceAccountName" -}} +{{- if .Values.kong.serviceAccount.create }} +{{- default (include "supabase.kong.fullname" .) .Values.kong.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.kong.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/kong/config.yaml b/templates/kong/config.yaml new file mode 100644 index 0000000..2edd25a --- /dev/null +++ b/templates/kong/config.yaml @@ -0,0 +1,233 @@ +{{- if .Values.kong.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "supabase.kong.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +data: + wrapper.sh: | + #!/bin/bash + + set -euo pipefail + + echo "Replacing env placeholders of /usr/local/kong/kong.yml" + + sed \ + -e "s/\${SUPABASE_ANON_KEY}/${SUPABASE_ANON_KEY}/" \ + -e "s/\${SUPABASE_SERVICE_KEY}/${SUPABASE_SERVICE_KEY}/" \ + -e "s/\${DASHBOARD_USERNAME}/${DASHBOARD_USERNAME}/" \ + -e "s/\${DASHBOARD_PASSWORD}/${DASHBOARD_PASSWORD}/" \ + /usr/local/kong/template.yml \ + > /usr/local/kong/kong.yml + + exec /docker-entrypoint.sh kong docker-start + template.yml: | + _format_version: '2.1' + _transform: true + + consumers: + {{- if .Values.secret.dashboard }} + - username: DASHBOARD + {{- end }} + - username: anon + keyauth_credentials: + - key: ${SUPABASE_ANON_KEY} + - username: service_role + keyauth_credentials: + - key: ${SUPABASE_SERVICE_KEY} + acls: + - consumer: anon + group: anon + - consumer: service_role + group: admin + {{- if .Values.secret.dashboard }} + basicauth_credentials: + - consumer: DASHBOARD + username: ${DASHBOARD_USERNAME} + password: ${DASHBOARD_PASSWORD} + {{- end }} + services: + {{- if .Values.auth.enabled }} + - name: auth-v1-open + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/verify + routes: + - name: auth-v1-open + strip_path: true + paths: + - /auth/v1/verify + plugins: + - name: cors + - name: auth-v1-open-callback + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/callback + routes: + - name: auth-v1-open-callback + strip_path: true + paths: + - /auth/v1/callback + plugins: + - name: cors + - name: auth-v1-open-authorize + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/authorize + routes: + - name: auth-v1-open-authorize + strip_path: true + paths: + - /auth/v1/authorize + plugins: + - name: cors + - name: auth-v1 + _comment: "GoTrue: /auth/v1/* -> http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/*" + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }} + routes: + - name: auth-v1-all + strip_path: true + paths: + - /auth/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + {{- end }} + {{- if .Values.rest.enabled }} + - name: rest-v1 + _comment: "PostgREST: /rest/v1/* -> http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }}/*" + url: http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }}/ + routes: + - name: rest-v1-all + strip_path: true + paths: + - /rest/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + - name: graphql-v1 + _comment: 'PostgREST: /graphql/v1/* -> http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }}/rpc/graphql' + url: http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }}/rpc/graphql + routes: + - name: graphql-v1-all + strip_path: true + paths: + - /graphql/v1 + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: true + - name: request-transformer + config: + add: + headers: + - Content-Profile:graphql_public + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + {{- end }} + {{- if .Values.realtime.enabled }} + - name: realtime-v1 + _comment: "Realtime: /realtime/v1/* -> ws://{{ include "supabase.realtime.fullname" . }}:{{ .Values.realtime.service.port }}/socket/*" + url: http://{{ include "supabase.realtime.fullname" . }}:{{ .Values.realtime.service.port }}/socket + routes: + - name: realtime-v1-all + strip_path: true + paths: + - /realtime/v1/ + plugins: + - name: cors + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + - anon + {{- end }} + {{- if .Values.storage.enabled }} + - name: storage-v1 + _comment: "Storage: /storage/v1/* -> http://{{ include "supabase.storage.fullname" . }}:{{ .Values.storage.service.port }}/*" + url: http://{{ include "supabase.storage.fullname" . }}:{{ .Values.storage.service.port }}/ + routes: + - name: storage-v1-all + strip_path: true + paths: + - /storage/v1/ + plugins: + - name: cors + {{- end }} + {{- if .Values.functions.enabled }} + - name: functions-v1 + _comment: 'Edge Functions: /functions/v1/* -> http://{{ include "supabase.functions.fullname" . }}:{{ .Values.functions.service.port }}/*' + url: http://functions:{{ .Values.functions.service.port }}/ + routes: + - name: functions-v1-all + strip_path: true + paths: + - /functions/v1/ + plugins: + - name: cors + {{- end }} + {{- if .Values.analytics.enabled }} + - name: analytics-v1 + _comment: 'Analytics: /analytics/v1/* -> http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/*' + url: http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/ + routes: + - name: analytics-v1-all + strip_path: true + paths: + - /analytics/v1/ + {{- end }} + {{- if .Values.meta.enabled }} + - name: meta + _comment: "pg-meta: /pg/* -> http://{{ include "supabase.meta.fullname" . }}:{{ .Values.meta.service.port }}/*" + url: http://{{ include "supabase.meta.fullname" . }}:{{ .Values.meta.service.port }}/ + routes: + - name: meta-all + strip_path: true + paths: + - /pg/ + plugins: + - name: key-auth + config: + hide_credentials: false + - name: acl + config: + hide_groups_header: true + allow: + - admin + {{- end }} + - name: dashboard + _comment: 'Studio: /* -> http://{{ include "supabase.studio.fullname" . }}:{{ .Values.studio.service.port }}/*' + url: http://{{ include "supabase.studio.fullname" . }}:{{ .Values.studio.service.port }}/ + routes: + - name: dashboard-all + strip_path: true + paths: + - / + {{- if .Values.secret.dashboard }} + plugins: + - name: cors + - name: basic-auth + config: + hide_credentials: true + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/kong/deployment.yaml b/templates/kong/deployment.yaml new file mode 100644 index 0000000..fcedfbb --- /dev/null +++ b/templates/kong/deployment.yaml @@ -0,0 +1,141 @@ +{{- if .Values.kong.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.kong.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.kong.autoscaling.enabled }} + replicas: {{ .Values.kong.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.kong.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.kong.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.kong.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.kong.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.kong.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.kong.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.kong.name" $ }} + securityContext: + {{- toYaml .Values.kong.securityContext | nindent 12 }} + image: "{{ .Values.kong.image.repository }}:{{ .Values.kong.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.kong.image.pullPolicy }} + command: ["/bin/bash"] + args: ["/scripts/wrapper.sh"] + env: + {{- range $key, $value := .Values.kong.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: SUPABASE_ANON_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.anonKey | default "anonKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: anonKey + {{- end }} + - name: SUPABASE_SERVICE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.serviceKey | default "serviceKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: serviceKey + {{- end }} + {{- if .Values.secret.dashboard }} + - name: DASHBOARD_USERNAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.dashboard.secretRef }} + name: {{ .Values.secret.dashboard.secretRef }} + key: {{ .Values.secret.dashboard.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.dashboard" . }} + key: username + {{- end }} + - name: DASHBOARD_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.dashboard.secretRef }} + name: {{ .Values.secret.dashboard.secretRef }} + key: {{ .Values.secret.dashboard.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.dashboard" . }} + key: password + {{- end }} + {{- end }} + {{- with .Values.kong.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kong.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8000 + protocol: TCP + {{- with .Values.kong.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /usr/local/kong/template.yml + name: config + subPath: template.yml + - mountPath: /scripts + name: wrapper + {{- with .Values.kong.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kong.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.kong.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.kong.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: config + configMap: + name: {{ include "supabase.kong.fullname" $ }} + defaultMode: 0777 + items: + - key: template.yml + path: template.yml + - name: wrapper + configMap: + name: {{ include "supabase.kong.fullname" $ }} + defaultMode: 0777 + items: + - key: wrapper.sh + path: wrapper.sh + {{- with .Values.kong.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/kong/ingress.yaml b/templates/kong/ingress.yaml new file mode 100644 index 0000000..b6dff38 --- /dev/null +++ b/templates/kong/ingress.yaml @@ -0,0 +1,57 @@ +{{- if .Values.kong.enabled -}} +{{- if .Values.kong.ingress.enabled -}} +{{- $fullName := include "supabase.kong.fullname" . -}} +{{- $svcPort := .Values.kong.service.port -}} +{{- if and .Values.kong.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.kong.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.kong.ingress.annotations "kubernetes.io/ingress.class" .Values.kong.ingress.className}} + {{- end }} +{{- end }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.kong.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.kong.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.kong.ingress.className }} + {{- end }} + {{- if .Values.kong.ingress.tls }} + tls: + {{- range .Values.kong.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.kong.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/kong/service.yaml b/templates/kong/service.yaml new file mode 100644 index 0000000..a4e6c14 --- /dev/null +++ b/templates/kong/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.kong.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.kong.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.kong.service.type }} + ports: + - port: {{ .Values.kong.service.port }} + targetPort: 8000 + protocol: TCP + name: http + selector: + {{- include "supabase.kong.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/kong/serviceaccount.yaml b/templates/kong/serviceaccount.yaml new file mode 100644 index 0000000..88b2642 --- /dev/null +++ b/templates/kong/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.kong.enabled -}} +{{- if .Values.kong.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.kong.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.kong.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/meta/_helpers.tpl b/templates/meta/_helpers.tpl new file mode 100644 index 0000000..ec4af06 --- /dev/null +++ b/templates/meta/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.meta.name" -}} +{{- default (print .Chart.Name "-meta") .Values.meta.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.meta.fullname" -}} +{{- if .Values.meta.fullnameOverride }} +{{- .Values.meta.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-meta") .Values.meta.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.meta.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.meta.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.meta.serviceAccountName" -}} +{{- if .Values.meta.serviceAccount.create }} +{{- default (include "supabase.meta.fullname" .) .Values.meta.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.meta.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/meta/deployment.yaml b/templates/meta/deployment.yaml new file mode 100644 index 0000000..2dac17b --- /dev/null +++ b/templates/meta/deployment.yaml @@ -0,0 +1,114 @@ +{{- if .Values.meta.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.meta.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.meta.autoscaling.enabled }} + replicas: {{ .Values.meta.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.meta.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.meta.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.meta.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.meta.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.meta.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.meta.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.meta.name" $ }} + securityContext: + {{- toYaml .Values.meta.securityContext | nindent 12 }} + image: "{{ .Values.meta.image.repository }}:{{ .Values.meta.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.meta.image.pullPolicy }} + env: + {{- range $key, $value := .Values.meta.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_NAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: PG_META_DB_HOST + value: $(DB_HOST) + - name: PG_META_DB_PORT + value: $(DB_PORT) + - name: PG_META_DB_NAME + value: $(DB_NAME) + - name: PG_META_DB_USER + value: $(DB_USER) + - name: PG_META_DB_PASSWORD + value: $(DB_PASSWORD) + - name: PG_META_DB_SSL_MODE + value: $(DB_SSL) + {{- with .Values.meta.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.meta.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + protocol: TCP + {{- with .Values.meta.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.meta.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.meta.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.meta.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.meta.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.meta.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/meta/service.yaml b/templates/meta/service.yaml new file mode 100644 index 0000000..933c8bc --- /dev/null +++ b/templates/meta/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.meta.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.meta.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.meta.service.type }} + ports: + - port: {{ .Values.meta.service.port }} + targetPort: 8080 + protocol: TCP + name: http + selector: + {{- include "supabase.meta.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/meta/serviceaccount.yaml b/templates/meta/serviceaccount.yaml new file mode 100644 index 0000000..d494023 --- /dev/null +++ b/templates/meta/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.meta.enabled -}} +{{- if .Values.meta.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.meta.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.meta.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/minio/_helpers.tpl b/templates/minio/_helpers.tpl new file mode 100644 index 0000000..241d041 --- /dev/null +++ b/templates/minio/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.minio.name" -}} +{{- default (print .Chart.Name "-minio") .Values.minio.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.minio.fullname" -}} +{{- if .Values.minio.fullnameOverride }} +{{- .Values.minio.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-minio") .Values.minio.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.minio.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.minio.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.minio.serviceAccountName" -}} +{{- if .Values.minio.serviceAccount.create }} +{{- default (include "supabase.minio.fullname" .) .Values.minio.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.minio.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/minio/deployment.yaml b/templates/minio/deployment.yaml new file mode 100644 index 0000000..dc97073 --- /dev/null +++ b/templates/minio/deployment.yaml @@ -0,0 +1,100 @@ +{{- if .Values.minio.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.minio.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.minio.autoscaling.enabled }} + replicas: {{ .Values.minio.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.minio.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.minio.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.minio.selectorLabels" . | nindent 8 }} + spec: + restartPolicy: Always + {{- with .Values.minio.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.minio.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.minio.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.minio.name" $ }} + securityContext: + {{- toYaml .Values.minio.securityContext | nindent 12 }} + image: "{{ .Values.minio.image.repository }}:{{ .Values.minio.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.minio.image.pullPolicy }} + args: + - server + - --console-address + - ":9001" + - /data + env: + - name: MINIO_ROOT_USER + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: keyId + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: accessKey + {{- with .Values.minio.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.minio.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 9000 + protocol: TCP + {{- with .Values.minio.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- with .Values.minio.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - mountPath: /data + name: minio-data + {{- with .Values.minio.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.minio.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.minio.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: minio-data + {{- if .Values.minio.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "supabase.minio.fullname" . }}-pvc + {{- else }} + emptyDir: + medium: "" + {{- end }} + {{- with .Values.minio.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/minio/service.yaml b/templates/minio/service.yaml new file mode 100644 index 0000000..30b6b1f --- /dev/null +++ b/templates/minio/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.minio.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.minio.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.minio.service.type }} + ports: + - port: {{ .Values.minio.service.port }} + targetPort: 9000 + protocol: TCP + name: http + selector: + {{- include "supabase.minio.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/minio/serviceaccount.yaml b/templates/minio/serviceaccount.yaml new file mode 100644 index 0000000..84116a1 --- /dev/null +++ b/templates/minio/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.minio.enabled -}} +{{- if .Values.minio.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.minio.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.minio.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/minio/volume.yaml b/templates/minio/volume.yaml new file mode 100644 index 0000000..26e3e66 --- /dev/null +++ b/templates/minio/volume.yaml @@ -0,0 +1,25 @@ +{{- if .Values.minio.enabled -}} +{{- if .Values.minio.persistence.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "supabase.minio.fullname" . }}-pvc + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.minio.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.minio.persistence.minioClassName }} + minioClassName: {{ .Values.minio.persistence.minioClassName }} + {{- end }} + accessModes: + {{- range .Values.minio.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.minio.persistence.size | quote }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/realtime/_helpers.tpl b/templates/realtime/_helpers.tpl new file mode 100644 index 0000000..54177a0 --- /dev/null +++ b/templates/realtime/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.realtime.name" -}} +{{- default (print .Chart.Name "-realtime") .Values.realtime.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.realtime.fullname" -}} +{{- if .Values.realtime.fullnameOverride }} +{{- .Values.realtime.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-realtime") .Values.realtime.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.realtime.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.realtime.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.realtime.serviceAccountName" -}} +{{- if .Values.realtime.serviceAccount.create }} +{{- default (include "supabase.realtime.fullname" .) .Values.realtime.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.realtime.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/realtime/deployment.yaml b/templates/realtime/deployment.yaml new file mode 100644 index 0000000..4083a46 --- /dev/null +++ b/templates/realtime/deployment.yaml @@ -0,0 +1,155 @@ +{{- if .Values.realtime.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.realtime.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.realtime.autoscaling.enabled }} + replicas: {{ .Values.realtime.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.realtime.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.realtime.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.realtime.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.realtime.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.realtime.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.realtime.podSecurityContext | nindent 8 }} + initContainers: + - name: init-db + image: postgres:15-alpine + imagePullPolicy: IfNotPresent + env: + - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} + - name: DB_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: DB_PORT + value: {{ .Values.analytics.environment.DB_PORT | quote }} + command: ["/bin/sh", "-c"] + args: + - | + until pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER); do + echo "Waiting for database to start..." + sleep 2 + done + - echo "Database is ready" + containers: + - name: {{ include "supabase.realtime.name" $ }} + securityContext: + {{- toYaml .Values.realtime.securityContext | nindent 12 }} + image: "{{ .Values.realtime.image.repository }}:{{ .Values.realtime.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.realtime.image.pullPolicy }} + command: ["/bin/sh"] + args: ["-c", "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"] + env: + {{- range $key, $value := .Values.realtime.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_NAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + - name: API_JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + {{- with .Values.realtime.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.realtime.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 4000 + protocol: TCP + {{- with .Values.realtime.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.realtime.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.realtime.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/realtime/service.yaml b/templates/realtime/service.yaml new file mode 100644 index 0000000..24b3009 --- /dev/null +++ b/templates/realtime/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.realtime.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.realtime.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.realtime.service.type }} + ports: + - port: {{ .Values.realtime.service.port }} + targetPort: 4000 + protocol: TCP + name: http + selector: + {{- include "supabase.realtime.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/realtime/serviceaccount.yaml b/templates/realtime/serviceaccount.yaml new file mode 100644 index 0000000..f1c55f7 --- /dev/null +++ b/templates/realtime/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.realtime.enabled -}} +{{- if .Values.realtime.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.realtime.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.realtime.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/rest/_helpers.tpl b/templates/rest/_helpers.tpl new file mode 100644 index 0000000..f763a06 --- /dev/null +++ b/templates/rest/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.rest.name" -}} +{{- default (print .Chart.Name "-rest") .Values.rest.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.rest.fullname" -}} +{{- if .Values.rest.fullnameOverride }} +{{- .Values.rest.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-rest") .Values.rest.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.rest.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.rest.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.rest.serviceAccountName" -}} +{{- if .Values.rest.serviceAccount.create }} +{{- default (include "supabase.rest.fullname" .) .Values.rest.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.rest.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/rest/deployment.yaml b/templates/rest/deployment.yaml new file mode 100644 index 0000000..e877fa6 --- /dev/null +++ b/templates/rest/deployment.yaml @@ -0,0 +1,134 @@ +{{- if .Values.rest.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.rest.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.rest.autoscaling.enabled }} + replicas: {{ .Values.rest.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.rest.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.rest.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.rest.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.rest.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.rest.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.rest.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.rest.name" $ }} + securityContext: + {{- toYaml .Values.rest.securityContext | nindent 12 }} + image: "{{ .Values.rest.image.repository }}:{{ .Values.rest.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.rest.image.pullPolicy }} + env: + {{- range $key, $value := .Values.rest.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password_encoded + {{- end }} + - name: DB_NAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: PGRST_DB_URI + value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD_ENC)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=$(DB_SSL) + - name: PGRST_JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + - name: PGRST_APP_SETTINGS_JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + {{- with .Values.rest.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.rest.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 3000 + protocol: TCP + {{- with .Values.rest.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.rest.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.rest.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rest.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rest.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.rest.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/rest/service.yaml b/templates/rest/service.yaml new file mode 100644 index 0000000..a680b47 --- /dev/null +++ b/templates/rest/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rest.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.rest.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.rest.service.type }} + ports: + - port: {{ .Values.rest.service.port }} + targetPort: 3000 + protocol: TCP + name: http + selector: + {{- include "supabase.rest.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/rest/serviceaccount.yaml b/templates/rest/serviceaccount.yaml new file mode 100644 index 0000000..030d960 --- /dev/null +++ b/templates/rest/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.rest.enabled -}} +{{- if .Values.rest.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.rest.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.rest.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/secrets/_helpers.tpl b/templates/secrets/_helpers.tpl new file mode 100644 index 0000000..61d74ec --- /dev/null +++ b/templates/secrets/_helpers.tpl @@ -0,0 +1,57 @@ +{{/* +Expand the name of the JWT secret. +*/}} +{{- define "supabase.secret.jwt" -}} +{{- printf "%s-jwt" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Expand the name of the SMTP secret. +*/}} +{{- define "supabase.secret.smtp" -}} +{{- printf "%s-smtp" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Expand the name of the dashboard secret. +*/}} +{{- define "supabase.secret.dashboard" -}} +{{- printf "%s-dashboard" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Expand the name of the database secret. +*/}} +{{- define "supabase.secret.db" -}} +{{- printf "%s-db" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Expand the name of the analytics secret. +*/}} +{{- define "supabase.secret.analytics" -}} +{{- printf "%s-analytics" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Expand the name of the s3 secret. +*/}} +{{- define "supabase.secret.s3" -}} +{{- printf "%s-s3" (include "supabase.fullname" .) }} +{{- end -}} + +{{/* +Check if both s3 keys are valid +*/}} +{{- define "supabase.secret.s3.isValid" -}} +{{- $isValid := "false" -}} +{{- if .Values.secret.s3.keyId -}} +{{- if .Values.secret.s3.accessKey -}} +{{- printf "true" -}} +{{- else -}} +{{- printf "false" -}} +{{- end -}} +{{- else -}} +{{- printf "false" -}} +{{- end -}} +{{- end -}} diff --git a/templates/secrets/analytics.yaml b/templates/secrets/analytics.yaml new file mode 100644 index 0000000..b80f4f7 --- /dev/null +++ b/templates/secrets/analytics.yaml @@ -0,0 +1,17 @@ +{{- if not .Values.secret.analytics.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.analytics" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.analytics }} +{{- if $value }} +{{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/secrets/dashboard.yaml b/templates/secrets/dashboard.yaml new file mode 100644 index 0000000..cd99197 --- /dev/null +++ b/templates/secrets/dashboard.yaml @@ -0,0 +1,19 @@ +{{- if .Values.secret.dashboard }} +{{- if not .Values.secret.dashboard.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.dashboard" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.dashboard }} +{{- if $value }} +{{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/secrets/db.yaml b/templates/secrets/db.yaml new file mode 100644 index 0000000..8cd9791 --- /dev/null +++ b/templates/secrets/db.yaml @@ -0,0 +1,18 @@ +{{- if not .Values.secret.db.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.db" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.db }} +{{- if $value }} +{{- if or (eq (typeOf $value) "string") (eq (typeOf $value) "numeric") }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} +{{- end }} + password_encoded: {{ .Values.secret.db.password | urlquery | b64enc }} +{{- end }} diff --git a/templates/secrets/jwt.yaml b/templates/secrets/jwt.yaml new file mode 100644 index 0000000..0b190a1 --- /dev/null +++ b/templates/secrets/jwt.yaml @@ -0,0 +1,19 @@ +{{- if .Values.secret.jwt }} +{{- if not .Values.secret.jwt.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.jwt" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.jwt }} +{{- if $value }} +{{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value | toString | b64enc }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/secrets/s3.yaml b/templates/secrets/s3.yaml new file mode 100644 index 0000000..7421da4 --- /dev/null +++ b/templates/secrets/s3.yaml @@ -0,0 +1,19 @@ +{{- if .Values.secret.s3 }} +{{- if not .Values.secret.s3.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.s3" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.s3 }} +{{- if $value }} +{{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value | toString | b64enc }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/secrets/smtp.yaml b/templates/secrets/smtp.yaml new file mode 100644 index 0000000..05c3579 --- /dev/null +++ b/templates/secrets/smtp.yaml @@ -0,0 +1,19 @@ +{{- if .Values.secret.smtp }} +{{- if not .Values.secret.smtp.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "supabase.secret.smtp" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +type: Opaque +data: +{{- range $key, $value := .Values.secret.smtp }} +{{- if $value }} +{{- if eq (typeOf $value) "string" }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/storage/_helpers.tpl b/templates/storage/_helpers.tpl new file mode 100644 index 0000000..9f90832 --- /dev/null +++ b/templates/storage/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.storage.name" -}} +{{- default (print .Chart.Name "-storage") .Values.storage.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.storage.fullname" -}} +{{- if .Values.storage.fullnameOverride }} +{{- .Values.storage.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-storage") .Values.storage.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.storage.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.storage.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.storage.serviceAccountName" -}} +{{- if .Values.storage.serviceAccount.create }} +{{- default (include "supabase.storage.fullname" .) .Values.storage.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.storage.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/storage/deployment.yaml b/templates/storage/deployment.yaml new file mode 100644 index 0000000..b045429 --- /dev/null +++ b/templates/storage/deployment.yaml @@ -0,0 +1,245 @@ +{{- if .Values.storage.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.storage.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.storage.autoscaling.enabled }} + replicas: {{ .Values.storage.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.storage.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.storage.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.storage.selectorLabels" . | nindent 8 }} + spec: + restartPolicy: Always + {{- with .Values.storage.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.storage.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.storage.podSecurityContext | nindent 8 }} + initContainers: + - name: init-db + image: postgres:15-alpine + imagePullPolicy: IfNotPresent + env: + - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} + - name: DB_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: DB_PORT + value: {{ .Values.analytics.environment.DB_PORT | quote }} + command: ["/bin/sh", "-c"] + args: + - | + until pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER); do + echo "Waiting for database to start..." + sleep 2 + done + - echo "Database is ready" + {{- if .Values.minio.enabled }} + - env: + - name: MINIO_ROOT_USER + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: keyId + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: accessKey + name: init-bucket + image: minio/mc + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - | + until /usr/bin/mc alias set supa-minio http://{{ include "supabase.minio.fullname" . }}:{{ .Values.minio.service.port }} $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD; do + echo "Waiting for minio to start..." + sleep 2 + done + /usr/bin/mc mb --ignore-existing supa-minio/stub + {{- end }} + containers: + - name: {{ include "supabase.storage.name" $ }} + securityContext: + {{- toYaml .Values.storage.securityContext | nindent 12 }} + image: "{{ .Values.storage.image.repository }}:{{ .Values.storage.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.storage.image.pullPolicy }} + env: + {{- range $key, $value := .Values.storage.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} + {{- end }} + {{- if .Values.rest.enabled }} + - name: POSTGREST_URL + value: http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }} + {{- end }} + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password + {{- end }} + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.password | default "password" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: password_encoded + {{- end }} + - name: DB_NAME + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.database | default "database" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: database + {{- end }} + - name: DATABASE_URL + value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD_ENC)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?search_path=auth&sslmode=$(DB_SSL) + - name: PGRST_JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.secret | default "secret" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: secret + {{- end }} + - name: ANON_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.anonKey | default "anonKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: anonKey + {{- end }} + - name: SERVICE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.serviceKey | default "serviceKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: serviceKey + {{- end }} + {{- if .Values.imgproxy.enabled }} + - name: IMGPROXY_URL + value: http://{{ include "supabase.imgproxy.fullname" . }}:{{ .Values.imgproxy.service.port | int }} + {{- end }} + {{- if eq (include "supabase.secret.s3.isValid" .) "true" }} + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + {{- if .Values.secret.s3.secretRef }} + name: {{ .Values.secret.s3.secretRef }} + key: {{ .Values.secret.s3.secretRefKey.keyId | default "keyId" }} + {{- else }} + name: {{ include "supabase.secret.s3" . }} + key: keyId + {{- end }} + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.s3.secretRef }} + name: {{ .Values.secret.s3.secretRef }} + key: {{ .Values.secret.s3.secretRefKey.keyId | default "accessKey" }} + {{- else }} + name: {{ include "supabase.secret.s3" . }} + key: accessKey + {{- end }} + {{- end }} + {{- if .Values.minio.enabled }} + - name: GLOBAL_S3_ENDPOINT + value: http://{{ include "supabase.minio.fullname" . }}:{{ default 9000 .Values.minio.service.port }} + {{- end }} + {{- with .Values.storage.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.storage.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 5000 + protocol: TCP + {{- with .Values.storage.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- with .Values.storage.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - mountPath: /var/lib/storage + name: storage-data + {{- with .Values.storage.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.storage.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.storage.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: storage-data + {{- if .Values.storage.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "supabase.storage.fullname" . }}-pvc + {{- else }} + emptyDir: + medium: "" + {{- end }} + {{- with .Values.storage.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/storage/service.yaml b/templates/storage/service.yaml new file mode 100644 index 0000000..738f2b1 --- /dev/null +++ b/templates/storage/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.storage.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.storage.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.storage.service.type }} + ports: + - port: {{ .Values.storage.service.port }} + targetPort: 5000 + protocol: TCP + name: http + selector: + {{- include "supabase.storage.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/storage/serviceaccount.yaml b/templates/storage/serviceaccount.yaml new file mode 100644 index 0000000..56df332 --- /dev/null +++ b/templates/storage/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.storage.enabled -}} +{{- if .Values.storage.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.storage.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.storage.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/storage/volume.yaml b/templates/storage/volume.yaml new file mode 100644 index 0000000..6455767 --- /dev/null +++ b/templates/storage/volume.yaml @@ -0,0 +1,25 @@ +{{- if .Values.storage.enabled -}} +{{- if .Values.storage.persistence.enabled -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "supabase.storage.fullname" . }}-pvc + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.storage.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.storage.persistence.storageClassName }} + storageClassName: {{ .Values.storage.persistence.storageClassName }} + {{- end }} + accessModes: + {{- range .Values.storage.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.storage.persistence.size | quote }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/studio/_helpers.tpl b/templates/studio/_helpers.tpl new file mode 100644 index 0000000..b4cda02 --- /dev/null +++ b/templates/studio/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.studio.name" -}} +{{- default (print .Chart.Name "-studio") .Values.studio.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.studio.fullname" -}} +{{- if .Values.studio.fullnameOverride }} +{{- .Values.studio.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-studio") .Values.studio.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.studio.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.studio.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.studio.serviceAccountName" -}} +{{- if .Values.studio.serviceAccount.create }} +{{- default (include "supabase.studio.fullname" .) .Values.studio.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.studio.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/studio/deployment.yaml b/templates/studio/deployment.yaml new file mode 100644 index 0000000..dc4d9f8 --- /dev/null +++ b/templates/studio/deployment.yaml @@ -0,0 +1,115 @@ +{{- if .Values.studio.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.studio.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + {{- if not .Values.studio.autoscaling.enabled }} + replicas: {{ .Values.studio.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.studio.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.studio.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "supabase.studio.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.studio.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.studio.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.studio.podSecurityContext | nindent 8 }} + containers: + - name: {{ include "supabase.studio.name" $ }} + securityContext: + {{- toYaml .Values.studio.securityContext | nindent 12 }} + image: "{{ .Values.studio.image.repository }}:{{ .Values.studio.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.studio.image.pullPolicy }} + env: + {{- range $key, $value := .Values.studio.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.kong.enabled }} + - name: SUPABASE_URL + value: http://{{ include "supabase.kong.fullname" . }}:{{ .Values.kong.service.port }} + {{- end }} + {{- if .Values.meta.enabled }} + - name: STUDIO_PG_META_URL + value: http://{{ include "supabase.meta.fullname" . }}:{{ .Values.meta.service.port }} + {{- end }} + - name: SUPABASE_ANON_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.anonKey | default "anonKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: anonKey + {{- end }} + - name: SUPABASE_SERVICE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.secret.jwt.secretRef }} + name: {{ .Values.secret.jwt.secretRef }} + key: {{ .Values.secret.jwt.secretRefKey.serviceKey | default "serviceKey" }} + {{- else }} + name: {{ include "supabase.secret.jwt" . }} + key: serviceKey + {{- end }} + {{- if .Values.analytics.enabled }} + - name: LOGFLARE_URL + value: http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }} + - name: LOGFLARE_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.analytics" . }} + key: apiKey + {{- end }} + {{- with .Values.studio.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.studio.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 3000 + protocol: TCP + {{- with .Values.studio.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.studio.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.studio.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.studio.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.studio.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.studio.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/studio/service.yaml b/templates/studio/service.yaml new file mode 100644 index 0000000..f7662ba --- /dev/null +++ b/templates/studio/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.studio.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.studio.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.studio.service.type }} + ports: + - port: {{ .Values.studio.service.port }} + targetPort: 3000 + protocol: TCP + name: http + selector: + {{- include "supabase.studio.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/templates/studio/serviceaccount.yaml b/templates/studio/serviceaccount.yaml new file mode 100644 index 0000000..dfe5897 --- /dev/null +++ b/templates/studio/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.studio.enabled -}} +{{- if .Values.studio.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.studio.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.studio.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/test/analytics.yaml b/templates/test/analytics.yaml new file mode 100644 index 0000000..b97c340 --- /dev/null +++ b/templates/test/analytics.yaml @@ -0,0 +1,26 @@ +{{- if .Values.analytics.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-analytics + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-analytics + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/health + echo "Sevice {{ include "supabase.analytics.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/auth.yaml b/templates/test/auth.yaml new file mode 100644 index 0000000..19a7fb1 --- /dev/null +++ b/templates/test/auth.yaml @@ -0,0 +1,26 @@ +{{- if .Values.auth.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-auth + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-auth + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/health + echo "Sevice {{ include "supabase.auth.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/db.yaml b/templates/test/db.yaml new file mode 100644 index 0000000..296bcc3 --- /dev/null +++ b/templates/test/db.yaml @@ -0,0 +1,44 @@ +{{- if .Values.db.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-db + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - command: + - /bin/sh + - -c + - | + pg_isready -h $(DB_HOST) -p $(DB_PORT) -U $(DB_USER) || $(echo "\e[0;31mFailed to connect to the database." && exit 1) + echo "Database is ready" + env: + - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} + - name: DB_USER + valueFrom: + secretKeyRef: + {{- if .Values.secret.db.secretRef }} + name: {{ .Values.secret.db.secretRef }} + key: {{ .Values.secret.db.secretRefKey.username | default "username" }} + {{- else }} + name: {{ include "supabase.secret.db" . }} + key: username + {{- end }} + - name: DB_PORT + value: {{ .Values.auth.environment.DB_PORT | quote }} + image: postgres:15-alpine + imagePullPolicy: IfNotPresent + name: test-db + restartPolicy: Never +{{- end }} diff --git a/templates/test/imgproxy.yaml b/templates/test/imgproxy.yaml new file mode 100644 index 0000000..b283dc8 --- /dev/null +++ b/templates/test/imgproxy.yaml @@ -0,0 +1,26 @@ +{{- if .Values.imgproxy.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-imgproxy + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-imgproxy + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.imgproxy.fullname" . }}:{{ .Values.imgproxy.service.port }}/health + echo "Sevice {{ include "supabase.imgproxy.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/kong.yaml b/templates/test/kong.yaml new file mode 100644 index 0000000..f8c741f --- /dev/null +++ b/templates/test/kong.yaml @@ -0,0 +1,40 @@ +{{- if .Values.kong.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-kong + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - env: + - name: DASHBOARD_USERNAME + valueFrom: + secretKeyRef: + key: username + name: {{ include "supabase.fullname" . }}-dashboard + - name: DASHBOARD_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: {{ include "supabase.fullname" . }}-dashboard + name: test-kong + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + echo "Attempting to access dashboard with provided credentials..." + curl -sL --fail \ + -o /dev/null \ + "http://${DASHBOARD_USERNAME}:${DASHBOARD_PASSWORD}@{{ include "supabase.kong.fullname" . }}:{{ .Values.kong.service.port }}" \ + || ( echo -e "\e[0;31mFailed to get a valid response." && exit 1 ) + echo "Successfully connected." + restartPolicy: Never +{{- end }} diff --git a/templates/test/meta.yaml b/templates/test/meta.yaml new file mode 100644 index 0000000..9aee7f5 --- /dev/null +++ b/templates/test/meta.yaml @@ -0,0 +1,26 @@ +{{- if .Values.meta.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-meta + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-meta + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.meta.fullname" . }}:{{ .Values.meta.service.port }}/health + echo "Sevice {{ include "supabase.meta.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/minio.yaml b/templates/test/minio.yaml new file mode 100644 index 0000000..b3c3db2 --- /dev/null +++ b/templates/test/minio.yaml @@ -0,0 +1,25 @@ +{{- if .Values.minio.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-minio + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-minio + image: kdevup/curljq + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.minio.fullname" . }}:{{ .Values.minio.service.port }}/minio/health/live + echo "Sevice {{ include "supabase.minio.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/realtime.yaml b/templates/test/realtime.yaml new file mode 100644 index 0000000..e373fde --- /dev/null +++ b/templates/test/realtime.yaml @@ -0,0 +1,26 @@ +{{- if .Values.realtime.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-realtime + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-realtime + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.realtime.fullname" . }}:{{ .Values.realtime.service.port }} + echo "Sevice {{ include "supabase.realtime.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/rest.yaml b/templates/test/rest.yaml new file mode 100644 index 0000000..927ffc7 --- /dev/null +++ b/templates/test/rest.yaml @@ -0,0 +1,26 @@ +{{- if .Values.rest.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-rest + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-rest + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.rest.fullname" . }}:{{ .Values.rest.service.port }} + echo "Sevice {{ include "supabase.rest.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/secretrefs.yaml b/templates/test/secretrefs.yaml new file mode 100644 index 0000000..51245ac --- /dev/null +++ b/templates/test/secretrefs.yaml @@ -0,0 +1,79 @@ +{{- if .Values.debug }} +{{- if .Values.debug.secretRef }} +{{- if .Values.secret.jwt.secretRef }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.jwt.secretRef }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install +type: Opaque +data: + anonKey: ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5QWdDaUFnSUNBaWNtOXNaU0k2SUNKaGJtOXVJaXdLSUNBZ0lDSnBjM01pT2lBaWMzVndZV0poYzJVdFpHVnRieUlzQ2lBZ0lDQWlhV0YwSWpvZ01UWTBNVGMyT1RJd01Dd0tJQ0FnSUNKbGVIQWlPaUF4TnprNU5UTTFOakF3Q24wLmRjX1g1aVJfVlBfcVQwenNpeWpfSV9PWjJUOUZ0UlUyQkJOV044QnU0R0U= + serviceKey: ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5QWdDaUFnSUNBaWNtOXNaU0k2SUNKelpYSjJhV05sWDNKdmJHVWlMQW9nSUNBZ0ltbHpjeUk2SUNKemRYQmhZbUZ6WlMxa1pXMXZJaXdLSUNBZ0lDSnBZWFFpT2lBeE5qUXhOelk1TWpBd0xBb2dJQ0FnSW1WNGNDSTZJREUzT1RrMU16VTJNREFLZlEuRGFZbE5Fb1VyckVuMklnN3RxaWJTLVBISzV2Z3VzYmNibzdYMzZYVnQ0UQ== + secret: eW91ci1zdXBlci1zZWNyZXQtand0LXRva2VuLXdpdGgtYXQtbGVhc3QtMzItY2hhcmFjdGVycy1sb25n +{{- end }} +{{- if .Values.secret.smtp.secretRef }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.smtp.secretRef }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install +type: Opaque +data: + username: eW91ci1tYWlsQGV4YW1wbGUuY29t + password: ZXhhbXBsZTEyMzQ1Ng== +{{- end }} +{{- if .Values.secret.dashboard.secretRef }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.dashboard.secretRef }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install +type: Opaque +data: + username: c3VwYWJhc2U= + password: dGhpc19wYXNzd29yZF9pc19pbnNlY3VyZV9hbmRfc2hvdWxkX2JlX3VwZGF0ZWQ= +{{- end }} +{{- if .Values.secret.db.secretRef }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.db.secretRef }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install +type: Opaque +data: + username: cG9zdGdyZXM= + password: ZXhhbXBsZTEyMzQ1Ng== + database: cG9zdGdyZXM= +{{- end }} +{{- if .Values.secret.analytics.secretRef }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.analytics.secretRef }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install +type: Opaque +data: + apiKey: eW91ci1zdXBlci1zZWNyZXQtYW5kLWxvbmctbG9nZmxhcmUta2V5 +{{- end }} +{{- end }} +{{- end }} diff --git a/templates/test/storage.yaml b/templates/test/storage.yaml new file mode 100644 index 0000000..db129ff --- /dev/null +++ b/templates/test/storage.yaml @@ -0,0 +1,26 @@ +{{- if .Values.storage.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-storage + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-storage + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.storage.fullname" . }}:{{ .Values.storage.service.port }}/status + echo "Sevice {{ include "supabase.storage.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/test/studio.yaml b/templates/test/studio.yaml new file mode 100644 index 0000000..7e5082d --- /dev/null +++ b/templates/test/studio.yaml @@ -0,0 +1,26 @@ +{{- if .Values.studio.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "supabase.fullname" . }}-test-studio + labels: + {{- include "supabase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + ttlSecondsAfterFinished: 100 + template: + spec: + containers: + - name: test-studio + image: kdevup/curljq + imagePullPolicy: IfNotPresent + command: + - /bin/bash + - -c + - | + curl -sfo /dev/null \ + http://{{ include "supabase.studio.fullname" . }}:{{ .Values.studio.service.port }}/api/profile + echo "Sevice {{ include "supabase.studio.fullname" . }} is healthy." + restartPolicy: Never +{{- end }} diff --git a/templates/vector/_helpers.tpl b/templates/vector/_helpers.tpl new file mode 100644 index 0000000..bd861b4 --- /dev/null +++ b/templates/vector/_helpers.tpl @@ -0,0 +1,43 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "supabase.vector.name" -}} +{{- default (print .Chart.Name "-vector") .Values.vector.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "supabase.vector.fullname" -}} +{{- if .Values.vector.fullnameOverride }} +{{- .Values.vector.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default (print .Chart.Name "-vector") .Values.vector.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "supabase.vector.selectorLabels" -}} +app.kubernetes.io/name: {{ include "supabase.vector.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "supabase.vector.serviceAccountName" -}} +{{- if .Values.vector.serviceAccount.create }} +{{- default (include "supabase.vector.fullname" .) .Values.vector.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.vector.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/templates/vector/config.yaml b/templates/vector/config.yaml new file mode 100644 index 0000000..df2e897 --- /dev/null +++ b/templates/vector/config.yaml @@ -0,0 +1,255 @@ +{{- if .Values.vector.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "supabase.vector.fullname" . }}-config + labels: + {{- include "supabase.labels" . | nindent 4 }} +data: + secret.sh: | + #!/bin/sh + cat << EOF + { + "logflare_api_key": { + "value": "$LOGFLARE_API_KEY", + "error": null + } + } + EOF + vector.yml: | + secret: + credentials: + type: exec + command: + - /etc/vector/secret.sh + + api: + enabled: true + address: 0.0.0.0:{{ .Values.vector.service.port }} + + sources: + kubernetes_host: + type: kubernetes_logs + extra_label_selector: app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/name!={{ include "supabase.vector.name" . }} + + transforms: + project_logs: + type: remap + inputs: + - kubernetes_host + source: |- + .project = "default" + .event_message = del(.message) + .appname = del(.kubernetes.container_name) + del(.file) + del(.kubernetes) + del(.source_type) + del(.stream) + router: + type: route + inputs: + - project_logs + route: + kong: '.appname == {{ include "supabase.kong.name" . | quote }}' + auth: '.appname == {{ include "supabase.auth.name" . | quote }}' + rest: '.appname == {{ include "supabase.rest.name" . | quote }}' + realtime: '.appname == {{ include "supabase.realtime.name" . | quote }}' + storage: '.appname == {{ include "supabase.storage.name" . | quote }}' + functions: '.appname == {{ include "supabase.functions.name" . | quote }}' + db: '.appname == {{ include "supabase.db.name" . | quote }}' + # Ignores non nginx errors since they are related with kong booting up + kong_logs: + type: remap + inputs: + - router.kong + source: |- + req, err = parse_nginx_log(.event_message, "combined") + if err == null { + .timestamp = req.timestamp + .metadata.request.headers.referer = req.referer + .metadata.request.headers.user_agent = req.agent + .metadata.request.headers.cf_connecting_ip = req.client + .metadata.request.method = req.method + .metadata.request.path = req.path + .metadata.request.protocol = req.protocol + .metadata.response.status_code = req.status + } + if err != null { + abort + } + # Ignores non nginx errors since they are related with kong booting up + kong_err: + type: remap + inputs: + - router.kong + source: |- + .metadata.request.method = "GET" + .metadata.response.status_code = 200 + parsed, err = parse_nginx_log(.event_message, "error") + if err == null { + .timestamp = parsed.timestamp + .severity = parsed.severity + .metadata.request.host = parsed.host + .metadata.request.headers.cf_connecting_ip = parsed.client + url, err = split(parsed.request, " ") + if err == null { + .metadata.request.method = url[0] + .metadata.request.path = url[1] + .metadata.request.protocol = url[2] + } + } + if err != null { + abort + } + # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency. + auth_logs: + type: remap + inputs: + - router.auth + source: |- + parsed, err = parse_json(.event_message) + if err == null { + .metadata.timestamp = parsed.time + .metadata = merge!(.metadata, parsed) + } + # PostgREST logs are structured so we separate timestamp from message using regex + rest_logs: + type: remap + inputs: + - router.rest + source: |- + parsed, err = parse_regex(.event_message, r'^(?P