diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c3028e0790f0af74fa16e316a06fd8d0ab57c325 --- /dev/null +++ b/.github/workflows/lint-test.yaml @@ -0,0 +1,45 @@ +name: Lint and Test Charts + +on: pull_request + +jobs: + lint-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v3 + with: + version: v3.12.1 + + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + check-latest: true + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.6.0 + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Run chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' + run: ct lint --validate-maintainers=false --target-branch ${{ github.event.repository.default_branch }} + + - name: Create kind cluster + if: steps.list-changed.outputs.changed == 'true' + uses: helm/kind-action@v1.8.0 + + - name: Run chart-testing (install) + if: steps.list-changed.outputs.changed == 'true' + run: ct install --target-branch ${{ github.event.repository.default_branch }} diff --git a/charts/supabase/Chart.yaml b/charts/supabase/Chart.yaml index caa158b25f3494b1627fe46594d6c238322cc542..d7b9a1fc9f54ac603cc518e7ae9bdee9b7c5eb9a 100644 --- a/charts/supabase/Chart.yaml +++ b/charts/supabase/Chart.yaml @@ -15,7 +15,7 @@ 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.0.3 +version: 0.1.0 # 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 @@ -23,3 +23,6 @@ version: 0.0.3 # 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/charts/supabase/README.md b/charts/supabase/README.md index 8d2f9f08a710354ddc167c83b002781ff970867d..28c59665a35bb8a20cbb6055274c36719875d839 100644 --- a/charts/supabase/README.md +++ b/charts/supabase/README.md @@ -21,80 +21,56 @@ git clone https://github.com/supabase-community/supabase-kubernetes # Switch to charts directory cd supabase-kubernetes/charts/supabase/ -# Create JWT secret -kubectl -n default create secret generic demo-supabase-jwt \ - --from-literal=anonKey='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICAgInJvbGUiOiAiYW5vbiIsCiAgICAiaXNzIjogInN1cGFiYXNlIiwKICAgICJpYXQiOiAxNjc1NDAwNDAwLAogICAgImV4cCI6IDE4MzMxNjY4MDAKfQ.ztuiBzjaVoFHmoljUXWmnuDN6QU2WgJICeqwyzyZO88' \ - --from-literal=serviceKey='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ewogICAgInJvbGUiOiAic2VydmljZV9yb2xlIiwKICAgICJpc3MiOiAic3VwYWJhc2UiLAogICAgImlhdCI6IDE2NzU0MDA0MDAsCiAgICAiZXhwIjogMTgzMzE2NjgwMAp9.qNsmXzz4tG7eqJPh1Y58DbtIlJBauwpqx39UF-MwM8k' \ - --from-literal=secret='abcdefghijklmnopqrstuvwxyz123456' - -# Create SMTP secret -kubectl -n default create secret generic demo-supabase-smtp \ - --from-literal=username='your-mail@example.com' \ - --from-literal=password='example123456' - -# Create DB secret -kubectl -n default create secret generic demo-supabase-db \ - --from-literal=username='postgres' \ - --from-literal=password='example123456' - # Install the chart -helm -n default install demo -f values.example.yaml . +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 -n default get pod +kubectl get pod -l app.kubernetes.io/instance=demo NAME READY STATUS RESTARTS AGE -demo-supabase-auth-78547c5c8d-chkbm 1/1 Running 2 (40s ago) 47s -demo-supabase-db-5bc75fbf56-4cxcv 1/1 Running 0 47s -demo-supabase-kong-8c666695f-5vzwt 1/1 Running 0 47s -demo-supabase-meta-6779677c7-s77qq 1/1 Running 0 47s -demo-supabase-realtime-6b55986d7d-csnr7 1/1 Running 0 47s -demo-supabase-rest-5d864469d-bk5rv 1/1 Running 0 47s -demo-supabase-storage-6c878dcbd4-zzzcv 1/1 Running 0 47s +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 ``` -### Tunnel with Minikube - -When the installation will be complete you'll be able to create a tunnel using minikube: +### 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 -# First, enable the ingress addon in Minikube -minikube addons enable ingress - -# Then enable the tunnel (will need sudo credentials because you are opening Port 80/443 on your local machine) -minikube tunnel +# This will redirect request for example.com to the minikube IP +<minikube-ip> example.com ``` - -If you just use the `value.example.yaml` file, you can access the API or the Studio App using the following endpoints: - -- <http://api.localhost> -- <http://studio.localhost> +Open http://example.com in your browser. ### Uninstall ```Bash # Uninstall Helm chart -helm -n default uninstall demo +helm uninstall demo -# Delete secrets -kubectl -n default delete secret demo-supabase-db -kubectl -n default delete secret demo-supabase-jwt -kubectl -n default delete secret demo-supabase-smtp +# 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`: -- `JWT_SECRET_NAME`: Reference to Kubernetes secret with JWT secret data `secret`, `anonKey` & `serviceKey` -- `SMTP_SECRET_NAME`: Reference to Kubernetes secret with SMTP credentials `username` & `password` -- `DB_SECRET_NAME`: Reference to Kubernetes secret with Postgres credentials `username` & `password` - `RELEASE_NAME`: Name used for helm release -- `NAMESPACE`: Namespace used for the helm release -- `API.EXAMPLE.COM` URL to Kong API - `STUDIO.EXAMPLE.COM` URL to Studio If you want to use mail, consider to adjust the following values in `values.yaml`: @@ -109,17 +85,11 @@ If you want to use mail, consider to adjust the following values in `values.yaml We encourage you to use your own JWT keys by generating a new Kubernetes secret and reference it in `values.yaml`: ```yaml +secret: jwt: - secretName: "JWT_SECRET_NAME" -``` - -The secret can be created with kubectl via command-line: - -```bash -kubectl -n NAMESPACE create secret generic JWT_SECRET_NAME \ - --from-literal=secret='JWT_TOKEN_AT_LEAST_32_CHARACTERS_LONG' \ - --from-literal=anonKey='JWT_ANON_KEY' \ - --from-literal=serviceKey='JWT_SERVICE_KEY' + anonKey: <new-anon-jwt> + serviceKey: <new-service-role-jwt> + secret: <jwt-secret> ``` > 32 characters long secret can be generated with `openssl rand 64 | base64` @@ -130,16 +100,10 @@ kubectl -n NAMESPACE create secret generic JWT_SECRET_NAME \ Connection credentials for the SMTP mail server will also be provided via Kubernetes secret referenced in `values.yaml`: ```yaml +secret: smtp: - secretName: "SMTP_SECRET_NAME" -``` - -The secret can be created with kubectl via command-line: - -```bash -kubectl -n NAMESPACE create secret generic SMTP_SECRET_NAME \ - --from-literal=username='SMTP_USER' \ - --from-literal=password='SMTP_PASSWORD' + username: <your-smtp-username> + password: <your-smtp-password> ``` ### DB Secret @@ -147,19 +111,65 @@ kubectl -n NAMESPACE create secret generic SMTP_SECRET_NAME \ DB credentials will also be stored in a Kubernetes secret and referenced in `values.yaml`: ```yaml +secret: db: - secretName: "DB_SECRET_NAME" + username: <db-username> + password: <db-password> + database: <supabase-database-name> ``` The secret can be created with kubectl via command-line: -```bash -kubectl -n NAMESPACE create secret generic DB_SECRET_NAME \ - --from-literal=username='DB_USER' \ - --from-literal=password='PW_USER' +> 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 ``` -> If you depend on database providers like [StackGres](https://stackgres.io/) or [Postgres Operator](https://github.com/zalando/postgres-operator) you only need to reference the already existing secret in `values.yaml`. +### 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 @@ -173,6 +183,48 @@ But here are the important points you have to think about: - 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 @@ -187,3 +239,36 @@ kong: 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/charts/supabase/ci/example-values.yaml b/charts/supabase/ci/example-values.yaml new file mode 120000 index 0000000000000000000000000000000000000000..6b4faa48f1210a948887dae9f51de1119167257b --- /dev/null +++ b/charts/supabase/ci/example-values.yaml @@ -0,0 +1 @@ +../values.example.yaml \ No newline at end of file diff --git a/charts/supabase/templates/NOTES.txt b/charts/supabase/templates/NOTES.txt new file mode 100644 index 0000000000000000000000000000000000000000..f06631a85ed8215f9bdfd250dbdc71ce27b9159a --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/analytics/_helpers.tpl b/charts/supabase/templates/analytics/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..d960a1d6a46616bd098a048f952728d227ea77ea --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/analytics/deployment.yaml b/charts/supabase/templates/analytics/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3f11bb921c87fc41ee6a870db23ab0a74c14a81d --- /dev/null +++ b/charts/supabase/templates/analytics/deployment.yaml @@ -0,0 +1,154 @@ +{{- 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: + name: {{ include "supabase.secret.db" . }} + key: username + - 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: + name: {{ include "supabase.secret.db" . }} + key: password + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password_encoded + - name: DB_DATABASE + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database + - name: LOGFLARE_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.analytics" . }} + key: apiKey + {{- 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/charts/supabase/templates/analytics/gcloud.secret.yaml b/charts/supabase/templates/analytics/gcloud.secret.yaml new file mode 100644 index 0000000000000000000000000000000000000000..71a645ada0caad1232329188ad21e6b0af966f0d --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/analytics/service.yaml b/charts/supabase/templates/analytics/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d8f43cb1227f88ab4ab62d8f66c22efea5f72b32 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/analytics/serviceaccount.yaml b/charts/supabase/templates/analytics/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..73c72f20469804eae75586e5f58e2f713715b46f --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/auth/config.yaml b/charts/supabase/templates/auth/config.yaml deleted file mode 100644 index 3b0ec639326dd7fbec06274de21d5814045e89f2..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/auth/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.auth.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.auth.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.auth.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/auth/deployment.yaml b/charts/supabase/templates/auth/deployment.yaml index 947e78126a1361546f9766d943a0c0debd9c0da8..b2ee0b42cbdc4f5522135fa946f4b4567b41b4a2 100644 --- a/charts/supabase/templates/auth/deployment.yaml +++ b/charts/supabase/templates/auth/deployment.yaml @@ -34,14 +34,18 @@ spec: imagePullPolicy: IfNotPresent env: - name: DB_HOST + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} value: {{ .Values.auth.environment.DB_HOST | quote }} - - name: DB_PORT - value: {{ .Values.auth.environment.DB_PORT | quote }} + {{- end }} - name: DB_USER valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: username + - name: DB_PORT + value: {{ .Values.auth.environment.DB_PORT | quote }} command: ["/bin/sh", "-c"] args: - | @@ -59,40 +63,54 @@ spec: env: {{- range $key, $value := .Values.auth.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.auth.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} {{- end }} - - name: DB_USER - valueFrom: - secretKeyRef: - name: {{ .Values.db.secretName }} - key: username - name: DB_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password_encoded + - name: DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database - name: GOTRUE_DB_DATABASE_URL - value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?search_path=auth&sslmode=$(DB_SSL) + 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: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: secret - name: GOTRUE_SMTP_USER valueFrom: secretKeyRef: - name: {{ .Values.smtp.secretName }} + name: {{ include "supabase.secret.smtp" . }} key: username - name: GOTRUE_SMTP_PASS valueFrom: secretKeyRef: - name: {{ .Values.smtp.secretName }} + name: {{ include "supabase.secret.smtp" . }} key: password + {{- with .Values.auth.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.auth.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - name: http containerPort: 9999 diff --git a/charts/supabase/templates/auth/ingress.yaml b/charts/supabase/templates/auth/ingress.yaml deleted file mode 100644 index 799e9928dc7e35bdacd79b8ca5048f9af1a1478c..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/auth/ingress.yaml +++ /dev/null @@ -1,55 +0,0 @@ -{{- if .Values.auth.ingress.enabled -}} -{{- $fullName := include "supabase.auth.fullname" . -}} -{{- $svcPort := .Values.auth.service.port -}} -{{- if and .Values.auth.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.auth.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.auth.ingress.annotations "kubernetes.io/ingress.class" .Values.auth.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.auth.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.auth.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.auth.ingress.className }} - {{- end }} - {{- if .Values.auth.ingress.tls }} - tls: - {{- range .Values.auth.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.auth.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 }} \ No newline at end of file diff --git a/charts/supabase/templates/db/deployment.yaml b/charts/supabase/templates/db/deployment.yaml index c9c3c943c157a045abf987914041aad74b4110fe..13da44b807f5c35314274becbf64be08ca137a30 100644 --- a/charts/supabase/templates/db/deployment.yaml +++ b/charts/supabase/templates/db/deployment.yaml @@ -28,6 +28,30 @@ spec: 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: @@ -37,33 +61,53 @@ spec: env: {{- range $key, $value := .Values.db.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.db.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} {{- end }} - name: POSTGRES_USER valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: username + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: PGDATABASE + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database + {{- 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: custom-init-scripts + - mountPath: /docker-entrypoint-initdb.d + name: initdb-scripts-data {{- with .Values.db.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} {{- if .Values.db.persistence.enabled }} - - mountPath: {{ .Values.db.storage.mountPath }} + - mountPath: /var/lib/postgresql/data name: postgres-volume {{- end }} {{- with .Values.db.resources }} @@ -71,9 +115,15 @@ spec: {{- 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 }} diff --git a/charts/supabase/templates/db/ingress.yaml b/charts/supabase/templates/db/ingress.yaml deleted file mode 100644 index 2d02bdd32e11a2bc37a67470691e6580d77b081f..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/db/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.db.enabled -}} -{{- if .Values.db.ingress.enabled -}} -{{- $fullName := include "supabase.db.fullname" . -}} -{{- $svcPort := .Values.db.service.port -}} -{{- if and .Values.db.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.db.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.db.ingress.annotations "kubernetes.io/ingress.class" .Values.db.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.db.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.db.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.db.ingress.className }} - {{- end }} - {{- if .Values.db.ingress.tls }} - tls: - {{- range .Values.db.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.db.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/charts/supabase/templates/db/initdb.config.yaml b/charts/supabase/templates/db/initdb.config.yaml index 98ae0e4f2dc9f174306a61009b6180f8f361811c..25f61ad23fac72cd7b215433d5876a6f24efabd4 100644 --- a/charts/supabase/templates/db/initdb.config.yaml +++ b/charts/supabase/templates/db/initdb.config.yaml @@ -6,388 +6,238 @@ metadata: labels: {{- include "supabase.labels" . | nindent 4 }} data: - 01-initial-schema.sql: | - -- Set up realtime - create schema if not exists realtime; - -- create publication supabase_realtime; -- defaults to empty publication - create publication supabase_realtime; - create schema if not exists _realtime; - create schema if not exists realtime; - - -- Supabase super admin - create user supabase_admin; - alter user supabase_admin with superuser createdb createrole replication bypassrls; - - -- Extension namespacing - create schema if not exists extensions; - create extension if not exists "uuid-ossp" with schema extensions; - create extension if not exists pgcrypto with schema extensions; - create extension if not exists pgjwt with schema extensions; - - -- Set up auth roles for the developer - create role anon nologin noinherit; - create role authenticated nologin noinherit; -- "logged in" user: web_user, app_user, etc - create role service_role nologin noinherit bypassrls; -- allow developers to create JWT's that bypass their policies - - create user authenticator noinherit; - grant anon to authenticator; - grant authenticated to authenticator; - grant service_role to authenticator; - grant supabase_admin to authenticator; - - grant usage on schema public to postgres, anon, authenticated, service_role; - alter default privileges in schema public grant all on tables to postgres, anon, authenticated, service_role; - alter default privileges in schema public grant all on functions to postgres, anon, authenticated, service_role; - alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role; - - -- Allow Extensions to be used in the API - grant usage on schema extensions to postgres, anon, authenticated, service_role; - - -- Set up namespacing - alter user supabase_admin SET search_path TO public, extensions; -- don't include the "auth" schema - - -- These are required so that the users receive grants whenever "supabase_admin" creates tables/function - alter default privileges for user supabase_admin in schema public grant all - on sequences to postgres, anon, authenticated, service_role; - alter default privileges for user supabase_admin in schema public grant all - on tables to postgres, anon, authenticated, service_role; - alter default privileges for user supabase_admin in schema public grant all - on functions to postgres, anon, authenticated, service_role; - - -- Set short statement/query timeouts for API roles - alter role anon set statement_timeout = '3s'; - alter role authenticated set statement_timeout = '8s'; - 02-auth-schema.sql: | - CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION supabase_admin; - - -- auth.users definition - - CREATE TABLE auth.users ( - instance_id uuid NULL, - id uuid NOT NULL UNIQUE, - aud varchar(255) NULL, - "role" varchar(255) NULL, - email varchar(255) NULL UNIQUE, - encrypted_password varchar(255) NULL, - confirmed_at timestamptz NULL, - invited_at timestamptz NULL, - confirmation_token varchar(255) NULL, - confirmation_sent_at timestamptz NULL, - recovery_token varchar(255) NULL, - recovery_sent_at timestamptz NULL, - email_change_token varchar(255) NULL, - email_change varchar(255) NULL, - email_change_sent_at timestamptz NULL, - last_sign_in_at timestamptz NULL, - raw_app_meta_data jsonb NULL, - raw_user_meta_data jsonb NULL, - is_super_admin bool NULL, - created_at timestamptz NULL, - updated_at timestamptz NULL, - CONSTRAINT users_pkey PRIMARY KEY (id) - ); - CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, email); - CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); - comment on table auth.users is 'Auth: Stores user login data within a secure schema.'; - - -- auth.refresh_tokens definition - - CREATE TABLE auth.refresh_tokens ( - instance_id uuid NULL, - id bigserial NOT NULL, - "token" varchar(255) NULL, - user_id varchar(255) NULL, - revoked bool NULL, - created_at timestamptz NULL, - updated_at timestamptz NULL, - CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id) - ); - CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); - CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); - CREATE INDEX refresh_tokens_token_idx ON auth.refresh_tokens USING btree (token); - comment on table auth.refresh_tokens is 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; - - -- auth.instances definition - - CREATE TABLE auth.instances ( - id uuid NOT NULL, - uuid uuid NULL, - raw_base_config text NULL, - created_at timestamptz NULL, - updated_at timestamptz NULL, - CONSTRAINT instances_pkey PRIMARY KEY (id) - ); - comment on table auth.instances is 'Auth: Manages users across multiple sites.'; - - -- auth.audit_log_entries definition - - CREATE TABLE auth.audit_log_entries ( - instance_id uuid NULL, - id uuid NOT NULL, - payload json NULL, - created_at timestamptz NULL, - CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id) - ); - CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); - comment on table auth.audit_log_entries is 'Auth: Audit trail for user actions.'; - - -- auth.schema_migrations definition - - CREATE TABLE auth.schema_migrations ( - "version" varchar(255) NOT NULL, - CONSTRAINT schema_migrations_pkey PRIMARY KEY ("version") - ); - comment on table auth.schema_migrations is 'Auth: Manages updates to the auth system.'; - - INSERT INTO auth.schema_migrations (version) - VALUES ('20171026211738'), - ('20171026211808'), - ('20171026211834'), - ('20180103212743'), - ('20180108183307'), - ('20180119214651'), - ('20180125194653'); - - create or replace function auth.uid() - returns uuid - language sql stable - as $$ - select - coalesce( - current_setting('request.jwt.claim.sub', true), - (current_setting('request.jwt.claims', true)::jsonb ->> 'sub') - )::uuid - $$; - - create or replace function auth.role() - returns text - language sql stable - as $$ - select - coalesce( - current_setting('request.jwt.claim.role', true), - (current_setting('request.jwt.claims', true)::jsonb ->> 'role') - )::text - $$; + 99-jwt.sql: | + \set jwt_secret `echo "$JWT_SECRET"` + \set jwt_exp `echo "$JWT_EXP"` - create or replace function auth.email() - returns text - language sql stable - as $$ - select - coalesce( - current_setting('request.jwt.claim.email', true), - (current_setting('request.jwt.claims', true)::jsonb ->> 'email') - )::text - $$; + 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"` - -- usage on auth functions to API roles - GRANT USAGE ON SCHEMA auth TO anon, authenticated, service_role; + create schema if not exists _analytics; + alter schema _analytics owner to :pguser; + 99-realtime.sql: | + \set pguser `echo "$POSTGRES_USER"` - -- Supabase super admin - CREATE USER supabase_auth_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; - GRANT ALL PRIVILEGES ON SCHEMA auth TO supabase_auth_admin; - GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO supabase_auth_admin; - GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA auth TO supabase_auth_admin; - ALTER USER supabase_auth_admin SET search_path = "auth"; - ALTER table "auth".users OWNER TO supabase_auth_admin; - ALTER table "auth".refresh_tokens OWNER TO supabase_auth_admin; - ALTER table "auth".audit_log_entries OWNER TO supabase_auth_admin; - ALTER table "auth".instances OWNER TO supabase_auth_admin; - ALTER table "auth".schema_migrations OWNER TO supabase_auth_admin; - - ALTER FUNCTION "auth"."uid" OWNER TO supabase_auth_admin; - ALTER FUNCTION "auth"."role" OWNER TO supabase_auth_admin; - ALTER FUNCTION "auth"."email" OWNER TO supabase_auth_admin; - GRANT EXECUTE ON FUNCTION "auth"."uid"() TO PUBLIC; - GRANT EXECUTE ON FUNCTION "auth"."role"() TO PUBLIC; - GRANT EXECUTE ON FUNCTION "auth"."email"() TO PUBLIC; - - 03-storage-schema.sql: | - CREATE SCHEMA IF NOT EXISTS storage AUTHORIZATION supabase_admin; - - grant usage on schema storage to postgres, anon, authenticated, service_role; - alter default privileges in schema storage grant all on tables to postgres, anon, authenticated, service_role; - alter default privileges in schema storage grant all on functions to postgres, anon, authenticated, service_role; - alter default privileges in schema storage grant all on sequences to postgres, anon, authenticated, service_role; - - CREATE TABLE "storage"."buckets" ( - "id" text not NULL, - "name" text NOT NULL, - "owner" uuid, - "created_at" timestamptz DEFAULT now(), - "updated_at" timestamptz DEFAULT now(), - CONSTRAINT "buckets_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"), - PRIMARY KEY ("id") - ); - CREATE UNIQUE INDEX "bname" ON "storage"."buckets" USING BTREE ("name"); - - CREATE TABLE "storage"."objects" ( - "id" uuid NOT NULL DEFAULT extensions.uuid_generate_v4(), - "bucket_id" text, - "name" text, - "owner" uuid, - "created_at" timestamptz DEFAULT now(), - "updated_at" timestamptz DEFAULT now(), - "last_accessed_at" timestamptz DEFAULT now(), - "metadata" jsonb, - CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"), - CONSTRAINT "objects_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"), - PRIMARY KEY ("id") - ); - CREATE UNIQUE INDEX "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name"); - CREATE INDEX name_prefix_search ON storage.objects(name text_pattern_ops); - - ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; - - CREATE FUNCTION storage.foldername(name text) - RETURNS text[] - LANGUAGE plpgsql - AS $function$ - DECLARE - _parts text[]; - BEGIN - select string_to_array(name, '/') into _parts; - return _parts[1:array_length(_parts,1)-1]; - END - $function$; - - CREATE FUNCTION storage.filename(name text) - RETURNS text - LANGUAGE plpgsql - AS $function$ - DECLARE - _parts text[]; - BEGIN - select string_to_array(name, '/') into _parts; - return _parts[array_length(_parts,1)]; - END - $function$; - - CREATE FUNCTION storage.extension(name text) - RETURNS text - LANGUAGE plpgsql - AS $function$ - DECLARE - _parts text[]; - _filename text; - BEGIN - select string_to_array(name, '/') into _parts; - select _parts[array_length(_parts,1)] into _filename; - -- @todo return the last part instead of 2 - return split_part(_filename, '.', 2); - END - $function$; - - CREATE FUNCTION storage.search(prefix text, bucketname text, limits int DEFAULT 100, levels int DEFAULT 1, offsets int DEFAULT 0) - RETURNS TABLE ( - name text, - id uuid, - updated_at TIMESTAMPTZ, - created_at TIMESTAMPTZ, - last_accessed_at TIMESTAMPTZ, - metadata jsonb - ) - LANGUAGE plpgsql - AS $function$ - DECLARE - _bucketId text; - BEGIN - -- will be replaced by migrations when server starts - -- saving space for cloud-init - END - $function$; - - -- create migrations table - -- https://github.com/ThomWright/postgres-migrations/blob/master/src/migrations/0_create-migrations-table.sql - -- we add this table here and not let it be auto-created so that the permissions are properly applied to it - CREATE TABLE IF NOT EXISTS storage.migrations ( - id integer PRIMARY KEY, - name varchar(100) UNIQUE NOT NULL, - hash varchar(40) NOT NULL, -- sha1 hex encoded hash of the file name and contents, to ensure it hasn't been altered since applying the migration - executed_at timestamp DEFAULT current_timestamp - ); - - CREATE USER supabase_storage_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION; - GRANT ALL PRIVILEGES ON SCHEMA storage TO supabase_storage_admin; - GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO supabase_storage_admin; - GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO supabase_storage_admin; - ALTER USER supabase_storage_admin SET search_path = "storage"; - ALTER table "storage".objects owner to supabase_storage_admin; - ALTER table "storage".buckets owner to supabase_storage_admin; - ALTER table "storage".migrations OWNER TO supabase_storage_admin; - ALTER function "storage".foldername(text) owner to supabase_storage_admin; - ALTER function "storage".filename(text) owner to supabase_storage_admin; - ALTER function "storage".extension(text) owner to supabase_storage_admin; - ALTER function "storage".search(text,text,int,int,int) owner to supabase_storage_admin; - - 04-post-setup.sql: | - ALTER ROLE postgres SET search_path TO "\$user",public,extensions; - CREATE OR REPLACE FUNCTION extensions.notify_api_restart() - RETURNS event_trigger - LANGUAGE plpgsql - AS $$ - BEGIN - NOTIFY ddl_command_end; - END; - $$; - CREATE EVENT TRIGGER api_restart ON ddl_command_end - EXECUTE PROCEDURE extensions.notify_api_restart(); - COMMENT ON FUNCTION extensions.notify_api_restart IS 'Sends a notification to the API to restart. If your database schema has changed, this is required so that Supabase can rebuild the relationships.'; - - -- Trigger for pg_cron - CREATE OR REPLACE FUNCTION extensions.grant_pg_cron_access() - RETURNS event_trigger - LANGUAGE plpgsql - AS $$ - DECLARE - schema_is_cron bool; - BEGIN - schema_is_cron = ( - SELECT n.nspname = 'cron' - FROM pg_event_trigger_ddl_commands() AS ev - LEFT JOIN pg_catalog.pg_namespace AS n - ON ev.objid = n.oid - ); - - IF schema_is_cron - THEN - grant usage on schema cron to postgres with grant option; - - alter default privileges in schema cron grant all on tables to postgres with grant option; - alter default privileges in schema cron grant all on functions to postgres with grant option; - alter default privileges in schema cron grant all on sequences to postgres with grant option; - - alter default privileges for user supabase_admin in schema cron grant all - on sequences to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on tables to postgres with grant option; - alter default privileges for user supabase_admin in schema cron grant all - on functions to postgres with grant option; - - grant all privileges on all tables in schema cron to postgres with grant option; - - END IF; - - END; - $$; - CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end WHEN TAG in ('CREATE SCHEMA') - EXECUTE PROCEDURE extensions.grant_pg_cron_access(); - COMMENT ON FUNCTION extensions.grant_pg_cron_access IS 'Grants access to pg_cron'; - - -- Supabase dashboard user - CREATE ROLE dashboard_user NOSUPERUSER CREATEDB CREATEROLE REPLICATION; - GRANT ALL ON DATABASE postgres TO dashboard_user; - GRANT ALL ON SCHEMA auth TO dashboard_user; - GRANT ALL ON SCHEMA extensions TO dashboard_user; - GRANT ALL ON SCHEMA storage TO dashboard_user; - GRANT ALL ON ALL TABLES IN SCHEMA auth TO dashboard_user; - GRANT ALL ON ALL TABLES IN SCHEMA extensions TO dashboard_user; - -- GRANT ALL ON ALL TABLES IN SCHEMA storage TO dashboard_user; - GRANT ALL ON ALL SEQUENCES IN SCHEMA auth TO dashboard_user; - GRANT ALL ON ALL SEQUENCES IN SCHEMA storage TO dashboard_user; - GRANT ALL ON ALL SEQUENCES IN SCHEMA extensions TO dashboard_user; - GRANT ALL ON ALL ROUTINES IN SCHEMA auth TO dashboard_user; - GRANT ALL ON ALL ROUTINES IN SCHEMA storage TO dashboard_user; - GRANT ALL ON ALL ROUTINES IN SCHEMA extensions TO dashboard_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/charts/supabase/templates/db/config.yaml b/charts/supabase/templates/db/migration.config.yaml similarity index 58% rename from charts/supabase/templates/db/config.yaml rename to charts/supabase/templates/db/migration.config.yaml index 7a7a34baa34d7d3b06bd7ca1829522e488dae3b3..77acec568c7502c56e40a5939758bdf9d8a0059a 100644 --- a/charts/supabase/templates/db/config.yaml +++ b/charts/supabase/templates/db/migration.config.yaml @@ -1,10 +1,10 @@ {{- if .Values.db.enabled -}} -apiVersion: v1 kind: ConfigMap +apiVersion: v1 metadata: - name: {{ include "supabase.db.fullname" . }} + name: {{ include "supabase.db.fullname" . }}-migrations labels: {{- include "supabase.labels" . | nindent 4 }} data: - {{- toYaml .Values.db.environment | nindent 2 }} + {{- toYaml .Values.db.config | nindent 2 }} {{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/db/storage.yaml b/charts/supabase/templates/db/storage.yaml deleted file mode 100644 index 28ad48f0d003563e66441d67bec6d1e81d41425a..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/db/storage.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.db.persistence.enabled -}} -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: {{ include "supabase.db.fullname" . }}-pvc - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.db.storage.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - storageClassName: {{ .Values.db.storage.storageClass }} - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.db.storage.size }} -{{- end }} - - diff --git a/charts/supabase/templates/db/volume.yaml b/charts/supabase/templates/db/volume.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a02bc337b81f4144b6e53882d6dc77797d3ebcd9 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/functions/_helpers.tpl b/charts/supabase/templates/functions/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..10e6bea23d16c7b9df6e2763972c6cac6b5119ec --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/functions/deployment.yaml b/charts/supabase/templates/functions/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3bf7a7d9ecfa0641a5c5664b5b97209b4e078621 --- /dev/null +++ b/charts/supabase/templates/functions/deployment.yaml @@ -0,0 +1,121 @@ +{{- 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: + name: {{ include "supabase.secret.db" . }} + key: password + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password_encoded + - name: DB_DATABASE + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.jwt" . }} + key: secret + - name: SUPABASE_ANON_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.jwt" . }} + key: anonKey + - name: SUPABASE_SERVICE_ROLE_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.jwt" . }} + key: serviceKey + - 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/charts/supabase/templates/functions/functions.config.yaml b/charts/supabase/templates/functions/functions.config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8aed927d64ea220ebdc4a526839afc9b80597e00 --- /dev/null +++ b/charts/supabase/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<boolean> { + 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/charts/supabase/templates/functions/service.yaml b/charts/supabase/templates/functions/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f70f1e8d4a200b8241cb2afde064f35952c3d83e --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/functions/serviceaccount.yaml b/charts/supabase/templates/functions/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f8e60abf460296153626965678e37994db65777d --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/imgproxy/_helpers.tpl b/charts/supabase/templates/imgproxy/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..c37ad0264427ac80118206da760829e20f57eda0 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/imgproxy/deployment.yaml b/charts/supabase/templates/imgproxy/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ad89b030963b96c575a3db119e7d6c69b54f33ea --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/imgproxy/service.yaml b/charts/supabase/templates/imgproxy/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..109f3d59d4469875f8f7904a11eba7fd6e93c88f --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/imgproxy/serviceaccount.yaml b/charts/supabase/templates/imgproxy/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..04e62b9b809a2126820fa240f9d335b7346ac25b --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/imgproxy/volume.yaml b/charts/supabase/templates/imgproxy/volume.yaml new file mode 100644 index 0000000000000000000000000000000000000000..34204bd04b40afded542a10e8680660048848969 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/kong/config.yaml b/charts/supabase/templates/kong/config.yaml index fc4f22fc4fa5477ca4fa897dbab86931f97e7af2..2edd25af6fcdf3100b94e97034b850a9c4c90d86 100644 --- a/charts/supabase/templates/kong/config.yaml +++ b/charts/supabase/templates/kong/config.yaml @@ -6,45 +6,51 @@ metadata: labels: {{- include "supabase.labels" . | nindent 4 }} data: - {{- toYaml .Values.kong.environment | nindent 2 }} wrapper.sh: | - #!/bin/sh + #!/bin/bash set -euo pipefail - echo "Replacing env placeholders of /home/kong/kong.yml" + 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}/" \ - /home/kong/template.yml \ - > /home/kong/kong.yml + -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 -{{- if .Values.kong.config -}} - template.yml: - {{- toYaml .Values.kong.config | nindent 4 }} -{{- else }} template.yml: | - _format_version: "1.1" + _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" . }}.{{ .Release.Namespace }}.svc.cluster.local:9999/verify + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/verify routes: - name: auth-v1-open strip_path: true @@ -53,7 +59,7 @@ data: plugins: - name: cors - name: auth-v1-open-callback - url: http://{{ include "supabase.auth.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:9999/callback + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/callback routes: - name: auth-v1-open-callback strip_path: true @@ -62,7 +68,7 @@ data: plugins: - name: cors - name: auth-v1-open-authorize - url: http://{{ include "supabase.auth.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:9999/authorize + url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.auth.service.port }}/authorize routes: - name: auth-v1-open-authorize strip_path: true @@ -70,11 +76,9 @@ data: - /auth/v1/authorize plugins: - name: cors - - - name: auth-v1 - _comment: "GoTrue: /auth/v1/* -> http://{{ include "supabase.auth.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:9999/*" - url: http://{{ include "supabase.auth.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:9999 + _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 @@ -91,11 +95,11 @@ data: allow: - admin - anon - - + {{- end }} + {{- if .Values.rest.enabled }} - name: rest-v1 - _comment: "PostgREST: /rest/v1/* -> http://{{ include "supabase.rest.fullname" . }}.{{ .Release.Namespace }}:3000/*" - url: http://{{ include "supabase.rest.fullname" . }}.{{ .Release.Namespace }}:3000/ + _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 @@ -112,10 +116,35 @@ data: 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" . }}.{{ .Release.Namespace }}.svc.cluster.local:4000/socket/*" - url: http://{{ include "supabase.realtime.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:4000/socket + _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 @@ -132,10 +161,11 @@ data: allow: - admin - anon - + {{- end }} + {{- if .Values.storage.enabled }} - name: storage-v1 - _comment: "Storage: /storage/v1/* -> http://{{ include "supabase.storage.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:5000/*" - url: http://{{ include "supabase.storage.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:5000/ + _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 @@ -143,10 +173,33 @@ data: - /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" . }}.{{ .Release.Namespace }}.svc.cluster.local:8080/*" - url: http://{{ include "supabase.meta.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:8080/ + _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 @@ -161,5 +214,20 @@ data: hide_groups_header: true allow: - admin -{{- end }} + {{- 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/charts/supabase/templates/kong/deployment.yaml b/charts/supabase/templates/kong/deployment.yaml index 4981bddc5394e294d69c1b475b3aaab6fe9b208a..173c470daceba5cf5fc0b807fc87cb830d43f13f 100644 --- a/charts/supabase/templates/kong/deployment.yaml +++ b/charts/supabase/templates/kong/deployment.yaml @@ -34,26 +34,43 @@ spec: {{- 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/sh"] + command: ["/bin/bash"] args: ["/scripts/wrapper.sh"] env: {{- range $key, $value := .Values.kong.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.kong.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} {{- end }} - name: SUPABASE_ANON_KEY valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: anonKey - name: SUPABASE_SERVICE_KEY valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: serviceKey + {{- if .Values.secret.dashboard }} + - name: DASHBOARD_USERNAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.dashboard" . }} + key: username + - name: DASHBOARD_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.dashboard" . }} + key: password + {{- end }} + {{- with .Values.kong.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.kong.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - name: http containerPort: 8000 @@ -63,7 +80,7 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: - - mountPath: /home/kong/template.yml + - mountPath: /usr/local/kong/template.yml name: config subPath: template.yml - mountPath: /scripts diff --git a/charts/supabase/templates/meta/config.yaml b/charts/supabase/templates/meta/config.yaml deleted file mode 100644 index 9ae5c986c14531d5143446d863f3de174ab68d9a..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/meta/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.meta.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.meta.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.meta.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/meta/deployment.yaml b/charts/supabase/templates/meta/deployment.yaml index 7d9f47ff1e4d33933511b91ee87a130cd4405fb2..254371a5b33139da3ac5ca4d0e35593a9673a595 100644 --- a/charts/supabase/templates/meta/deployment.yaml +++ b/charts/supabase/templates/meta/deployment.yaml @@ -37,21 +37,22 @@ spec: env: {{- range $key, $value := .Values.meta.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.meta.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} {{- end }} - - name: DB_USER - valueFrom: - secretKeyRef: - name: {{ .Values.db.secretName }} - key: username - name: DB_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database - name: PG_META_DB_HOST value: $(DB_HOST) - name: PG_META_DB_PORT @@ -64,6 +65,14 @@ spec: 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 diff --git a/charts/supabase/templates/meta/ingress.yaml b/charts/supabase/templates/meta/ingress.yaml deleted file mode 100644 index 5253db292bf201bf3f6148c55874d0307ceb710b..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/meta/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.meta.enabled -}} -{{- if .Values.meta.ingress.enabled -}} -{{- $fullName := include "supabase.meta.fullname" . -}} -{{- $svcPort := .Values.meta.service.port -}} -{{- if and .Values.meta.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.meta.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.meta.ingress.annotations "kubernetes.io/ingress.class" .Values.meta.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.meta.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.meta.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.meta.ingress.className }} - {{- end }} - {{- if .Values.meta.ingress.tls }} - tls: - {{- range .Values.meta.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.meta.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/charts/supabase/templates/minio/_helpers.tpl b/charts/supabase/templates/minio/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..241d041ecdef904eef48fa205405ec3067b2a0ea --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/minio/deployment.yaml b/charts/supabase/templates/minio/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dc97073d282913a6821ce8dc254281c91d88c013 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/minio/service.yaml b/charts/supabase/templates/minio/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..30b6b1f6a02ce071b7d40c70b31b0bfa310fcf86 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/minio/serviceaccount.yaml b/charts/supabase/templates/minio/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..84116a1c194ad623aa99c76e75e10d9cbdca43fb --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/minio/volume.yaml b/charts/supabase/templates/minio/volume.yaml new file mode 100644 index 0000000000000000000000000000000000000000..26e3e66d806b56e63981dc98c5006e9871ce5bea --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/realtime/config.yaml b/charts/supabase/templates/realtime/config.yaml deleted file mode 100644 index 1ce92f8d6ac221093e06ad7ed7616296ddd2d7c0..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/realtime/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.realtime.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.realtime.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.realtime.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/realtime/deployment.yaml b/charts/supabase/templates/realtime/deployment.yaml index cf4b301a647aaa366e989ed5c2ba1b730a53b4a3..dcc0c043297b5c92f7fdb762f7e1dd50ef4520d7 100644 --- a/charts/supabase/templates/realtime/deployment.yaml +++ b/charts/supabase/templates/realtime/deployment.yaml @@ -34,14 +34,18 @@ spec: imagePullPolicy: IfNotPresent env: - name: DB_HOST - value: {{ .Values.realtime.environment.DB_HOST | quote }} - - name: DB_PORT - value: {{ .Values.realtime.environment.DB_PORT | quote }} + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} - name: DB_USER valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: username + - name: DB_PORT + value: {{ .Values.analytics.environment.DB_PORT | quote }} command: ["/bin/sh", "-c"] args: - | @@ -61,31 +65,40 @@ spec: env: {{- range $key, $value := .Values.realtime.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.realtime.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} {{- end }} - - name: DB_USER - valueFrom: - secretKeyRef: - name: {{ .Values.db.secretName }} - key: username - name: DB_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database - name: JWT_SECRET valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: secret - name: API_JWT_SECRET valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: secret + {{- with .Values.realtime.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.realtime.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - name: http containerPort: 4000 diff --git a/charts/supabase/templates/realtime/ingress.yaml b/charts/supabase/templates/realtime/ingress.yaml deleted file mode 100644 index 416c484f2df43c97c77177fd3d50704dfa96e52d..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/realtime/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.realtime.enabled -}} -{{- if .Values.realtime.ingress.enabled -}} -{{- $fullName := include "supabase.realtime.fullname" . -}} -{{- $svcPort := .Values.realtime.service.port -}} -{{- if and .Values.realtime.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.realtime.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.realtime.ingress.annotations "kubernetes.io/ingress.class" .Values.realtime.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.realtime.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.realtime.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.realtime.ingress.className }} - {{- end }} - {{- if .Values.realtime.ingress.tls }} - tls: - {{- range .Values.realtime.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.realtime.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/charts/supabase/templates/rest/config.yaml b/charts/supabase/templates/rest/config.yaml deleted file mode 100644 index acd847936e0f89f38a70a958fc808c77cc130e1a..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/rest/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.rest.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.rest.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.rest.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/rest/deployment.yaml b/charts/supabase/templates/rest/deployment.yaml index f1980ef5feec9c96eb2ab369d5784757214977bf..226527ba9257f4e5d21a59a313f5a6dbeedc476c 100644 --- a/charts/supabase/templates/rest/deployment.yaml +++ b/charts/supabase/templates/rest/deployment.yaml @@ -37,28 +37,47 @@ spec: env: {{- range $key, $value := .Values.rest.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.rest.fullname" $ }} - key: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.db.enabled }} + - name: DB_HOST + value: {{ include "supabase.db.fullname" . }} {{- end }} - - name: DB_USER - valueFrom: - secretKeyRef: - name: {{ .Values.db.secretName }} - key: username - name: DB_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password_encoded + - name: DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database - name: PGRST_DB_URI - value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=$(DB_SSL) + value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD_ENC)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?sslmode=$(DB_SSL) - name: PGRST_JWT_SECRET valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: secret + - name: JWT_EXPIRY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.jwt" . }} + key: expiry + {{- with .Values.rest.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.rest.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - name: http containerPort: 3000 diff --git a/charts/supabase/templates/rest/ingress.yaml b/charts/supabase/templates/rest/ingress.yaml deleted file mode 100644 index 4ec5656454bedaa8f68084932ab34567c6594bea..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/rest/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.rest.enabled -}} -{{- if .Values.rest.ingress.enabled -}} -{{- $fullName := include "supabase.rest.fullname" . -}} -{{- $svcPort := .Values.rest.service.port -}} -{{- if and .Values.rest.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.rest.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.rest.ingress.annotations "kubernetes.io/ingress.class" .Values.rest.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.rest.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.rest.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.rest.ingress.className }} - {{- end }} - {{- if .Values.rest.ingress.tls }} - tls: - {{- range .Values.rest.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.rest.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/charts/supabase/templates/secrets/_helpers.tpl b/charts/supabase/templates/secrets/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..1b167a8ca7100ba45d3a04a9e1c6223468b8f681 --- /dev/null +++ b/charts/supabase/templates/secrets/_helpers.tpl @@ -0,0 +1,41 @@ +{{/* +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 -}} diff --git a/charts/supabase/templates/secrets/analytics.yaml b/charts/supabase/templates/secrets/analytics.yaml new file mode 100644 index 0000000000000000000000000000000000000000..83e7b42090fe5c693d89dc16facb6c15f7ba01c4 --- /dev/null +++ b/charts/supabase/templates/secrets/analytics.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.analytics }} +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 }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/supabase/templates/secrets/dashboard.yaml b/charts/supabase/templates/secrets/dashboard.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f81ef57b3cbe3f5390a8418b7a6a417f6a553343 --- /dev/null +++ b/charts/supabase/templates/secrets/dashboard.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.dashboard }} +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 }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/supabase/templates/secrets/db.yaml b/charts/supabase/templates/secrets/db.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1a6a26e0ec25bc67e07bf9c9165d0361bcaf3823 --- /dev/null +++ b/charts/supabase/templates/secrets/db.yaml @@ -0,0 +1,12 @@ +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 }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} + password_encoded: {{ .Values.secret.db.password | urlquery | b64enc }} diff --git a/charts/supabase/templates/secrets/jwt.yaml b/charts/supabase/templates/secrets/jwt.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5622d23243bc42a3536a95f236728f215793076a --- /dev/null +++ b/charts/supabase/templates/secrets/jwt.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.jwt }} +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 }} + {{ $key }}: {{ $value | toString | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/supabase/templates/secrets/s3.yaml b/charts/supabase/templates/secrets/s3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1752e65812036aebba7e0ead11a27aaf8979c6a8 --- /dev/null +++ b/charts/supabase/templates/secrets/s3.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.s3 }} +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 }} + {{ $key }}: {{ $value | toString | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/supabase/templates/secrets/smtp.yaml b/charts/supabase/templates/secrets/smtp.yaml new file mode 100644 index 0000000000000000000000000000000000000000..38b70b70878b6180df9ce993fb3876d7a27c5608 --- /dev/null +++ b/charts/supabase/templates/secrets/smtp.yaml @@ -0,0 +1,13 @@ +{{- if .Values.secret.smtp }} +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 }} + {{ $key }}: {{ $value | b64enc }} +{{- end }} +{{- end }} diff --git a/charts/supabase/templates/storage/config.yaml b/charts/supabase/templates/storage/config.yaml deleted file mode 100644 index d6b205467ad4b06096cd347a68bf0b1247ccee1e..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/storage/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.storage.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.storage.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.storage.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/storage/deployment.yaml b/charts/supabase/templates/storage/deployment.yaml index 6fdea4df56bceba4e11c62f4d9de718e4eb943db..5fe6d5d134e18bf9e7ff146f3fff1c6b34944c8f 100644 --- a/charts/supabase/templates/storage/deployment.yaml +++ b/charts/supabase/templates/storage/deployment.yaml @@ -35,14 +35,18 @@ spec: imagePullPolicy: IfNotPresent env: - name: DB_HOST - value: {{ .Values.storage.environment.DB_HOST | quote }} - - name: DB_PORT - value: {{ .Values.storage.environment.DB_PORT | quote }} + {{- if .Values.db.enabled }} + value: {{ include "supabase.db.fullname" . | quote }} + {{- else }} + value: {{ .Values.auth.environment.DB_HOST | quote }} + {{- end }} - name: DB_USER valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: username + - name: DB_PORT + value: {{ .Values.analytics.environment.DB_PORT | quote }} command: ["/bin/sh", "-c"] args: - | @@ -51,6 +55,31 @@ spec: 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: @@ -60,38 +89,76 @@ spec: env: {{- range $key, $value := .Values.storage.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.storage.fullname" $ }} - key: {{ $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_USER - valueFrom: - secretKeyRef: - name: {{ .Values.db.secretName }} - key: username - name: DB_PASSWORD valueFrom: secretKeyRef: - name: {{ .Values.db.secretName }} + name: {{ include "supabase.secret.db" . }} key: password + - name: DB_PASSWORD_ENC + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: password_encoded + - name: DB_NAME + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.db" . }} + key: database - name: DATABASE_URL - value: $(DB_DRIVER)://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)?search_path=auth&sslmode=$(DB_SSL) + 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: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: secret - name: ANON_KEY valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: anonKey - name: SERVICE_KEY valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: serviceKey + {{- if .Values.imgproxy.enabled }} + - name: IMGPROXY_URL + value: http://{{ include "supabase.imgproxy.fullname" . }}:{{ .Values.imgproxy.service.port | int }} + {{- end }} + {{- if .Values.secret.s3 }} + - name: AWS_ACCESS_KEY_ID + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: keyId + - name: AWS_SECRET_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.s3" . }} + key: accessKey + {{- 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 @@ -100,10 +167,12 @@ spec: resources: {{- toYaml . | nindent 12 }} {{- end }} - {{- with .Values.storage.volumeMounts }} volumeMounts: + {{- with .Values.storage.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} + - mountPath: /var/lib/storage + name: storage-data {{- with .Values.storage.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -117,11 +186,14 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} volumes: - {{- if .Values.storage.persistence.enabled }} - name: storage-data + {{- if .Values.storage.persistence.enabled }} persistentVolumeClaim: claimName: {{ include "supabase.storage.fullname" . }}-pvc - {{- end }} + {{- else }} + emptyDir: + medium: "" + {{- end }} {{- with .Values.storage.volumes }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/charts/supabase/templates/storage/ingress.yaml b/charts/supabase/templates/storage/ingress.yaml deleted file mode 100644 index bede0887bfbc5417c292c9b377b8aa68316c4516..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/storage/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.storage.enabled -}} -{{- if .Values.storage.ingress.enabled -}} -{{- $fullName := include "supabase.storage.fullname" . -}} -{{- $svcPort := .Values.storage.service.port -}} -{{- if and .Values.storage.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.storage.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.storage.ingress.annotations "kubernetes.io/ingress.class" .Values.storage.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.storage.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.storage.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.storage.ingress.className }} - {{- end }} - {{- if .Values.storage.ingress.tls }} - tls: - {{- range .Values.storage.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.storage.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/charts/supabase/templates/studio/config.yaml b/charts/supabase/templates/studio/config.yaml deleted file mode 100644 index 106c052c791233d62ffd537bd40372f3f2ddcddf..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/studio/config.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- if .Values.studio.enabled -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ include "supabase.studio.fullname" . }} - labels: - {{- include "supabase.labels" . | nindent 4 }} -data: - {{- toYaml .Values.studio.environment | nindent 2 }} -{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/studio/deployment.yaml b/charts/supabase/templates/studio/deployment.yaml index b0c25472fb77e08109a4f3d2fa6ebeda3762263a..2d4ddc7daab6bebd5efa5e488d0d755af7b4d8fa 100644 --- a/charts/supabase/templates/studio/deployment.yaml +++ b/charts/supabase/templates/studio/deployment.yaml @@ -37,21 +37,43 @@ spec: env: {{- range $key, $value := .Values.studio.environment }} - name: {{ $key }} - valueFrom: - configMapKeyRef: - name: {{ include "supabase.studio.fullname" $ }} - key: {{ $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: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: anonKey - name: SUPABASE_SERVICE_KEY valueFrom: secretKeyRef: - name: {{ .Values.jwt.secretName }} + name: {{ include "supabase.secret.jwt" . }} key: serviceKey + {{- 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 diff --git a/charts/supabase/templates/studio/ingress.yaml b/charts/supabase/templates/studio/ingress.yaml deleted file mode 100644 index 06153180acd491a36e9a0d5a48d68a9453314267..0000000000000000000000000000000000000000 --- a/charts/supabase/templates/studio/ingress.yaml +++ /dev/null @@ -1,57 +0,0 @@ -{{- if .Values.studio.enabled -}} -{{- if .Values.studio.ingress.enabled -}} -{{- $fullName := include "supabase.studio.fullname" . -}} -{{- $svcPort := .Values.studio.service.port -}} -{{- if and .Values.studio.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.studio.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.studio.ingress.annotations "kubernetes.io/ingress.class" .Values.studio.ingress.className}} - {{- end }} -{{- end }} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "supabase.labels" . | nindent 4 }} - {{- with .Values.studio.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.studio.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.studio.ingress.className }} - {{- end }} - {{- if .Values.studio.ingress.tls }} - tls: - {{- range .Values.studio.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.studio.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/charts/supabase/templates/test/analytics.yaml b/charts/supabase/templates/test/analytics.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b97c3408d2378d8dd6d9b7be7e77f225a606eca0 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/auth.yaml b/charts/supabase/templates/test/auth.yaml new file mode 100644 index 0000000000000000000000000000000000000000..19a7fb1fa5df26474add56837c2446fd9dba49dc --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/db.yaml b/charts/supabase/templates/test/db.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4863f7a698298465513dd9327e957f5e8a0f4ebb --- /dev/null +++ b/charts/supabase/templates/test/db.yaml @@ -0,0 +1,39 @@ +{{- 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: + name: {{ include "supabase.secret.db" . }} + key: username + - 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/charts/supabase/templates/test/imgproxy.yaml b/charts/supabase/templates/test/imgproxy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b283dc8af170c5433b9f56e4e48b9228a2af8734 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/kong.yaml b/charts/supabase/templates/test/kong.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f8c741f83e20aa7ac70e8544a81a4eff96cf7ff5 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/meta.yaml b/charts/supabase/templates/test/meta.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9aee7f54b23afdbfbc5210a054e6c293a7a48d53 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/minio.yaml b/charts/supabase/templates/test/minio.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b3c3db27f7a4e2a9a908de95c997bceabd7a3830 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/realtime.yaml b/charts/supabase/templates/test/realtime.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e373fde8282047930da5cdf8339f5bc5b9ef2fb3 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/rest.yaml b/charts/supabase/templates/test/rest.yaml new file mode 100644 index 0000000000000000000000000000000000000000..927ffc73bc550c41f17b3749399ebdc8c5953df7 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/storage.yaml b/charts/supabase/templates/test/storage.yaml new file mode 100644 index 0000000000000000000000000000000000000000..db129fff85bbf67d9353b5add395374ca85c0c2e --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/test/studio.yaml b/charts/supabase/templates/test/studio.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7e5082d9cda8ca809f7c51c5d3010d2210f6fa6c --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/vector/_helpers.tpl b/charts/supabase/templates/vector/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..bd861b450d977688bd6e9e7e68159fb491a85616 --- /dev/null +++ b/charts/supabase/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/charts/supabase/templates/vector/config.yaml b/charts/supabase/templates/vector/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..df2e897384de705233b38f9523ad1fd1aed06f35 --- /dev/null +++ b/charts/supabase/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<time>.*): (?P<msg>.*)$') + if err == null { + .event_message = parsed.msg + .timestamp = parse_timestamp!(parsed.time, format: "%e/%b/%Y %R %:z") + .metadata.host = .project + } + # Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date) + realtime_logs: + type: remap + inputs: + - router.realtime + source: |- + .metadata.project = del(.project) + .metadata.external_id = .metadata.project + parsed, err = parse_regex(.event_message, r'^(?P<time>\d+:\d+:\d+\.\d+) \[(?P<level>\w+)\] (?P<msg>.*)$') + if err == null { + .event_message = parsed.msg + .metadata.level = parsed.level + } + # Storage logs may contain json objects so we parse them for completeness + storage_logs: + type: remap + inputs: + - router.storage + source: |- + .metadata.project = del(.project) + .metadata.tenantId = .metadata.project + parsed, err = parse_json(.event_message) + if err == null { + .event_message = parsed.msg + .metadata.level = parsed.level + .metadata.timestamp = parsed.time + .metadata.context[0].host = parsed.hostname + .metadata.context[0].pid = parsed.pid + } + # Postgres logs some messages to stderr which we map to warning severity level + db_logs: + type: remap + inputs: + - router.db + source: |- + .metadata.host = "db-default" + .metadata.parsed.timestamp = .timestamp + + parsed, err = parse_regex(.event_message, r'.*(?P<level>INFO|NOTICE|WARNING|ERROR|LOG|FATAL|PANIC?):.*', numeric_groups: true) + + if err != null || parsed == null { + .metadata.parsed.error_severity = "info" + } + if parsed != null { + .metadata.parsed.error_severity = parsed.level + } + if .metadata.parsed.error_severity == "info" { + .metadata.parsed.error_severity = "log" + } + .metadata.parsed.error_severity = upcase!(.metadata.parsed.error_severity) + + {{- if .Values.analytics.enabled }} + sinks: + logflare_auth: + type: 'http' + inputs: + - auth_logs + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=gotrue.logs.prod&api_key=SECRET[credentials.logflare_api_key]' + logflare_realtime: + type: 'http' + inputs: + - realtime_logs + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=realtime.logs.prod&api_key=SECRET[credentials.logflare_api_key]' + logflare_rest: + type: 'http' + inputs: + - rest_logs + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=postgREST.logs.prod&api_key=SECRET[credentials.logflare_api_key]' + logflare_db: + type: 'http' + inputs: + - db_logs + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + # We must route the sink through kong because ingesting logs before logflare is fully initialised will + # lead to broken queries from studio. This works by the assumption that containers are started in the + # following order: vector > db > logflare > kong + uri: 'http://{{ include "supabase.kong.fullname" . }}:{{ .Values.kong.service.port }}/analytics/v1/api/logs?source_name=postgres.logs&api_key=SECRET[credentials.logflare_api_key]' + logflare_functions: + type: 'http' + inputs: + - router.functions + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=deno-relay-logs&api_key=SECRET[credentials.logflare_api_key]' + logflare_storage: + type: 'http' + inputs: + - storage_logs + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=storage.logs.prod.2&api_key=SECRET[credentials.logflare_api_key]' + logflare_kong: + type: 'http' + inputs: + - kong_logs + - kong_err + encoding: + codec: 'json' + method: 'post' + request: + retry_max_duration_secs: 10 + uri: 'http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.analytics.service.port }}/api/logs?source_name=cloudflare.logs.prod&api_key=SECRET[credentials.logflare_api_key]' + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/vector/deployment.yaml b/charts/supabase/templates/vector/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5dae5fd02824e9243a6291bbf0cbfbc15d7b99ba --- /dev/null +++ b/charts/supabase/templates/vector/deployment.yaml @@ -0,0 +1,103 @@ +{{- if .Values.vector.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "supabase.vector.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + vector.dev/exclude: "true" +spec: + {{- if not .Values.vector.autoscaling.enabled }} + replicas: {{ .Values.vector.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "supabase.vector.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.vector.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + checksum/config: {{ include (print $.Template.BasePath "/vector/config.yaml") . | sha256sum }} + labels: + {{- include "supabase.vector.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.vector.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "supabase.vector.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.vector.podSecurityContext | nindent 8 }} + containers: + - args: + - --config + - /etc/vector/vector.yml + name: {{ include "supabase.vector.name" $ }} + securityContext: + {{- toYaml .Values.vector.securityContext | nindent 12 }} + image: "{{ .Values.vector.image.repository }}:{{ .Values.vector.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.vector.image.pullPolicy }} + env: + {{- range $key, $value := .Values.vector.environment }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: VECTOR_SELF_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- if .Values.analytics.enabled }} + - name: LOGFLARE_API_KEY + valueFrom: + secretKeyRef: + name: {{ include "supabase.secret.analytics" . }} + key: apiKey + {{- end }} + {{- with .Values.vector.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.vector.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.vector.service.port }} + protocol: TCP + volumeMounts: + {{- with .Values.vector.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + - mountPath: /etc/vector/vector.yml + name: vector-config + subPath: vector.yml + - mountPath: /etc/vector/secret.sh + name: vector-config + subPath: secret.sh + {{- with .Values.vector.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + {{- with .Values.vector.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + - name: vector-config + configMap: + name: {{ include "supabase.vector.fullname" . }}-config + defaultMode: 0777 + {{- with .Values.vector.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vector.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vector.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/vector/service.yaml b/charts/supabase/templates/vector/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..be52f122a4945584c65500e982ca361f2174ee0a --- /dev/null +++ b/charts/supabase/templates/vector/service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.vector.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "supabase.vector.fullname" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} +spec: + type: {{ .Values.vector.service.type }} + ports: + - port: {{ .Values.vector.service.port }} + targetPort: 9001 + protocol: TCP + name: http + selector: + {{- include "supabase.vector.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/charts/supabase/templates/vector/serviceaccount.yaml b/charts/supabase/templates/vector/serviceaccount.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4c71eaf6e58493ea0757d7b6866da138ae9e03b0 --- /dev/null +++ b/charts/supabase/templates/vector/serviceaccount.yaml @@ -0,0 +1,41 @@ +{{- if .Values.vector.enabled -}} +{{- if .Values.vector.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "supabase.vector.serviceAccountName" . }} + labels: + {{- include "supabase.labels" . | nindent 4 }} + {{- with .Values.vector.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "supabase.fullname" . }}-reader +rules: + - apiGroups: [""] + resources: ["nodes", "namespaces", "pods"] + verbs: ["list", "watch"] + - apiGroups: [""] + resources: ["pods/log"] + resourceNames: + - {{ include "supabase.fullname" . }}-* + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "supabase.fullname" . }}-view +subjects: + - kind: ServiceAccount + name: {{ include "supabase.vector.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ include "supabase.fullname" . }}-reader + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/supabase/values.example.yaml b/charts/supabase/values.example.yaml index f8b873fa53856a69c93715259dc7736452fb20ac..835c8b1673f7a60e55c643d8dbf79c8cdcb39ea4 100644 --- a/charts/supabase/values.example.yaml +++ b/charts/supabase/values.example.yaml @@ -1,180 +1,151 @@ -jwt: - secretName: "demo-supabase-jwt" - -smtp: - secretName: "demo-supabase-smtp" +secret: + jwt: + anonKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE + serviceKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q + secret: your-super-secret-jwt-token-with-at-least-32-characters-long + smtp: + username: your-mail@example.com + password: example123456 + dashboard: + username: supabase + password: this_password_is_insecure_and_should_be_updated + db: + username: postgres + password: example123456 + database: postgres + analytics: + apiKey: your-super-secret-and-long-logflare-key db: enabled: true image: - repository: supabase/postgres - tag: 14.1.0.105 - secretName: "demo-supabase-db" - persistence: + tag: 15.1.0.147 + livenessProbe: + exec: + command: + - pg_isready + - -U + - postgres + initialDelaySeconds: 3 + persistence: enabled: false - storage: - mountPath: /var/lib/postgresql/data - size: 20Gi - storageClass: standard - annotations: - helm.sh/resource-policy: "keep" studio: image: - repository: supabase/studio - tag: 20230127-6bfd87b + tag: 20240326-5e5586d environment: - SUPABASE_URL: http://localhost - SUPABASE_REST_URL: http://localhost/rest/v1/ - STUDIO_PG_META_URL: http://demo-supabase-kong.default.svc.cluster.local:8000/pg - SUPABASE_PUBLIC_URL: http://localhost/ - ingress: - enabled: true - className: "nginx" - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / - tls: [] - # - secretName: studio.localhost - # hosts: - # - studio.localhost - hosts: - - host: studio.localhost - paths: - - path: / - pathType: Prefix - backend: - serviceName: api - servicePort: 3000 + STUDIO_DEFAULT_ORGANIZATION: "My Organization" + STUDIO_DEFAULT_PROJECT: "My Project" + SUPABASE_PUBLIC_URL: http://example.com/ + NEXT_PUBLIC_ENABLE_LOGS: "true" + livenessProbe: + httpGet: + path: /api/profile + port: 3000 + initialDelaySeconds: 3 auth: image: - repo: v2.40.1 + tag: v2.143.0 environment: - DB_HOST: demo-supabase-db.default.svc.cluster.local - DB_PORT: "5432" - DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow - GOTRUE_API_HOST: "0.0.0.0" - GOTRUE_API_PORT: "9999" - GOTRUE_SITE_URL: http://studio.localhost - GOTRUE_URI_ALLOW_LIST: "*" - GOTRUE_DISABLE_SIGNUP: "false" - GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated - GOTRUE_JWT_ADMIN_ROLES: service_role - GOTRUE_JWT_AUD: authenticated - GOTRUE_JWT_EXP: "3600" + API_EXTERNAL_URL: http://example.com + GOTRUE_SITE_URL: http://example.com GOTRUE_EXTERNAL_EMAIL_ENABLED: "true" GOTRUE_MAILER_AUTOCONFIRM: "true" GOTRUE_SMTP_ADMIN_EMAIL: "your-mail@example.com" GOTRUE_SMTP_HOST: "smtp.example.com" GOTRUE_SMTP_PORT: "587" GOTRUE_SMTP_SENDER_NAME: "your-mail@example.com" - GOTRUE_EXTERNAL_PHONE_ENABLED: "false" - GOTRUE_SMS_AUTOCONFIRM: "false" - GOTRUE_MAILER_URLPATHS_INVITE: "http://localhost:32534/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_CONFIRMATION: "http://localhost:32534/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_RECOVERY: "http://localhost:32534/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: "http://localhost:32534/auth/v1/verify" rest: image: - repository: postgrest/postgrest - tag: v9.0.1.20220717 - environment: - DB_HOST: demo-supabase-db.default.svc.cluster.local - DB_PORT: "5432" - DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow - PGRST_DB_SCHEMA: public,storage - PGRST_DB_ANON_ROLE: anon - PGRST_DB_USE_LEGACY_GUCS: "false" + tag: v12.0.1 realtime: image: - repository: supabase/realtime - tag: v2.1.0 - environment: - DB_HOST: demo-supabase-db.default.svc.cluster.local - DB_PORT: "5432" - DB_NAME: postgres - DB_SSL: "false" - PORT: "4000" - REPLICATION_MODE: RLS - REPLICATION_POLL_INTERVAL: "100" - SECURE_CHANNELS: "true" - SLOT_NAME: supabase_realtime_rls - TEMPORARY_SLOT: "true" - DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime' - DB_ENC_KEY: supabaserealtime - FLY_ALLOC_ID: fly123 - FLY_APP_NAME: realtime - SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq - ERL_AFLAGS: -proto_dist inet_tcp - ENABLE_TAILSCALE: "false" - DNS_NODES: "''" + tag: v2.27.5 + livenessProbe: + httpGet: + path: / + port: 4000 + initialDelaySeconds: 3 + meta: - environment: - DB_HOST: demo-supabase-db.default.svc.cluster.local - DB_PORT: "5432" - DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: disable - PG_META_PORT: "8080" + image: + tag: v0.80.0 storage: image: - repository: supabase/storage-api - tag: v0.26.1 + tag: v0.46.4 + livenessProbe: + httpGet: + path: /status + port: 5000 + initialDelaySeconds: 3 + persistence: + enabled: false + +imgproxy: + image: + tag: v3.8.0 environment: - DB_HOST: demo-supabase-db.default.svc.cluster.local - DB_PORT: "5432" - DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: disable - POSTGREST_URL: http://demo-supabase-rest.default.svc.cluster.local:3000 - PGOPTIONS: -c search_path=storage,public - FILE_SIZE_LIMIT: '52428800' - STORAGE_BACKEND: file - FILE_STORAGE_BACKEND_PATH: /var/lib/storage - TENANT_ID: stub - REGION: stub - GLOBAL_S3_BUCKET: stub - persistence: + IMGPROXY_ENABLE_WEBP_DETECTION: "true" + livenessProbe: + exec: + command: + - imgproxy + - health + initialDelaySeconds: 3 + persistence: enabled: false - accessModes: - - ReadWriteOnce - size: 4Gi - storageClassName: standard - annotations: - helm.sh/resource-policy: "keep" kong: image: repository: kong tag: 2.8.1 environment: - KONG_DATABASE: "off" - KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml - KONG_DNS_ORDER: LAST,A,CNAME - KONG_PLUGINS: request-transformer,cors,key-auth,acl - KONG_LOG_LEVEL: debug - + KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml + KONG_LOG_LEVEL: info ingress: enabled: true className: "nginx" annotations: nginx.ingress.kubernetes.io/rewrite-target: / tls: [] - # - secretName: localhost + # - secretName: example-ingress-tls # hosts: - # - localhost + # - example.com hosts: - - host: localhost + - host: example.com paths: - path: / pathType: Prefix - backend: - serviceName: api - servicePort: 80 \ No newline at end of file + +analytics: + image: + tag: 1.4.0 + livenessProbe: + httpGet: + path: /health + port: 4000 + initialDelaySeconds: 3 + +vector: + image: + tag: 0.34.0-alpine + livenessProbe: + httpGet: + path: /health + port: 9001 + initialDelaySeconds: 3 + volumeMounts: + - name: pod-logs + mountPath: /var/log/pods + volumes: + - name: pod-logs + hostPath: + path: /var/log/pods + +functions: + image: + tag: v1.41.2 diff --git a/charts/supabase/values.yaml b/charts/supabase/values.yaml index 3dd2c159d7fe4797cbd29ea11a4ae5a5e72b1eed..d4dca75233715b973dc02deb807da97d11fd8fb6 100644 --- a/charts/supabase/values.yaml +++ b/charts/supabase/values.yaml @@ -1,30 +1,57 @@ # File structure of values.yaml: -# |-- 1. Database -# |-- 2. Studio -# |-- 3. Auth -# |-- 4. Rest -# |-- 5. Realtime -# |-- 6. Meta -# |-- 7. Storage -# |-- 8. Kong +# |-- 1. Database +# |-- 2. Studio +# |-- 3. Auth +# |-- 4. Rest +# |-- 5. Realtime +# |-- 6. Meta +# |-- 7. Storage +# |-- 8. Image Proxy +# |-- 9. Kong +# |-- 10. Analytics +# |-- 11. Vector +# |-- 12. Functions +# |-- 13. Minio -# jwt will be used to reference secret in multiple services: -# Anon & Service key: Studio, Storage, Kong -# JWT Secret: Auth, Rest, Realtime, Storage -jwt: - secretName: "JWT_SECRET_NAME" - -# smtp will be used to reference secret including smtp credentials -smtp: - secretName: "SMTP_SECRET_NAME" +secret: + # jwt will be used to reference secret in multiple services: + # Anon & Service key: Studio, Storage, Kong + # JWT Secret: Analytics, Auth, Rest, Realtime, Storage + jwt: + anonKey: "" + serviceKey: "" + secret: "" + expiry: 3600 + # database credentials + # these fields must be provided even if using external database + db: + username: "" + password: "" + database: "" + # analytics Logflare API key + analytics: + apiKey: "" + # smtp will be used to reference secret including smtp credentials + smtp: + # username: "" + # password: "" + # secret used to access the studio dashboard + # leave it empty to disable dashboard authentication + dashboard: + # username: "" + # password: "" + # S3 credentials for storage object bucket + s3: + # keyId: "" + # accessKey: "" # Optional: Postgres Database -# A standalone Postgres database configured to work with Supabase services. -# You can spin up any other Postgres database container if required. +# A standalone Postgres database configured to work with Supabase services. +# You can spin up any other Postgres database container if required. # If so, make sure to adjust DB_HOST accordingly to point to the right database service. db: - enabled: false # Disable the database provisioning - secretName: "DB_SECRET_NAME" # Still NEEDS TO BE DEFINED, even if database provisioning is disabled + # Enable database provisioning + enabled: true image: repository: supabase/postgres pullPolicy: IfNotPresent @@ -33,6 +60,8 @@ db: replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -55,26 +84,13 @@ db: type: ClusterIP port: 5432 environment: + POSTGRES_HOST: /var/run/postgresql + PGPORT: "5432" + POSTGRES_PORT: "5432" # POSTGRES_HOST_AUTH_METHOD: md5 - ingress: - enabled: false - className: "nginx" - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / - # cert-manager.io/cluster-issuer: "letsencrypt-staging" - # kubernetes.io/tls-acme: "true" - tls: - - secretName: DB.EXAMPLE.COM - hosts: - - DB.EXAMPLE.COM - hosts: - - host: DB.EXAMPLE.COM - paths: - - path: / - pathType: Prefix - backend: - serviceName: database - servicePort: 5432 + # Enable SSL for postgres by specifying paths for mounted certificate key pair + # POSTGRES_SSL_CERT: /path/to/ssl/server.crt + # POSTGRES_SSL_KEY: /path/to/ssl/server.key # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -97,6 +113,14 @@ db: # requests: # cpu: 100m # memory: 128Mi + persistence: + enabled: true + storageClassName: "" + annotations: {} + size: 8Gi + accessModes: + - ReadWriteOnce + class: "" autoscaling: enabled: true minReplicas: 1 @@ -106,10 +130,13 @@ db: nodeSelector: {} tolerations: [] affinity: {} + # Additional migration scripts can be defined here + config: {} # Studio Application studio: - enabled: true # Disable the studio + # Enable studio provisioning + enabled: true image: repository: supabase/studio pullPolicy: IfNotPresent @@ -118,6 +145,8 @@ studio: replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -140,28 +169,11 @@ studio: type: ClusterIP port: 3000 environment: - SUPABASE_URL: https://API.EXAMPLE.COM - SUPABASE_REST_URL: https://API.EXAMPLE.COM/rest/v1/ - STUDIO_PG_META_URL: http://RELEASE_NAME-kong.NAMESPACE.svc.cluster.local:8000/pg - ingress: - enabled: true - className: "nginx" - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / - # cert-manager.io/cluster-issuer: "letsencrypt-staging" - # kubernetes.io/tls-acme: "true" - tls: - - secretName: STUDIO.EXAMPLE.COM - hosts: - - STUDIO.EXAMPLE.COM - hosts: - - host: STUDIO.EXAMPLE.COM - paths: - - path: / - pathType: Prefix - backend: - serviceName: api - servicePort: 3000 + STUDIO_DEFAULT_ORGANIZATION: Default Organization + STUDIO_DEFAULT_PROJECT: Default Project + STUDIO_PORT: "3000" + SUPABASE_PUBLIC_URL: http://example.com + NEXT_PUBLIC_ENABLE_LOGS: "true" # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -196,7 +208,8 @@ studio: # Auth Service auth: - enabled: true # Disable the auth service + # Enable auth provisioning + enabled: true image: repository: supabase/gotrue pullPolicy: IfNotPresent @@ -205,6 +218,8 @@ auth: replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -227,14 +242,16 @@ auth: type: ClusterIP port: 9999 environment: - DB_HOST: DATABASE.NAMESPACE.svc.cluster.local - DB_PORT: "5432" + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USER: supabase_auth_admin + DB_PORT: 5432 DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow # disable, allow, prefer, require, verify-ca, verify-full + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full + API_EXTERNAL_URL: http://example.com GOTRUE_API_HOST: "0.0.0.0" GOTRUE_API_PORT: "9999" - GOTRUE_SITE_URL: https://STUDIO.EXAMPLE.COM + GOTRUE_SITE_URL: http://example.com GOTRUE_URI_ALLOW_LIST: "*" GOTRUE_DISABLE_SIGNUP: "false" GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated @@ -243,25 +260,18 @@ auth: GOTRUE_JWT_EXP: "3600" GOTRUE_EXTERNAL_EMAIL_ENABLED: "true" GOTRUE_MAILER_AUTOCONFIRM: "true" + # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true + # GOTRUE_SMTP_MAX_FREQUENCY: 1s GOTRUE_SMTP_ADMIN_EMAIL: "SMTP_ADMIN_MAIL" GOTRUE_SMTP_HOST: "SMTP_HOST" GOTRUE_SMTP_PORT: "SMTP_PORT" GOTRUE_SMTP_SENDER_NAME: "SMTP_SENDER_NAME" GOTRUE_EXTERNAL_PHONE_ENABLED: "false" GOTRUE_SMS_AUTOCONFIRM: "false" - GOTRUE_MAILER_URLPATHS_INVITE: "https://API.EXAMPLE.COM/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_CONFIRMATION: "https://API.EXAMPLE.COM/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_RECOVERY: "https://API.EXAMPLE.COM/auth/v1/verify" - GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: "https://API.EXAMPLE.COM/auth/v1/verify" - ingress: - enabled: false - className: "" - annotations: {} - hosts: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + GOTRUE_MAILER_URLPATHS_INVITE: "/auth/v1/verify" + GOTRUE_MAILER_URLPATHS_CONFIRMATION: "/auth/v1/verify" + GOTRUE_MAILER_URLPATHS_RECOVERY: "/auth/v1/verify" + GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: "/auth/v1/verify" # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -296,14 +306,17 @@ auth: # Rest Service rest: - enabled: true # Disable the rest service + # Enable postgrest provisioning + enabled: true image: repository: postgrest/postgrest pullPolicy: IfNotPresent - tag: "latest" + tag: "latest" imagePullSecrets: [] nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -326,23 +339,15 @@ rest: type: ClusterIP port: 3000 environment: - DB_HOST: DATABASE.NAMESPACE.svc.cluster.local - DB_PORT: "5432" + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USER: authenticator + DB_PORT: 5432 DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow # disable, allow, prefer, require, verify-ca, verify-full - PGRST_DB_SCHEMA: public,storage + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full + PGRST_DB_SCHEMAS: public,storage,graphql_public PGRST_DB_ANON_ROLE: anon - PGRST_DB_USE_LEGACY_GUCS: "false" - ingress: - enabled: false - className: "" - annotations: {} - hosts: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + PGRST_DB_USE_LEGACY_GUCS: false # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -377,14 +382,17 @@ rest: # Realtime Service realtime: - enabled: true # Disable the realtime service + # Enable realtime provisioning + enabled: true image: repository: supabase/realtime pullPolicy: IfNotPresent tag: "latest" imagePullSecrets: [] nameOverride: "" - fullnameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -407,25 +415,20 @@ realtime: type: ClusterIP port: 4000 environment: - DB_HOST: DATABASE.NAMESPACE.svc.cluster.local - DB_PORT: "5432" - DB_NAME: postgres - DB_SSL: "false" + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USER: supabase_admin + DB_PORT: 5432 + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full + DB_AFTER_CONNECT_QUERY: "SET search_path TO _realtime" + DB_ENC_KEY: supabaserealtime PORT: "4000" - REPLICATION_MODE: RLS - REPLICATION_POLL_INTERVAL: "100" - SECURE_CHANNELS: "true" - SLOT_NAME: supabase_realtime_rls - TEMPORARY_SLOT: "true" - ingress: - enabled: false - className: "" - annotations: {} - hosts: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + FLY_ALLOC_ID: fly123 + FLY_APP_NAME: realtime + SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq + ERL_AFLAGS: -proto_dist inet_tcp + ENABLE_TAILSCALE: "false" + DNS_NODES: "''" # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -460,15 +463,18 @@ realtime: # Meta Service meta: - enabled: true # Disable the meta service + # Enable meta provisioning + enabled: true image: repository: supabase/postgres-meta pullPolicy: IfNotPresent tag: "latest" imagePullSecrets: [] - replicaCount: 1 + replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -491,21 +497,13 @@ meta: type: ClusterIP port: 8080 environment: - DB_HOST: DATABASE.NAMESPACE.svc.cluster.local - DB_PORT: "5432" + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USER: supabase_admin + DB_PORT: 5432 DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow # disable, allow, prefer, require, verify-ca, verify-full + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full PG_META_PORT: "8080" - ingress: - enabled: false - className: "" - annotations: {} - hosts: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -536,11 +534,12 @@ meta: # targetMemoryUtilizationPercentage: 80 nodeSelector: {} tolerations: [] - affinity: {} + affinity: {} # Storage Service storage: - enabled: true # Disable the storage service + # Enable storage provisioning + enabled: true image: repository: supabase/storage-api pullPolicy: IfNotPresent @@ -549,6 +548,8 @@ storage: replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -571,28 +572,105 @@ storage: type: ClusterIP port: 5000 environment: - DB_HOST: DATABASE.NAMESPACE.svc.cluster.local - DB_PORT: "5432" + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USER: supabase_storage_admin + DB_PORT: 5432 DB_DRIVER: postgres - DB_NAME: postgres - DB_SSL: allow # disable, allow, prefer, require, verify-ca, verify-full - POSTGREST_URL: http://RELEASE_NAME-rest.NAMESPACE.svc.cluster.local:3000 + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full PGOPTIONS: -c search_path=storage,public - FILE_SIZE_LIMIT: '52428800' - STORAGE_BACKEND: file + FILE_SIZE_LIMIT: "52428800" + STORAGE_BACKEND: file # file, s3 FILE_STORAGE_BACKEND_PATH: /var/lib/storage TENANT_ID: stub REGION: stub GLOBAL_S3_BUCKET: stub - ingress: - enabled: false - className: "" + # Set variables below and secret.s3 above to enable S3 storage bucket + # If using this chart's minio, skip the endpoint and protocol below + # GLOBAL_S3_ENDPOINT: http://minio:9000 + # GLOBAL_S3_PROTOCOL: http + # GLOBAL_S3_FORCE_PATH_STYLE: true + # AWS_DEFAULT_REGION: stub + # volumeMounts: + # - name: volume_name + # mountPath: /path/to/my/secret + # volumes: + # - name: volume_name + # secret: + # defaultMode: 733 + # secretName: my_secret + # items: + # - key: my_secret.txt + # path: name_of_file_in_container.txt + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + persistence: + enabled: true + storageClassName: "" annotations: {} - hosts: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + size: 10Gi + accessModes: + - ReadWriteOnce + class: "" + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} + +# imgproxy +imgproxy: + # Enable imgproxy provisioning + enabled: true + image: + repository: darthsim/imgproxy + pullPolicy: IfNotPresent + tag: "latest" + imagePullSecrets: [] + replicaCount: 1 + nameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + podAnnotations: {} + podSecurityContext: {} + # fsGroup: 2000 + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + service: + type: ClusterIP + port: 5001 + environment: + IMGPROXY_BIND: ":5001" + IMGPROXY_LOCAL_FILESYSTEM_ROOT: / + IMGPROXY_USE_ETAG: "true" + IMGPROXY_ENABLE_WEBP_DETECTION: "true" # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -635,7 +713,8 @@ storage: # Kong kong: - enabled: true # Disable the kong service + # Enable kong provisioning + enabled: true image: repository: kong pullPolicy: IfNotPresent @@ -644,6 +723,8 @@ kong: replicaCount: 1 nameOverride: "" fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} serviceAccount: # Specifies whether a service account should be created create: true @@ -667,10 +748,13 @@ kong: port: 8000 environment: KONG_DATABASE: "off" - KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml + KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml # https://github.com/supabase/cli/issues/14 KONG_DNS_ORDER: LAST,A,CNAME - KONG_PLUGINS: request-transformer,cors,key-auth,acl + KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth + KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k + KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + KONG_LOG_LEVEL: warn ingress: enabled: true className: "nginx" @@ -678,18 +762,18 @@ kong: nginx.ingress.kubernetes.io/rewrite-target: / # cert-manager.io/cluster-issuer: "letsencrypt-staging" # kubernetes.io/tls-acme: "true" - tls: - - secretName: API.EXAMPLE.COM - hosts: - - API.EXAMPLE.COM + tls: [] + # Define TLS secret for SSL termination. + # This section can be left blank if using cluster certificate manager. + # Otherwise, setting this in tandem with certificate manager will overwrite the secret name. + # - secretName: example-com-tls + # hosts: + # - example.com hosts: - - host: API.EXAMPLE.COM + - host: example.com paths: - path: / pathType: Prefix - backend: - serviceName: api - servicePort: 80 # volumeMounts: # - name: volume_name # mountPath: /path/to/my/secret @@ -721,152 +805,305 @@ kong: nodeSelector: {} tolerations: [] affinity: {} - config: {} - # _format_version: "1.1" - - # ### - # ### Consumers / Users - # ### - # consumers: - # - username: anon - # keyauth_credentials: - # - key: ${JWT_ANON_KEY} - # - username: service_role - # keyauth_credentials: - # - key: ${JWT_SERVICE_KEY} - - # ### - # ### Access Control List - # ### - # acls: - # - consumer: anon - # group: anon - # - consumer: service_role - # group: admin - # ### - # ### API Routes - # ### - # services: - # ## Open Auth routes - # - name: auth-v1-open - # url: http://svc.cluster.local:9999/verify - # routes: - # - name: auth-v1-open - # strip_path: true - # paths: - # - /auth/v1/verify - # plugins: - # - name: cors - # - name: auth-v1-open-callback - # url: http://svc.cluster.local:9999/callback - # routes: - # - name: auth-v1-open-callback - # strip_path: true - # paths: - # - /auth/v1/callback - # plugins: - # - name: cors - # - name: auth-v1-open-authorize - # url: http://svc.cluster.local:9999/authorize - # routes: - # - name: auth-v1-open-authorize - # strip_path: true - # paths: - # - /auth/v1/authorize - # plugins: - # - name: cors - - # ## Secure Auth routes - # - name: auth-v1 - # _comment: "GoTrue: /auth/v1/* -> http://svc.cluster.local:9999/*" - # url: http://svc.cluster.local:9999/ - # 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 - - # ## Secure REST routes - # - name: rest-v1 - # _comment: "PostgREST: /rest/v1/* -> http://DATABASE_ENDPOINT:3000/*" - # url: http://DATABASE_ENDPOINT:3000/ - # 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 +# Analytics +analytics: + # Enable analytics provisioning + enabled: true + image: + repository: supabase/logflare + pullPolicy: IfNotPresent + tag: "latest" + imagePullSecrets: [] + replicaCount: 1 + nameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + podAnnotations: {} + podSecurityContext: {} + # fsGroup: 2000 + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + service: + type: ClusterIP + port: 4000 + environment: + LOGFLARE_NODE_HOST: 127.0.0.1 + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USERNAME: supabase_admin + DB_PORT: 5432 + DB_DRIVER: postgresql + DB_SCHEMA: _analytics + LOGFLARE_SINGLE_TENANT: "true" + LOGFLARE_SUPABASE_MODE: "true" + FEATURE_FLAG_OVERRIDE: multibackend=true + # Enable Big Query backend for analytics + bigQuery: + enabled: false + projectId: google-project-id + projectNumber: google-project-number + gcloudJson: "" + # volumeMounts: + # - name: volume_name + # mountPath: /path/to/my/secret + # volumes: + # - name: volume_name + # secret: + # defaultMode: 733 + # secretName: my_secret + # items: + # - key: my_secret.txt + # path: name_of_file_in_container.txt + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} - # ## Secure Realtime routes - # - name: realtime-v1 - # _comment: "Realtime: /realtime/v1/* -> ws://svc.cluster.local:4000/socket/*" - # url: http://svc.cluster.local:4000/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 +# Vector +vector: + # Enable vector provisioning + enabled: true + image: + repository: timberio/vector + pullPolicy: IfNotPresent + tag: "latest" + imagePullSecrets: [] + replicaCount: 1 + nameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + podAnnotations: {} + podSecurityContext: {} + # fsGroup: 2000 + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + service: + type: ClusterIP + port: 9001 + # volumeMounts: + # - name: volume_name + # mountPath: /path/to/my/secret + # volumes: + # - name: volume_name + # secret: + # defaultMode: 733 + # secretName: my_secret + # items: + # - key: my_secret.txt + # path: name_of_file_in_container.txt + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} - # ## Storage routes: the storage server manages its own auth - # - name: storage-v1 - # _comment: "Storage: /storage/v1/* -> http://svc.cluster.local:5000/*" - # url: http://svc.cluster.local:5000/ - # routes: - # - name: storage-v1-all - # strip_path: true - # paths: - # - /storage/v1/ - # plugins: - # - name: cors +# Functions +functions: + # Enable functions provisioning + enabled: true + image: + repository: supabase/edge-runtime + pullPolicy: IfNotPresent + tag: "latest" + imagePullSecrets: [] + replicaCount: 1 + nameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + podAnnotations: {} + podSecurityContext: {} + # fsGroup: 2000 + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + service: + type: ClusterIP + port: 9000 + environment: + # Override the database hostname if using external database + # DB_HOST: DATABASE.NAMESPACE.svc.cluster.local + DB_USERNAME: supabase_functions_admin + DB_PORT: 5432 + DB_DRIVER: postgresql + DB_SSL: disable # disable, allow, prefer, require, verify-ca, verify-full + # Mount user functions + # volumeMounts: + # - name: my_functions + # mountPath: /home/deno/functions/main/main + # volumes: + # - name: my_functions + # secret: + # defaultMode: 733 + # secretName: my_secret + # items: + # - key: my_secret.ts + # path: name_of_file_in_container.ts + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {} - # ## Secure Database routes - # - name: meta - # _comment: "pg-meta: /pg/* -> http://svc.cluster.local:8080/*" - # url: http://svc.cluster.local:8080/ - # 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 \ No newline at end of file +# minio +minio: + enabled: false + image: + repository: minio/minio + pullPolicy: IfNotPresent + tag: "latest" + imagePullSecrets: [] + replicaCount: 1 + nameOverride: "" + fullnameOverride: "" + livenessProbe: {} + readinessProbe: {} + serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + podAnnotations: {} + podSecurityContext: {} + # fsGroup: 2000 + securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + service: + type: ClusterIP + port: 9000 + environment: {} + # Mount user functions + # volumeMounts: + # - name: my_functions + # mountPath: /home/deno/functions/main/main + # volumes: + # - name: my_functions + # secret: + # defaultMode: 733 + # secretName: my_secret + # items: + # - key: my_secret.ts + # path: name_of_file_in_container.ts + resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + persistence: + enabled: false + storageClassName: "" + annotations: {} + size: 10Gi + accessModes: + - ReadWriteOnce + class: "" + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + nodeSelector: {} + tolerations: [] + affinity: {}