(helm) deploy new infra using helm

The new infra we have must be configured in the helm chart. This commit
all the missing templates to deploy yhub, it also automate the creation
of the private keys needed by all services.
This commit is contained in:
Manuel Raynaud
2026-09-09 17:44:03 +02:00
committed by Anthony LC
parent e949ebb787
commit 2ef6d0e7c2
32 changed files with 1430 additions and 370 deletions
+46 -7
View File
@@ -71,7 +71,10 @@ backend:
STORAGES_STATICFILES_BACKEND: servestatic.storage.CompressedManifestStaticFilesStorage
DOCSPEC_API_URL: http://impress-docs-docspec:4000/conversion
USER_RECONCILIATION_FORM_URL: https://docs.127.0.0.1.nip.io
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider-converter:443/api/
# the collaboration server, reached in-cluster: the backend reads and
# writes document content there, and fetches its JWKS from the same host
YHUB_API_BASE_URL: http://impress-docs-yhub:443
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider:443/api/
Y_PROVIDER_API_KEY: my-secret
CACHES_KEY_PREFIX: "{{ now | unixEpoch }}"
django:
@@ -159,11 +162,6 @@ frontend:
runAsNonRoot: false
yProvider:
converter:
enabled: true
replicas: 2
replicas: 1
image:
@@ -192,6 +190,47 @@ yProvider:
- key: cacert.pem
path: cacert.pem
# The keys the backend and the collaboration server sign the calls they make to
# each other with, generated on the cluster by a job into a secret both mount
# read-only.
jwtKeys:
enabled: true
yhub:
replicas: 1
image:
repository: localhost:5001/impress-yhub
pullPolicy: Always
tag: "latest"
envVars:
# its own logical database on the dev-backend postgres: the init-db job
# creates it, the backend never touches it
POSTGRES: postgres://dinum:pass@dev-backend-postgres:5432/yhub
# a redis database of its own too — the backend cache and celery live in /1
REDIS: redis://user:pass@dev-backend-redis:6379/2
REDIS_PREFIX: yhub
COLLABORATION_BACKEND_BASE_URL: https://docs.127.0.0.1.nip.io
COLLABORATION_SERVER_ORIGIN: https://docs.127.0.0.1.nip.io
NODE_EXTRA_CA_CERTS: /cert/cacert.pem
# YHUB_JWT_PRIVATE_KEY_FILE comes from the jwtKeys job below
# Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts:
- name: certs
mountPath: /cert/cacert.pem
subPath: cacert.pem
# Extra volumes to manage our local custom CA and avoid to set ssl_verify: false
extraVolumes:
- name: certs
configMap:
name: certifi
items:
- key: cacert.pem
path: cacert.pem
docSpec:
enabled: true
replicas: 1
@@ -218,7 +257,7 @@ ingressCollaborationWS:
host: docs.127.0.0.1.nip.io
ingressCollaborationApi:
enabled: true
enabled: false
host: docs.127.0.0.1.nip.io
ingressAdmin:
@@ -71,7 +71,10 @@ backend:
STORAGES_STATICFILES_BACKEND: servestatic.storage.CompressedManifestStaticFilesStorage
DOCSPEC_API_URL: http://impress-docs-docspec:4000/conversion
USER_RECONCILIATION_FORM_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider-converter:443/api/
# the collaboration server, reached in-cluster: the backend reads and
# writes document content there, and fetches its JWKS from the same host
YHUB_API_BASE_URL: http://impress-docs-yhub:443
Y_PROVIDER_API_BASE_URL: http://impress-docs-y-provider:443/api/
Y_PROVIDER_API_KEY: my-secret
CACHES_KEY_PREFIX: "{{ now | unixEpoch }}"
migrate:
@@ -137,11 +140,6 @@ frontend:
tag: *tag
yProvider:
converter:
enabled: true
replicas: 1
replicas: 1
image:
@@ -155,6 +153,30 @@ yProvider:
COLLABORATION_SERVER_ORIGIN: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
NODE_OPTIONS: "--max-old-space-size=1024"
# The keys the backend and the collaboration server sign the calls they make to
# each other with, generated on the cluster by a job into a secret both mount
# read-only.
jwtKeys:
enabled: true
yhub:
replicas: 1
image:
repository: lasuite/impress-yhub
pullPolicy: Always
tag: *tag
envVars:
# its own logical database on the dev-backend postgres, created by the
# init-db job; redis /2, the backend cache and celery live in /1
POSTGRES: postgres://dinum:pass@dev-backend-postgres:5432/yhub
REDIS: redis://user:pass@dev-backend-redis:6379/2
REDIS_PREFIX: yhub
COLLABORATION_BACKEND_BASE_URL: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
COLLABORATION_SERVER_ORIGIN: https://{{ .Values.feature }}-docs.{{ .Values.domain }}
NODE_OPTIONS: "--max-old-space-size=1024"
docSpec:
enabled: true
replicas: 1
@@ -182,7 +204,7 @@ ingressCollaborationWS:
host: {{ .Values.feature }}-docs.{{ .Values.domain }}
ingressCollaborationApi:
enabled: true
enabled: false
host: {{ .Values.feature }}-docs.{{ .Values.domain }}
ingressAdmin:
+111 -3
View File
@@ -38,7 +38,6 @@
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket` | | `true` |
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout` | | `86400` |
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout` | | `86400` |
| `ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/upstream-hash-by` | | `$arg_room` |
| `ingressRedirects.enabled` | whether to enable the Ingress Redirects or not | `false` |
| `ingressRedirects.className` | IngressClass to use for the Ingress Redirects | `nil` |
| `ingressRedirects.host` | Host for the Ingress Redirects | `impress.example.com` |
@@ -51,13 +50,13 @@
| `ingressCollaborationApi.className` | IngressClass to use for the Ingress | `nil` |
| `ingressCollaborationApi.host` | Host for the Ingress | `impress.example.com` |
| `ingressCollaborationApi.path` | Path to use for the Ingress | `/collaboration/api/` |
| `ingressCollaborationApi.paths` | Paths to route to the collaboration server, one rule each | `["/collaboration/ydoc/","/collaboration/jwks/"]` |
| `ingressCollaborationApi.hosts` | Additional host to configure for the Ingress | `[]` |
| `ingressCollaborationApi.tls.enabled` | Whether to enable TLS for the Ingress | `true` |
| `ingressCollaborationApi.tls.secretName` | Secret name for TLS config | `nil` |
| `ingressCollaborationApi.tls.additional[].secretName` | Secret name for additional TLS config | |
| `ingressCollaborationApi.tls.additional[].hosts[]` | Hosts for additional TLS config | |
| `ingressCollaborationApi.customBackends` | Add custom backends to ingress | `[]` |
| `ingressCollaborationApi.annotations.nginx.ingress.kubernetes.io/upstream-hash-by` | | `$arg_room` |
| `ingressAdmin.enabled` | whether to enable the Ingress or not | `false` |
| `ingressAdmin.className` | IngressClass to use for the Ingress | `nil` |
| `ingressAdmin.host` | Host for the Ingress | `impress.example.com` |
@@ -301,13 +300,122 @@
| `yProvider.pdb.enabled` | Enable pdb on yProvider | `true` |
| `yProvider.serviceAccountName` | Optional service account name to use for yProvider pods | `nil` |
### JWT signing keys
| Name | Description | Value |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------ | -------------------- |
| `jwtKeys.enabled` | Generate the JWT signing keys of the services on the cluster | `false` |
| `jwtKeys.existingSecret` | Secret already holding the keys, generated in a secret of the chart's own when empty | `nil` |
| `jwtKeys.mountPath` | Path the keys are mounted at, in every service reading them | `/data/jwt` |
| `jwtKeys.backendKeyFilename` | Name of the key signing the tokens the backend issues | `private.pem` |
| `jwtKeys.yhubKeyFilename` | Name of the key signing the calls the collaboration server makes to the backend | `yhub-private.pem` |
| `jwtKeys.keySize` | Size, in bits, of the generated RSA keys | `2048` |
| `jwtKeys.rbac.create` | Create the service account and the role the job needs to create the secret | `true` |
| `jwtKeys.image.repository` | Repository to use to pull the image generating the keys | `alpine/openssl` |
| `jwtKeys.image.tag` | Tag of the image generating the keys | `3.5.7` |
| `jwtKeys.image.pullPolicy` | Pull policy of the image generating the keys | `IfNotPresent` |
| `jwtKeys.kubectlImage.repository` | Repository to use to pull the image handing the keys to the secret | `dtzar/helm-kubectl` |
| `jwtKeys.kubectlImage.tag` | Tag of the image handing the keys to the secret | `3.16.2` |
| `jwtKeys.kubectlImage.pullPolicy` | Pull policy of the image handing the keys to the secret | `IfNotPresent` |
| `jwtKeys.job.podSecurityContext` | Pod security context of the generating job | `{}` |
| `jwtKeys.job.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the job containers | `false` |
| `jwtKeys.job.securityContext.capabilities.drop` | List of capabilities to drop for the job containers | `["ALL"]` |
| `jwtKeys.job.securityContext.runAsNonRoot` | Whether to run the job containers as a non-root user | `true` |
| `jwtKeys.job.securityContext.runAsUser` | User the job containers run as, their images declaring none | `1000` |
| `jwtKeys.job.securityContext.runAsGroup` | Group the job containers run as | `1000` |
| `jwtKeys.job.securityContext.seccompProfile.type` | Seccomp profile type for the job containers | `RuntimeDefault` |
| `jwtKeys.job.restartPolicy` | Restart policy of the generating job | `Never` |
| `jwtKeys.job.backoffLimit` | Numbers of generating job retries | `2` |
| `jwtKeys.job.ttlSecondsAfterFinished` | Period to wait before removing the generating job | `30` |
| `jwtKeys.job.generateCommand` | Override the command generating the keys | `[]` |
| `jwtKeys.job.publishCommand` | Override the command creating the secret from the generated keys | `[]` |
| `jwtKeys.job.annotations` | Annotations to add to the generating job | `{}` |
| `jwtKeys.job.podAnnotations` | Annotations to add to the generating job Pod | `{}` |
| `jwtKeys.job.resources` | Resource requirements for the job containers | `{}` |
| `jwtKeys.job.nodeSelector` | Node selector for the generating job Pod | `{}` |
| `jwtKeys.job.tolerations` | Tolerations for the generating job Pod | `[]` |
| `jwtKeys.job.affinity` | Affinity for the generating job Pod | `{}` |
| `jwtKeys.job.serviceAccountName` | Service account of the generating job Pod, the one created above when empty | `nil` |
### yhub
| Name | Description | Value |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------ |
| `yhub.enabled` | Enable the yhub collaboration server, its service and its init-db job | `true` |
| `yhub.image.repository` | Repository to use to pull the yhub container image | `lasuite/impress-yhub` |
| `yhub.image.tag` | yhub container tag | `latest` |
| `yhub.image.pullPolicy` | yhub container image pull policy | `IfNotPresent` |
| `yhub.command` | Override the yhub container command | `[]` |
| `yhub.args` | Override the yhub container args | `[]` |
| `yhub.replicas` | Amount of yhub replicas | `3` |
| `yhub.shareProcessNamespace` | Enable share process namespace between containers | `false` |
| `yhub.sidecars` | Add sidecars containers to yhub deployment | `[]` |
| `yhub.terminationGracePeriodSeconds` | Grace period given to a yhub pod to drain before it is killed | `60` |
| `yhub.securityContext.allowPrivilegeEscalation` | Whether to allow privilege escalation for the yhub container | `false` |
| `yhub.securityContext.capabilities.drop` | List of capabilities to drop for the yhub container | `["ALL"]` |
| `yhub.securityContext.runAsNonRoot` | Whether to run the yhub container as a non-root user | `true` |
| `yhub.securityContext.runAsUser` | User the yhub container runs as | `1000` |
| `yhub.securityContext.runAsGroup` | Group the yhub container runs as | `1000` |
| `yhub.securityContext.seccompProfile.type` | Seccomp profile type for the yhub container | `RuntimeDefault` |
| `yhub.envVars` | Configure yhub container environment variables | `undefined` |
| `yhub.envVars.REDIS` | Required, redis/valkey url holding the live document state (e.g. redis://valkey:6379/0) | |
| `yhub.envVars.POSTGRES` | Required, url of the yhub database, created by the init-db job (e.g. postgres://user:pass@postgres:5432/yhub) | |
| `yhub.envVars.REDIS_PREFIX` | Namespace of the redis keys, when the instance is shared (default: yhub) | |
| `yhub.envVars.COLLABORATION_BACKEND_BASE_URL` | Base url of the Docs backend, which yhub asks about users and document access rights | |
| `yhub.envVars.COLLABORATION_SERVER_ORIGIN` | Comma separated list of the origins allowed to open a websocket | |
| `yhub.envVars.YHUB_JWT_PRIVATE_KEY_FILE` | Path to the RSA private key (PEM) yhub signs its calls to the backend with, mounted from a secret | |
| `yhub.envVars.SOFT_MIGRATION` | Set to "true" to seed rooms from the legacy Django/S3 document store on first access | |
| `yhub.envVars.BY_VALUE` | Example environment variable by setting value directly | |
| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
| `yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
| `yhub.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
| `yhub.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
| `yhub.podAnnotations` | Annotations to add to the yhub Pod | `{}` |
| `yhub.dpAnnotations` | Annotations to add to the yhub Deployment | `{}` |
| `yhub.initDbJobAnnotations` | Annotations for the yhub init-db job | `{}` |
| `yhub.jobs.ttlSecondsAfterFinished` | Period to wait before removing the init-db job | `30` |
| `yhub.jobs.backoffLimit` | Numbers of init-db job retries | `2` |
| `yhub.initDb.enabled` | Run the job creating and upgrading the yhub schema | `true` |
| `yhub.initDb.command` | Override the command creating and upgrading the yhub schema | `[]` |
| `yhub.initDb.retries` | How many times the schema script is retried while the postgres server does not answer | `60` |
| `yhub.initDb.retryDelaySeconds` | Seconds between two attempts | `5` |
| `yhub.initDb.restartPolicy` | Restart policy of the init-db job | `Never` |
| `yhub.service.type` | yhub Service type | `ClusterIP` |
| `yhub.service.port` | yhub Service listening port | `443` |
| `yhub.service.targetPort` | yhub container listening port | `3002` |
| `yhub.service.annotations` | Annotations to add to the yhub Service | `{}` |
| `yhub.probes.liveness.path` | Configure path for yhub HTTP liveness probe | `/collaboration/jwks/v1` |
| `yhub.probes.liveness.initialDelaySeconds` | Configure initial delay for yhub liveness probe | `10` |
| `yhub.probes.readiness.path` | Configure path for yhub HTTP readiness probe | `/collaboration/jwks/v1` |
| `yhub.probes.readiness.initialDelaySeconds` | Configure initial delay for yhub readiness probe | `5` |
| `yhub.probes.liveness.targetPort` | Configure port for yhub HTTP liveness probe | |
| `yhub.probes.liveness.timeoutSeconds` | Configure timeout for yhub liveness probe | |
| `yhub.probes.readiness.targetPort` | Configure port for yhub HTTP readiness probe | |
| `yhub.probes.readiness.timeoutSeconds` | Configure timeout for yhub readiness probe | |
| `yhub.probes.startup.path` | Configure path for yhub HTTP startup probe | |
| `yhub.probes.startup.targetPort` | Configure port for yhub HTTP startup probe | |
| `yhub.probes.startup.initialDelaySeconds` | Configure initial delay for yhub startup probe | |
| `yhub.probes.startup.timeoutSeconds` | Configure timeout for yhub startup probe | |
| `yhub.resources` | Resource requirements for the yhub container | `{}` |
| `yhub.nodeSelector` | Node selector for the yhub Pod | `{}` |
| `yhub.tolerations` | Tolerations for the yhub Pod | `[]` |
| `yhub.affinity` | Affinity for the yhub Pod | `{}` |
| `yhub.persistence` | Additional volumes to create and mount on the yhub. Used for debugging purposes | `{}` |
| `yhub.persistence.volume-name.size` | Size of the additional volume | |
| `yhub.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
| `yhub.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
| `yhub.extraVolumeMounts` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` |
| `yhub.extraVolumes` | Additional volumes to mount on the yhub. Mounted on the init-db job too | `[]` |
| `yhub.pdb.enabled` | Enable pdb on yhub | `true` |
| `yhub.serviceAccountName` | Optional service account name to use for yhub pods | `nil` |
### docSpec
| Name | Description | Value |
| -------------------------------------------------- | --------------------------------------------------------------- | ----------------------- |
| `docSpec.enabled` | Enable docSpec deployment | `false` |
| `docSpec.image.repository` | Repository to use to pull docSpec container image | `ghcr.io/docspecio/api` |
| `docSpec.image.tag` | docSpec container tag | `2.6.3` |
| `docSpec.image.tag` | docSpec container tag | `3.0.1` |
| `docSpec.image.pullPolicy` | docSpec container image pull policy | `IfNotPresent` |
| `docSpec.command` | Override the docSpec container command | `[]` |
| `docSpec.args` | Override the docSpec container args | `[]` |
+75 -10
View File
@@ -186,16 +186,6 @@ Requires top level scope
{{ include "impress.fullname" . }}-y-provider
{{- end }}
{{/*
Full name for the yProvider converter
Requires top level scope
*/}}
{{- define "impress.yProvider.converter.fullname" -}}
{{ include "impress.yProvider.fullname" . }}-converter
{{- end }}
{{/*
Full name for the docSpec
@@ -205,6 +195,81 @@ Requires top level scope
{{ include "impress.fullname" . }}-docspec
{{- end }}
{{/*
Full name for the yhub collaboration server
Requires top level scope
*/}}
{{- define "impress.yhub.fullname" -}}
{{ include "impress.fullname" . }}-yhub
{{- end }}
{{/*
JWT signing keys the RSA keys the services sign the calls they make to each
other with. The jwt-keys job generates them once into a secret every service
mounts read-only, so no key is ever templated into a manifest, written in a
values file, or kept anywhere the services themselves can write.
Requires top level scope
*/}}
{{- define "impress.jwtKeys.secretName" -}}
{{- .Values.jwtKeys.existingSecret | default (printf "%s-jwt-keys" (include "impress.fullname" .)) -}}
{{- end }}
{{- define "impress.jwtKeys.serviceAccountName" -}}
{{- .Values.jwtKeys.job.serviceAccountName | default (printf "%s-jwt-keys" (include "impress.fullname" .)) -}}
{{- end }}
{{- define "impress.jwtKeys.backendPath" -}}
{{ .Values.jwtKeys.mountPath }}/{{ .Values.jwtKeys.backendKeyFilename }}
{{- end }}
{{- define "impress.jwtKeys.yhubPath" -}}
{{ .Values.jwtKeys.mountPath }}/{{ .Values.jwtKeys.yhubKeyFilename }}
{{- end }}
{{/*
The volume holding the keys. A pod referencing a secret that does not exist yet
stays in ContainerCreating and mounts it as soon as the job creates it, so
nothing else is needed to order the two.
Requires top level scope
*/}}
{{- define "impress.jwtKeys.volume" -}}
- name: jwt-keys
secret:
secretName: {{ include "impress.jwtKeys.secretName" . }}
# read-only for everyone, as the files the job generates are
defaultMode: 0444
{{- end }}
{{- define "impress.jwtKeys.volumeMount" -}}
- name: jwt-keys
mountPath: {{ .Values.jwtKeys.mountPath }}
readOnly: true
{{- end }}
{{/*
`*_FILE` environment variables pointing at the keys, added only when the
deployment did not set them by hand configuring a key of your own stays
possible, and wins.
Requires top level scope
*/}}
{{- define "impress.jwtKeys.backendEnv" -}}
{{- if not (hasKey (.Values.backend.envVars | default dict) "JWT_PRIVATE_KEY_FILE") }}
- name: "JWT_PRIVATE_KEY_FILE"
value: {{ include "impress.jwtKeys.backendPath" . | quote }}
{{- end }}
{{- end }}
{{- define "impress.jwtKeys.yhubEnv" -}}
{{- if not (hasKey (.Values.yhub.envVars | default dict) "YHUB_JWT_PRIVATE_KEY_FILE") }}
- name: "YHUB_JWT_PRIVATE_KEY_FILE"
value: {{ include "impress.jwtKeys.yhubPath" . | quote }}
{{- end }}
{{- end }}
{{/*
Full name for the Celery Worker
@@ -38,9 +38,12 @@ items:
imagePullPolicy: {{ ($.Values.backend.image | default dict).pullPolicy | default $.Values.image.pullPolicy }}
args:
{{- toYaml .command | nindent 22 }}
{{- if $envVars}}
{{- if or $envVars $.Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 22 }}
{{- if $.Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" $ | nindent 22 }}
{{- end }}
{{- end }}
{{- if $.Values.backend.envFrom }}
envFrom:
@@ -55,6 +58,9 @@ items:
{{- toYaml . | nindent 22 }}
{{- end }}
volumeMounts:
{{- if $.Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" $ | nindent 20 }}
{{- end }}
{{- range $index, $value := $.Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -72,6 +78,9 @@ items:
{{- end }}
restartPolicy: {{ .restartPolicy | default "Never" }}
volumes:
{{- if $.Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" $ | nindent 16 }}
{{- end }}
{{- range $index, $value := $.Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
@@ -49,9 +49,12 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- $envFrom := concat (.Values.backend.envFrom | default list) ((.Values.backend.django | default dict).envFrom | default list) }}
{{- if $envFrom }}
@@ -83,6 +86,9 @@ spec:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -116,6 +122,9 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
+10 -1
View File
@@ -44,9 +44,12 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
@@ -61,6 +64,9 @@ spec:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -90,6 +96,9 @@ spec:
{{- end }}
restartPolicy: {{ .Values.backend.job.restartPolicy }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
@@ -48,9 +48,12 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
@@ -65,6 +68,9 @@ spec:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -94,6 +100,9 @@ spec:
{{- end }}
restartPolicy: {{ .Values.backend.createsuperuser.restartPolicy }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
@@ -48,9 +48,12 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- if .Values.backend.envFrom }}
envFrom:
@@ -65,6 +68,9 @@ spec:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -94,6 +100,9 @@ spec:
{{- end }}
restartPolicy: {{ .Values.backend.migrate.restartPolicy }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
@@ -49,9 +49,12 @@ spec:
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.backendEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- $envFrom := concat (.Values.backend.envFrom | default list) (.Values.backend.celery.envFrom | default list) }}
{{- if $envFrom }}
@@ -83,6 +86,9 @@ spec:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
@@ -116,6 +122,9 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
@@ -46,20 +46,26 @@ spec:
- host: {{ .Values.ingressCollaborationApi.host | quote }}
http:
paths:
- path: {{ .Values.ingressCollaborationApi.path | quote }}
{{- /* one rule per route: what is not listed here is not reachable
from the outside, which is how the backend-internal routes
(reset-connections, migrate, restore-ydoc, reset-ydoc) stay
in-cluster */}}
{{- range .Values.ingressCollaborationApi.paths | default (list .Values.ingressCollaborationApi.path) }}
- path: {{ . | quote }}
{{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }}
pathType: ImplementationSpecific
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ include "impress.yProvider.fullname" . }}
name: {{ include "impress.yhub.fullname" $ }}
port:
number: {{ .Values.yProvider.service.port }}
number: {{ $.Values.yhub.service.port }}
{{- else }}
serviceName: {{ include "impress.yProvider.fullname" . }}
servicePort: {{ .Values.yProvider.service.port }}
serviceName: {{ include "impress.yhub.fullname" $ }}
servicePort: {{ $.Values.yhub.service.port }}
{{- end }}
{{- end }}
{{- with .Values.ingressCollaborationApi.customBackends }}
{{- toYaml . | nindent 10 }}
{{- end }}
@@ -53,12 +53,12 @@ spec:
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ include "impress.yProvider.fullname" . }}
name: {{ include "impress.yhub.fullname" . }}
port:
number: {{ .Values.yProvider.service.port }}
number: {{ .Values.yhub.service.port }}
{{- else }}
serviceName: {{ include "impress.yProvider.fullname" . }}
servicePort: {{ .Values.yProvider.service.port }}
serviceName: {{ include "impress.yhub.fullname" . }}
servicePort: {{ .Values.yhub.service.port }}
{{- end }}
{{- with .Values.ingressCollaborationWS.customBackends }}
{{- toYaml . | nindent 10 }}
@@ -0,0 +1,153 @@
{{- if and .Values.jwtKeys.enabled (not .Values.jwtKeys.existingSecret) -}}
{{- $fullName := include "impress.fullname" . -}}
{{- $component := "jwt-keys" -}}
{{- $secretName := include "impress.jwtKeys.secretName" . -}}
# Generates the RSA keys the services sign the calls they make to each other
# with: one for the backend (the tokens Django issues to the collaboration
# server and the converter), one for the collaboration server (the calls it
# makes back to Django). Only the private halves are written — each service
# publishes the public half of its own key on its JWKS endpoint, where the
# other one reads it, so no key is ever copied from one side to the other.
#
# Two steps, two images: openssl writes the keys in a volume the pod throws
# away, then kubectl hands them to the secret the services mount. They only
# ever exist in that pod and in the secret.
#
# Idempotent, and deliberately so: the secret is left alone when it is already
# there, which is what makes it safe to re-run on every sync. Rolling the keys
# is deleting the secret and letting the next run create it again — both
# services follow, they pick the verification key by its "kid" and re-fetch the
# set when they meet one they do not know.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ $fullName }}-jwt-keys
namespace: {{ .Release.Namespace | quote }}
annotations:
argocd.argoproj.io/sync-options: Replace=true,Force=true
# after the service account it runs as, before anything mounting the secret
argocd.argoproj.io/sync-wave: "-2"
{{- with .Values.jwtKeys.job.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
spec:
ttlSecondsAfterFinished: {{ .Values.jwtKeys.job.ttlSecondsAfterFinished }}
backoffLimit: {{ .Values.jwtKeys.job.backoffLimit }}
template:
metadata:
{{- with .Values.jwtKeys.job.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end}}
serviceAccountName: {{ include "impress.jwtKeys.serviceAccountName" . }}
{{- with .Values.jwtKeys.job.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: {{ .Values.jwtKeys.job.restartPolicy }}
initContainers:
- name: generate
image: "{{ .Values.jwtKeys.image.repository }}:{{ .Values.jwtKeys.image.tag }}"
imagePullPolicy: {{ .Values.jwtKeys.image.pullPolicy }}
{{- if .Values.jwtKeys.job.generateCommand }}
command:
{{- toYaml .Values.jwtKeys.job.generateCommand | nindent 12 }}
{{- else }}
# `command` rather than `args`: the image entrypoint is openssl
# itself. The keys are the ones `bin/generate-jwt-private-key.sh`
# writes for the compose stack, same command — PKCS#8 RSA, the format
# both the backend and the collaboration server read.
command:
- /bin/sh
- -c
- |
set -eu
for name in {{ .Values.jwtKeys.backendKeyFilename | quote }} {{ .Values.jwtKeys.yhubKeyFilename | quote }}; do
openssl genpkey -algorithm RSA \
-pkeyopt rsa_keygen_bits:{{ .Values.jwtKeys.keySize }} \
-out "/keys/$name"
echo "$name generated"
done
{{- end }}
{{- with .Values.jwtKeys.job.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.jwtKeys.job.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
- name: keys
mountPath: /keys
containers:
- name: publish
image: "{{ .Values.jwtKeys.kubectlImage.repository }}:{{ .Values.jwtKeys.kubectlImage.tag }}"
imagePullPolicy: {{ .Values.jwtKeys.kubectlImage.pullPolicy }}
{{- if .Values.jwtKeys.job.publishCommand }}
command:
{{- toYaml .Values.jwtKeys.job.publishCommand | nindent 12 }}
{{- else }}
command:
- /bin/sh
- -c
- |
set -eu
# the keys generated above are dropped on the floor when the
# secret is already there: they are new ones, and replacing the
# live pair is a decision, never a side effect of a sync
if kubectl get secret {{ $secretName | quote }} >/dev/null 2>&1; then
echo "secret {{ $secretName }} already exists, keeping the keys it holds"
exit 0
fi
kubectl create secret generic {{ $secretName | quote }} \
--from-file={{ .Values.jwtKeys.backendKeyFilename }}=/keys/{{ .Values.jwtKeys.backendKeyFilename }} \
--from-file={{ .Values.jwtKeys.yhubKeyFilename }}=/keys/{{ .Values.jwtKeys.yhubKeyFilename }}
echo "secret {{ $secretName }} created"
{{- end }}
{{- with .Values.jwtKeys.job.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.jwtKeys.job.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.jwtKeys.job.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
- name: keys
mountPath: /keys
readOnly: true
{{- with .Values.jwtKeys.job.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.jwtKeys.job.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.jwtKeys.job.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
# the keys live here for the lifetime of this pod and nowhere else
- name: keys
emptyDir:
medium: Memory
{{- end }}
@@ -0,0 +1,63 @@
{{- if and .Values.jwtKeys.enabled .Values.jwtKeys.rbac.create (not .Values.jwtKeys.existingSecret) -}}
{{- $component := "jwt-keys" -}}
{{- $name := include "impress.jwtKeys.serviceAccountName" . -}}
# The generating job is the only thing in this release allowed to touch the
# secret holding the keys, and all it is allowed to do is read whether it
# exists and create it — not read its content back, not replace it, not delete
# it. The services themselves get the keys through a volume, so they need no
# access to the kubernetes API at all.
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ $name }}
namespace: {{ .Release.Namespace | quote }}
annotations:
# The same wave as the job that runs under it. An earlier wave looks safer
# and is not: argocd only moves to the next wave once the current one is
# healthy, and a ServiceAccount has no health of its own to report. Within
# a wave it applies by kind, and accounts, roles and bindings all come
# before jobs — which is the ordering actually needed here.
argocd.argoproj.io/sync-wave: "-2"
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $name }}
namespace: {{ .Release.Namespace | quote }}
annotations:
argocd.argoproj.io/sync-wave: "-2"
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
rules:
# creating cannot be restricted to a name, kubernetes has no such rule
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
# reading can, and is restricted to the one secret — to its existence really,
# the job never looks at what it holds
- apiGroups: [""]
resources: ["secrets"]
resourceNames:
- {{ include "impress.jwtKeys.secretName" . | quote }}
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ $name }}
namespace: {{ .Release.Namespace | quote }}
annotations:
argocd.argoproj.io/sync-wave: "-2"
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ $name }}
subjects:
- kind: ServiceAccount
name: {{ $name }}
namespace: {{ .Release.Namespace | quote }}
{{- end }}
@@ -0,0 +1,178 @@
{{- if .Values.yhub.enabled -}}
{{- $envVars := include "impress.common.env" (list . .Values.yhub) -}}
{{- $fullName := include "impress.yhub.fullname" . -}}
{{- $component := "yhub" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
annotations:
{{- with .Values.yhub.dpAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
spec:
replicas: {{ .Values.yhub.replicas }}
selector:
matchLabels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
template:
metadata:
annotations:
{{- with .Values.yhub.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end}}
{{- if .Values.yhub.serviceAccountName }}
serviceAccountName: {{ .Values.yhub.serviceAccountName }}
{{- end }}
shareProcessNamespace: {{ .Values.yhub.shareProcessNamespace }}
# a websocket connection is dropped when the pod goes away, and the client
# reconnects to another one — but the updates it sent are only in redis
# until a worker persists them, so leave the embedded worker time to drain
terminationGracePeriodSeconds: {{ .Values.yhub.terminationGracePeriodSeconds }}
containers:
{{- with .Values.yhub.sidecars }}
{{- toYaml . | nindent 8 }}
{{- end }}
- name: {{ .Chart.Name }}
image: "{{ (.Values.yhub.image | default dict).repository | default .Values.image.repository }}:{{ (.Values.yhub.image | default dict).tag | default .Values.image.tag }}"
imagePullPolicy: {{ (.Values.yhub.image | default dict).pullPolicy | default .Values.image.pullPolicy }}
{{- with .Values.yhub.command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.yhub.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if or $envVars .Values.jwtKeys.enabled }}
env:
{{- $envVars | indent 12 }}
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.yhubEnv" . | nindent 12 }}
{{- end }}
{{- end }}
{{- if .Values.yhub.envFrom }}
envFrom:
{{- toYaml .Values.yhub.envFrom | nindent 12 }}
{{- end }}
{{- with .Values.yhub.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.yhub.service.targetPort }}
protocol: TCP
{{- if .Values.yhub.probes.liveness }}
livenessProbe:
{{- include "impress.probes.abstract" (merge .Values.yhub.probes.liveness (dict "targetPort" .Values.yhub.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.yhub.probes.readiness }}
readinessProbe:
{{- include "impress.probes.abstract" (merge .Values.yhub.probes.readiness (dict "targetPort" .Values.yhub.service.targetPort )) | nindent 12 }}
{{- end }}
{{- if .Values.yhub.probes.startup }}
startupProbe:
{{- include "impress.probes.abstract" (merge .Values.yhub.probes.startup (dict "targetPort" .Values.yhub.service.targetPort )) | nindent 12 }}
{{- end }}
{{- with .Values.yhub.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volumeMount" . | nindent 12 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
subPath: content
{{- end }}
{{- range $name, $volume := .Values.yhub.persistence }}
- name: "{{ $name }}"
mountPath: "{{ $volume.mountPath }}"
{{- end }}
{{- range .Values.yhub.extraVolumeMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- with .Values.yhub.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.yhub.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.yhub.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if .Values.jwtKeys.enabled }}
{{- include "impress.jwtKeys.volume" . | nindent 8 }}
{{- end }}
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
{{- end }}
{{- range $name, $volume := .Values.yhub.persistence }}
- name: "{{ $name }}"
{{- if eq $volume.type "emptyDir" }}
emptyDir: {}
{{- else }}
persistentVolumeClaim:
claimName: "{{ $fullName }}-{{ $name }}"
{{- end }}
{{- end }}
{{- range .Values.yhub.extraVolumes }}
- name: {{ .name }}
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
{{- else if .csi }}
csi:
{{- toYaml .csi | nindent 12 }}
{{- else if .configMap }}
configMap:
{{- toYaml .configMap | nindent 12 }}
{{- else if .emptyDir }}
emptyDir:
{{- toYaml .emptyDir | nindent 12 }}
{{- else }}
emptyDir: {}
{{- end }}
{{- end }}
---
{{ if .Values.yhub.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
spec:
maxUnavailable: 1
selector:
matchLabels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
{{ end }}
{{- end }}
@@ -0,0 +1,148 @@
{{- if and .Values.yhub.enabled .Values.yhub.initDb.enabled -}}
{{- $envVars := include "impress.common.env" (list . .Values.yhub) -}}
{{- $fullName := include "impress.yhub.fullname" . -}}
{{- $component := "yhub" -}}
# yhub never runs DDL from the server or the worker: the schema is created by
# the script it ships, which creates the database when it is missing and every
# table the installed version needs. It is idempotent, and it has to run again
# on every yhub upgrade that adds a table — the counterpart of the backend
# migrate job, hence the same Replace=true so a re-sync re-runs it.
#
# No sync wave, again like the backend migrate job: it runs in the default one,
# alongside it, and waits for its database the way that one waits for Django's.
# An earlier wave only moved it ahead of the postgres it needs.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ $fullName }}-init-db
namespace: {{ .Release.Namespace | quote }}
annotations:
argocd.argoproj.io/sync-options: Replace=true,Force=true
{{- with .Values.yhub.initDbJobAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
spec:
ttlSecondsAfterFinished: {{ .Values.yhub.jobs.ttlSecondsAfterFinished }}
backoffLimit: {{ .Values.yhub.jobs.backoffLimit }}
template:
metadata:
annotations:
{{- with .Values.yhub.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end}}
{{- if .Values.yhub.serviceAccountName }}
serviceAccountName: {{ .Values.yhub.serviceAccountName }}
{{- end }}
shareProcessNamespace: {{ .Values.yhub.shareProcessNamespace }}
containers:
- name: {{ .Chart.Name }}
image: "{{ (.Values.yhub.image | default dict).repository | default .Values.image.repository }}:{{ (.Values.yhub.image | default dict).tag | default .Values.image.tag }}"
imagePullPolicy: {{ (.Values.yhub.image | default dict).pullPolicy | default .Values.image.pullPolicy }}
{{- if .Values.yhub.initDb.command }}
command:
{{- toYaml .Values.yhub.initDb.command | nindent 12 }}
{{- else }}
# The script yhub ships, wrapped as `npm run init-db`, retried until
# the postgres server answers: nothing here creates it, and a chart
# sync does not wait for whatever does. Retrying the whole script
# rather than probing the port first — it is idempotent, so a run
# against a database that is up but incomplete is a no-op, and no
# postgres client has to be present in the image to ask.
command:
- /bin/sh
- -c
- |
set -u
attempt=1
until node node_modules/@y/hub/bin/init-db.js; do
if [ "$attempt" -ge {{ .Values.yhub.initDb.retries }} ]; then
echo "database still unreachable after $attempt attempts, giving up"
exit 1
fi
echo "database not ready, retrying in {{ .Values.yhub.initDb.retryDelaySeconds }}s ($attempt/{{ .Values.yhub.initDb.retries }})"
attempt=$((attempt + 1))
sleep {{ .Values.yhub.initDb.retryDelaySeconds }}
done
{{- end }}
{{- if $envVars}}
env:
{{- $envVars | indent 12 }}
{{- end }}
{{- if .Values.yhub.envFrom }}
envFrom:
{{- toYaml .Values.yhub.envFrom | nindent 12 }}
{{- end }}
{{- with .Values.yhub.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.yhub.initDb.resources | default .Values.yhub.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
subPath: content
{{- end }}
{{- range .Values.yhub.extraVolumeMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- with .Values.yhub.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.yhub.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.yhub.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: {{ .Values.yhub.initDb.restartPolicy }}
volumes:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
{{- end }}
{{- range .Values.yhub.extraVolumes }}
- name: {{ .name }}
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
{{- else if .csi }}
csi:
{{- toYaml .csi | nindent 12 }}
{{- else if .configMap }}
configMap:
{{- toYaml .configMap | nindent 12 }}
{{- else if .emptyDir }}
emptyDir:
{{- toYaml .emptyDir | nindent 12 }}
{{- else }}
emptyDir: {}
{{- end }}
{{- end }}
{{- end }}
+22
View File
@@ -0,0 +1,22 @@
{{- if .Values.yhub.enabled -}}
{{- $fullName := include "impress.yhub.fullname" . -}}
{{- $component := "yhub" -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
annotations:
{{- toYaml $.Values.yhub.service.annotations | nindent 4 }}
spec:
type: {{ .Values.yhub.service.type }}
ports:
- port: {{ .Values.yhub.service.port }}
targetPort: {{ .Values.yhub.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 4 }}
{{- end }}
@@ -7,7 +7,7 @@ metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
annotations:
{{- with .Values.backend.dpAnnotations }}
{{- with .Values.yProvider.dpAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
@@ -1,189 +0,0 @@
{{ if .Values.yProvider.converter.enabled -}}
{{- $yProvider := .Values.yProvider -}}
{{- $converter := .Values.yProvider.converter -}}
{{- $service := mergeOverwrite (dict) (default dict $yProvider.service) (default dict $converter.service) -}}
{{- $image := mergeOverwrite (dict) (default dict $yProvider.image) (default dict $converter.image) -}}
{{- $probes := mergeOverwrite (dict) (default dict $yProvider.probes) (default dict $converter.probes) -}}
{{- $pdb := mergeOverwrite (dict) (default dict $yProvider.pdb) (default dict $converter.pdb) -}}
{{- $dpAnnotations := mergeOverwrite (dict) (default dict $yProvider.dpAnnotations) (default dict $converter.dpAnnotations) -}}
{{- $podAnnotations := mergeOverwrite (dict) (default dict $yProvider.podAnnotations) (default dict $converter.podAnnotations) -}}
{{- $replicas := default $yProvider.replicas $converter.replicas -}}
{{- $serviceAccountName := default $yProvider.serviceAccountName $converter.serviceAccountName -}}
{{- $shareProcessNamespace := default $yProvider.shareProcessNamespace $converter.shareProcessNamespace -}}
{{- $sidecars := default $yProvider.sidecars $converter.sidecars -}}
{{- $command := default $yProvider.command $converter.command -}}
{{- $args := default $yProvider.args $converter.args -}}
{{- $envFrom := default $yProvider.envFrom $converter.envFrom -}}
{{- $securityContext := mergeOverwrite (dict) (default dict $yProvider.securityContext) (default dict $converter.securityContext) -}}
{{- $resources := mergeOverwrite (dict) (default dict $yProvider.resources) (default dict $converter.resources) -}}
{{- $nodeSelector := mergeOverwrite (dict) (default dict $yProvider.nodeSelector) (default dict $converter.nodeSelector) -}}
{{- $affinity := mergeOverwrite (dict) (default dict $yProvider.affinity) (default dict $converter.affinity) -}}
{{- $tolerations := default $yProvider.tolerations $converter.tolerations -}}
{{- $persistence := mergeOverwrite (dict) (default dict $yProvider.persistence) (default dict $converter.persistence) -}}
{{- $extraVolumeMounts := default $yProvider.extraVolumeMounts $converter.extraVolumeMounts -}}
{{- $extraVolumes := default $yProvider.extraVolumes $converter.extraVolumes -}}
{{- $envVarsScope := dict "envVars" (mergeOverwrite (dict) (default dict $yProvider.envVars) (default dict $converter.envVars)) -}}
{{- $envVars := include "impress.common.env" (list . $envVarsScope) -}}
{{- $fullName := include "impress.yProvider.converter.fullname" . -}}
{{- $component := "yProvider-converter" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
annotations:
{{- with $dpAnnotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
spec:
replicas: {{ $replicas }}
selector:
matchLabels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
template:
metadata:
annotations:
{{- with $podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 8 }}
spec:
{{- if $.Values.image.credentials }}
imagePullSecrets:
- name: {{ include "impress.secret.dockerconfigjson.name" (dict "fullname" (include "impress.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end}}
{{- if $serviceAccountName }}
serviceAccountName: {{ $serviceAccountName }}
{{- end }}
shareProcessNamespace: {{ $shareProcessNamespace }}
containers:
{{- with $sidecars }}
{{- toYaml . | nindent 8 }}
{{- end }}
- name: {{ .Chart.Name }}
image: "{{ $image.repository | default $.Values.image.repository }}:{{ $image.tag | default $.Values.image.tag }}"
imagePullPolicy: {{ $image.pullPolicy | default $.Values.image.pullPolicy }}
{{- with $command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with $args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if $envVars}}
env:
{{- $envVars | indent 12 }}
{{- end }}
{{- if $envFrom }}
envFrom:
{{- toYaml $envFrom | nindent 12 }}
{{- end }}
{{- with $securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ $service.targetPort }}
protocol: TCP
{{- if $probes.liveness }}
livenessProbe:
{{- include "impress.probes.abstract" (merge $probes.liveness (dict "targetPort" $service.targetPort )) | nindent 12 }}
{{- end }}
{{- if $probes.readiness }}
readinessProbe:
{{- include "impress.probes.abstract" (merge $probes.readiness (dict "targetPort" $service.targetPort )) | nindent 12 }}
{{- end }}
{{- if $probes.startup }}
startupProbe:
{{- include "impress.probes.abstract" (merge $probes.startup (dict "targetPort" $service.targetPort )) | nindent 12 }}
{{- end }}
{{- with $resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
mountPath: {{ $value.path }}
subPath: content
{{- end }}
{{- range $name, $volume := $persistence }}
- name: "{{ $name }}"
mountPath: "{{ $volume.mountPath }}"
{{- end }}
{{- range $extraVolumeMounts }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
subPath: {{ .subPath | default "" }}
readOnly: {{ .readOnly }}
{{- end }}
{{- with $nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with $tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- range $index, $value := .Values.mountFiles }}
- name: "files-{{ $index }}"
configMap:
name: "{{ include "impress.fullname" $ }}-files-{{ $index }}"
{{- end }}
{{- range $name, $volume := $persistence }}
- name: "{{ $name }}"
{{- if eq $volume.type "emptyDir" }}
emptyDir: {}
{{- else }}
persistentVolumeClaim:
claimName: "{{ $fullName }}-{{ $name }}"
{{- end }}
{{- end }}
{{- range $extraVolumes }}
- name: {{ .name }}
{{- if .existingClaim }}
persistentVolumeClaim:
claimName: {{ .existingClaim }}
{{- else if .secret }}
secret:
{{ toYaml .secret | nindent 12 }}
{{- else if .hostPath }}
hostPath:
{{ toYaml .hostPath | nindent 12 }}
{{- else if .csi }}
csi:
{{- toYaml .csi | nindent 12 }}
{{- else if .configMap }}
configMap:
{{- toYaml .configMap | nindent 12 }}
{{- else if .emptyDir }}
emptyDir:
{{- toYaml .emptyDir | nindent 12 }}
{{- else }}
emptyDir: {}
{{- end }}
{{- end }}
---
{{ if $pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
spec:
maxUnavailable: 1
selector:
matchLabels:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 6 }}
{{ end }}
{{ end }}
@@ -1,25 +0,0 @@
{{ if .Values.yProvider.converter.enabled -}}
{{- $yProvider := .Values.yProvider -}}
{{- $converter := .Values.yProvider.converter -}}
{{- $service := mergeOverwrite (dict) (default dict $yProvider.service) (default dict $converter.service) -}}
{{- $fullName := include "impress.yProvider.converter.fullname" . -}}
{{- $component := "yProvider-converter" -}}
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{- include "impress.common.labels" (list . $component) | nindent 4 }}
annotations:
{{- toYaml $service.annotations | nindent 4 }}
spec:
type: {{ $service.type }}
ports:
- port: {{ $service.port }}
targetPort: {{ $service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "impress.common.selectorLabels" (list . $component) | nindent 4 }}
{{ end -}}
+332 -77
View File
@@ -78,12 +78,14 @@ ingressCollaborationWS:
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/upstream-hash-by
##
## No upstream-hash-by: yhub passes updates between its replicas through
## redis, so two clients editing the same document may land on different
## pods — where the y-provider it replaces needed a room to stay on one.
annotations:
nginx.ingress.kubernetes.io/enable-websocket: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
nginx.ingress.kubernetes.io/upstream-hash-by: $arg_room
## @param ingressRedirects.enabled whether to enable the Ingress Redirects or not
## @param ingressRedirects.className IngressClass to use for the Ingress Redirects
@@ -112,7 +114,22 @@ ingressCollaborationApi:
enabled: false
className: null
host: impress.example.com
## Only used when `paths` below is empty
path: /collaboration/api/
## @param ingressCollaborationApi.paths Paths to route to the collaboration server, one rule each
##
## The routes yhub serves to browsers, guarded by the same document
## authorization as the websocket. Everything it serves that is not listed
## here stays in-cluster — `create-ydoc`, `reset-connections`, `migrate`,
## `restore-ydoc` and `reset-ydoc` are called by the backend only, and
## publishing them would put document deletion and the legacy migration one
## request away from the internet.
##
## `jwks` is public on purpose: it carries the public halves of the keys
## yhub signs with, and nothing else.
paths:
- /collaboration/ydoc/
- /collaboration/jwks/
## @param ingressCollaborationApi.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
@@ -129,9 +146,10 @@ ingressCollaborationApi:
## @param ingressCollaborationApi.customBackends Add custom backends to ingress
customBackends: []
## @param ingressCollaborationApi.annotations.nginx.ingress.kubernetes.io/upstream-hash-by
annotations:
nginx.ingress.kubernetes.io/upstream-hash-by: $arg_room
## @skip ingressCollaborationApi.annotations
## Same as ingressCollaborationWS: no upstream-hash-by, any yhub replica
## answers for any document.
annotations: {}
## @param ingressAdmin.enabled whether to enable the Ingress or not
## @param ingressAdmin.className IngressClass to use for the Ingress
@@ -643,7 +661,11 @@ posthog:
annotations: {}
## @section yProvider
##
## The conversion service, and nothing else since the collaboration moved to
## yhub: this deployment *is* the converter the backend calls on
## `Y_PROVIDER_API_BASE_URL`, so there is no separate converter release to
## enable anymore.
yProvider:
## @param yProvider.image.repository Repository to use to pull impress's yProvider container image
## @param yProvider.image.tag impress's yProvider container tag
@@ -653,77 +675,6 @@ yProvider:
pullPolicy: IfNotPresent
tag: "latest"
converter:
## @param yProvider.converter.enabled Enable the yProvider converter deployment and service
enabled: false
## @param yProvider.converter.replicas Amount of yProvider replicas
replicas: 3
## @param yProvider.converter.resources Resource requirements for the yProvider container
resources: {}
## @param yProvider.converter.service.type yProvider converter Service type
## @param yProvider.converter.service.port yProvider converter Service listening port
## @param yProvider.converter.service.targetPort yProvider converter container listening port
## @param yProvider.converter.service.annotations Annotations to add to the yProvider converter Service
service: {}
## @param yProvider.converter.command Override the yProvider converter container command
command: []
## @param yProvider.converter.args Override the yProvider converter container args
args: []
## @param yProvider.converter.shareProcessNamespace Enable share process namespace between containers
shareProcessNamespace: false
## @param yProvider.converter.sidecars Add sidecars containers to yProvider converter deployment
sidecars: []
## @skip yProvider.converter.securityContext
securityContext: {}
## @skip yProvider.converter.envVars
envVars: {}
## @skip yProvider.converter.envFrom
envFrom: []
## @param yProvider.converter.podAnnotations Annotations to add to the yProvider converter Pod
podAnnotations: {}
## @param yProvider.converter.dpAnnotations Annotations to add to the yProvider converter Deployment
dpAnnotations: {}
## @skip yProvider.converter.probes
probes: {}
## @param yProvider.converter.nodeSelector Node selector for the yProvider converter Pod
nodeSelector: {}
## @param yProvider.converter.tolerations Tolerations for the yProvider converter Pod
tolerations: []
## @param yProvider.converter.affinity Affinity for the yProvider converter Pod
affinity: {}
## @param yProvider.converter.persistence Additional volumes to create and mount on the yProvider converter
persistence: {}
## @param yProvider.converter.extraVolumeMounts Additional volumes to mount on the yProvider converter
extraVolumeMounts: []
## @param yProvider.converter.extraVolumes Additional volumes to mount on the yProvider converter
extraVolumes: []
## @param yProvider.converter.pdb.enabled Enable pdb on yProvider converter
pdb:
enabled: true
## @param yProvider.converter.serviceAccountName Optional service account name to use for yProvider converter pods
serviceAccountName: null
## @param yProvider.command Override the yProvider container command
command: []
@@ -834,6 +785,310 @@ yProvider:
## @param yProvider.serviceAccountName Optional service account name to use for yProvider pods
serviceAccountName: null
## @section JWT signing keys
##
## The services do not share a secret: each signs the calls it makes to the
## others with an RSA key of its own and publishes the public half on its JWKS
## endpoint, where the others read it. Enabling this generates those keys on
## the cluster — a job creates them once in a secret every service mounts
## read-only, and leaves them alone on the next run — and points the backend
## and the collaboration server at them. No key is ever templated into a
## manifest or written in a values file, and only that job may create the
## secret: nothing in the release can read it back through the api.
##
## Leave it disabled to keep providing the keys yourself, through
## `backend.envVars.JWT_PRIVATE_KEY_FILE` and
## `yhub.envVars.YHUB_JWT_PRIVATE_KEY_FILE` and volumes of your own — both are
## left untouched when they are set by hand, enabled or not.
jwtKeys:
## @param jwtKeys.enabled Generate the JWT signing keys of the services on the cluster
enabled: false
## @param jwtKeys.existingSecret Secret already holding the keys, generated in a secret of the chart's own when empty
##
## It has to hold the two filenames below. Naming one skips the job and the
## rights it needs, the services only mount what is there.
existingSecret: null
## @param jwtKeys.mountPath Path the keys are mounted at, in every service reading them
mountPath: /data/jwt
## @param jwtKeys.backendKeyFilename Name of the key signing the tokens the backend issues
backendKeyFilename: private.pem
## @param jwtKeys.yhubKeyFilename Name of the key signing the calls the collaboration server makes to the backend
yhubKeyFilename: yhub-private.pem
## @param jwtKeys.keySize Size, in bits, of the generated RSA keys
keySize: 2048
## @param jwtKeys.rbac.create Create the service account and the role the job needs to create the secret
##
## Turning it off means providing `jwtKeys.job.serviceAccountName` with an
## account allowed to `create` secrets and to `get` the one named above.
rbac:
create: true
## @param jwtKeys.image.repository Repository to use to pull the image generating the keys
## @param jwtKeys.image.tag Tag of the image generating the keys
## @param jwtKeys.image.pullPolicy Pull policy of the image generating the keys
##
## openssl and a shell, nothing else. Its entrypoint is openssl itself, which
## the job replaces by the script generating both keys.
image:
repository: alpine/openssl
pullPolicy: IfNotPresent
tag: "3.5.7"
## @param jwtKeys.kubectlImage.repository Repository to use to pull the image handing the keys to the secret
## @param jwtKeys.kubectlImage.tag Tag of the image handing the keys to the secret
## @param jwtKeys.kubectlImage.pullPolicy Pull policy of the image handing the keys to the secret
##
## A second image because the openssl one carries no kubectl, and reaching
## the api with what it does carry (busybox wget, which cannot be told about
## the cluster ca) would mean sending the token over an unverified
## connection.
kubectlImage:
repository: dtzar/helm-kubectl
pullPolicy: IfNotPresent
tag: "3.16.2"
## @param jwtKeys.job.podSecurityContext Pod security context of the generating job
## @param jwtKeys.job.securityContext.allowPrivilegeEscalation Whether to allow privilege escalation for the job containers
## @param jwtKeys.job.securityContext.capabilities.drop List of capabilities to drop for the job containers
## @param jwtKeys.job.securityContext.runAsNonRoot Whether to run the job containers as a non-root user
## @param jwtKeys.job.securityContext.runAsUser User the job containers run as, their images declaring none
## @param jwtKeys.job.securityContext.runAsGroup Group the job containers run as
## @param jwtKeys.job.securityContext.seccompProfile.type Seccomp profile type for the job containers
## @param jwtKeys.job.restartPolicy Restart policy of the generating job
## @param jwtKeys.job.backoffLimit Numbers of generating job retries
## @param jwtKeys.job.ttlSecondsAfterFinished Period to wait before removing the generating job
## @param jwtKeys.job.generateCommand Override the command generating the keys
## @param jwtKeys.job.publishCommand Override the command creating the secret from the generated keys
## @param jwtKeys.job.annotations Annotations to add to the generating job
## @param jwtKeys.job.podAnnotations Annotations to add to the generating job Pod
## @param jwtKeys.job.resources Resource requirements for the job containers
## @param jwtKeys.job.nodeSelector Node selector for the generating job Pod
## @param jwtKeys.job.tolerations Tolerations for the generating job Pod
## @param jwtKeys.job.affinity Affinity for the generating job Pod
## @param jwtKeys.job.serviceAccountName Service account of the generating job Pod, the one created above when empty
## @skip jwtKeys.job.env
job:
podSecurityContext: {}
# neither image declares a user of its own, and kubernetes refuses to start
# a container asking for runAsNonRoot without knowing which user to run as
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
restartPolicy: Never
backoffLimit: 2
ttlSecondsAfterFinished: 30
generateCommand: []
publishCommand: []
annotations: {}
podAnnotations: {}
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
serviceAccountName: null
env: []
## @section yhub
##
## The collaboration server: it serves the whole /collaboration/ prefix, the
## websocket included, and replaces the y-provider on that role. It keeps the
## live state of a document in redis/valkey and persists it to its own
## PostgreSQL database, so it needs both — set `yhub.envVars.REDIS` and
## `yhub.envVars.POSTGRES`, there is nothing sensible to default them to.
## Disabling it sends the /collaboration/ ingresses back to the y-provider.
yhub:
## @param yhub.enabled Enable the yhub collaboration server, its service and its init-db job
enabled: true
## @param yhub.image.repository Repository to use to pull the yhub container image
## @param yhub.image.tag yhub container tag
## @param yhub.image.pullPolicy yhub container image pull policy
image:
repository: lasuite/impress-yhub
pullPolicy: IfNotPresent
tag: "latest"
## @param yhub.command Override the yhub container command
command: []
## @param yhub.args Override the yhub container args
args: []
## @param yhub.replicas Amount of yhub replicas
## Every replica also runs a worker (redis consumer groups hand each task to
## one of them), and clients editing the same document need not land on the
## same pod: updates travel through redis.
replicas: 3
## @param yhub.shareProcessNamespace Enable share process namespace between containers
shareProcessNamespace: false
## @param yhub.sidecars Add sidecars containers to yhub deployment
sidecars: []
## @param yhub.terminationGracePeriodSeconds Grace period given to a yhub pod to drain before it is killed
terminationGracePeriodSeconds: 60
## @param yhub.securityContext.allowPrivilegeEscalation Whether to allow privilege escalation for the yhub container
## @param yhub.securityContext.capabilities.drop List of capabilities to drop for the yhub container
## @param yhub.securityContext.runAsNonRoot Whether to run the yhub container as a non-root user
## @param yhub.securityContext.runAsUser User the yhub container runs as
## @param yhub.securityContext.runAsGroup Group the yhub container runs as
## @param yhub.securityContext.seccompProfile.type Seccomp profile type for the yhub container
##
## The user is named rather than left to the image: asking for runAsNonRoot
## without it is refused outright by kubernetes ("container has runAsNonRoot
## and image will run as root") on any image that declares none — which every
## yhub image built before the un-privileged user was added to its Dockerfile
## does. 1000 is the `node` user the base image already carries.
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- "ALL"
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
## @param yhub.envVars Configure yhub container environment variables
## @extra yhub.envVars.REDIS Required, redis/valkey url holding the live document state (e.g. redis://valkey:6379/0)
## @extra yhub.envVars.POSTGRES Required, url of the yhub database, created by the init-db job (e.g. postgres://user:pass@postgres:5432/yhub)
## @extra yhub.envVars.REDIS_PREFIX Namespace of the redis keys, when the instance is shared (default: yhub)
## @extra yhub.envVars.COLLABORATION_BACKEND_BASE_URL Base url of the Docs backend, which yhub asks about users and document access rights
## @extra yhub.envVars.COLLABORATION_SERVER_ORIGIN Comma separated list of the origins allowed to open a websocket
## @extra yhub.envVars.YHUB_JWT_PRIVATE_KEY_FILE Path to the RSA private key (PEM) yhub signs its calls to the backend with, mounted from a secret
## @extra yhub.envVars.SOFT_MIGRATION Set to "true" to seed rooms from the legacy Django/S3 document store on first access
## @extra yhub.envVars.BY_VALUE Example environment variable by setting value directly
## @extra yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
## @extra yhub.envVars.FROM_CONFIGMAP.configMapKeyRef.key Key within a ConfigMap when configuring env vars from a ConfigMap
## @extra yhub.envVars.FROM_SECRET.secretKeyRef.name Name of a Secret when configuring env vars from a Secret
## @extra yhub.envVars.FROM_SECRET.secretKeyRef.key Key within a Secret when configuring env vars from a Secret
## @skip yhub.envVars
envVars:
<<: *commonEnvVars
## @skip yhub.envFrom List of environment variables taken from Secrets or configMaps
envFrom: []
# envFrom:
# - secret:
# name: super-secret-user-credentials
# - configMapRef:
# name: my-environment-variables
## @param yhub.podAnnotations Annotations to add to the yhub Pod
podAnnotations: {}
## @param yhub.dpAnnotations Annotations to add to the yhub Deployment
dpAnnotations: {}
## @param yhub.initDbJobAnnotations Annotations for the yhub init-db job
initDbJobAnnotations: {}
## @param yhub.jobs.ttlSecondsAfterFinished Period to wait before removing the init-db job
## @param yhub.jobs.backoffLimit Numbers of init-db job retries
jobs:
ttlSecondsAfterFinished: 30
backoffLimit: 2
## @param yhub.initDb.enabled Run the job creating and upgrading the yhub schema
## @param yhub.initDb.command Override the command creating and upgrading the yhub schema
## @param yhub.initDb.retries How many times the schema script is retried while the postgres server does not answer
## @param yhub.initDb.retryDelaySeconds Seconds between two attempts
## @param yhub.initDb.restartPolicy Restart policy of the init-db job
## @skip yhub.initDb.resources Resource requirements for the init-db container, defaults to yhub.resources
##
## The job runs in the default sync wave, next to the backend migrate job,
## and waits for its database the same way that one waits for Django's:
## nothing in this chart creates the postgres server, so it has to be given
## the time whatever does takes. The defaults below wait five minutes.
initDb:
enabled: true
command: []
retries: 60
retryDelaySeconds: 5
restartPolicy: Never
resources: {}
## @param yhub.service.type yhub Service type
## @param yhub.service.port yhub Service listening port
## @param yhub.service.targetPort yhub container listening port
## @param yhub.service.annotations Annotations to add to the yhub Service
service:
type: ClusterIP
port: 443
targetPort: 3002
annotations: {}
## @param yhub.probes.liveness.path Configure path for yhub HTTP liveness probe
## @param yhub.probes.liveness.initialDelaySeconds Configure initial delay for yhub liveness probe
## @param yhub.probes.readiness.path Configure path for yhub HTTP readiness probe
## @param yhub.probes.readiness.initialDelaySeconds Configure initial delay for yhub readiness probe
## @extra yhub.probes.liveness.targetPort Configure port for yhub HTTP liveness probe
## @extra yhub.probes.liveness.timeoutSeconds Configure timeout for yhub liveness probe
## @extra yhub.probes.readiness.targetPort Configure port for yhub HTTP readiness probe
## @extra yhub.probes.readiness.timeoutSeconds Configure timeout for yhub readiness probe
## @extra yhub.probes.startup.path Configure path for yhub HTTP startup probe
## @extra yhub.probes.startup.targetPort Configure port for yhub HTTP startup probe
## @extra yhub.probes.startup.initialDelaySeconds Configure initial delay for yhub startup probe
## @extra yhub.probes.startup.timeoutSeconds Configure timeout for yhub startup probe
##
## The JWKS route is the only one yhub serves unauthenticated: it answers 200
## with the public keys it signs its calls to the backend with (an empty set
## when no key is configured), and reads neither redis nor postgres.
probes:
liveness:
path: /collaboration/jwks/v1
initialDelaySeconds: 10
readiness:
path: /collaboration/jwks/v1
initialDelaySeconds: 5
## @param yhub.resources Resource requirements for the yhub container
resources: {}
## @param yhub.nodeSelector Node selector for the yhub Pod
nodeSelector: {}
## @param yhub.tolerations Tolerations for the yhub Pod
tolerations: []
## @param yhub.affinity Affinity for the yhub Pod
affinity: {}
## @param yhub.persistence Additional volumes to create and mount on the yhub. Used for debugging purposes
## @extra yhub.persistence.volume-name.size Size of the additional volume
## @extra yhub.persistence.volume-name.type Type of the additional volume, persistentVolumeClaim or emptyDir
## @extra yhub.persistence.volume-name.mountPath Path where the volume should be mounted to
persistence: {}
## @param yhub.extraVolumeMounts Additional volumes to mount on the yhub. Mounted on the init-db job too
extraVolumeMounts: []
## @param yhub.extraVolumes Additional volumes to mount on the yhub. Mounted on the init-db job too
extraVolumes: []
## @param yhub.pdb.enabled Enable pdb on yhub
pdb:
enabled: true
## @param yhub.serviceAccountName Optional service account name to use for yhub pods
serviceAccountName: null
## @section docSpec
docSpec:
## @param docSpec.enabled Enable docSpec deployment