From 2e89854f8605d025581e523c779489dac6267b25 Mon Sep 17 00:00:00 2001 From: Sienna Meridian Satterwhite Date: Tue, 3 Mar 2026 14:30:45 +0000 Subject: [PATCH] feat(lasuite): deploy La Suite Docs (impress) Adds the impress Helm chart (suitenumerique/docs, v4.5.0) to the lasuite namespace with full Pingora routing, VSO secrets, and local overlay resource tuning. Routing (pingora-config.yaml): - docs.* frontend -> docs-frontend:80 (nginx, static Next.js export) - /api/* and /admin/* -> docs-backend:80 (Django/uvicorn) - /collaboration/ws/* -> docs-y-provider:4444 (Hocuspocus WebSocket) - integration.* -> integration:80 (La Gaufre hub, same file) Secrets (vault-secrets.yaml): - VaultDynamicSecret docs-db-credentials (DB engine, static role) - VaultStaticSecret docs-django-secret (DJANGO_SECRET_KEY) - VaultStaticSecret docs-collaboration-secret (y-provider shared secret) OIDC client (oidc-clients.yaml): - Fix redirect_uri from /oidc/callback/ to /api/v1.0/callback/ -- impress mounts all OIDC URLs under api/{API_VERSION}/ via lasuite.oidc_login, same pattern as people. Local overlay (values-resources.yaml): - docs-backend: 512Mi limit, WEB_CONCURRENCY=2 (4 uvicorn workers exceeded 384Mi at startup on the arm64 Lima VM) - docs-celery-worker: 384Mi limit, CELERY_WORKER_CONCURRENCY=2 - docs-y-provider: 256Mi limit - seaweedfs-filer: raised from 256Mi to 512Mi (OOMKilled during 188MB multipart S3 upload of impress-y-provider image layer) Local overlay (kustomization.yaml): - Image mirrors for impress-backend, impress-frontend, impress-y-provider (amd64-only images retagged to Gitea via cmd_mirror before deploy) --- base/ingress/pingora-config.yaml | 22 +++- base/lasuite/docs-values.yaml | 162 +++++++++++++++++++++++++++ base/lasuite/kustomization.yaml | 8 ++ base/lasuite/oidc-clients.yaml | 2 +- base/lasuite/vault-secrets.yaml | 70 ++++++++++++ overlays/local/kustomization.yaml | 8 ++ overlays/local/values-resources.yaml | 84 +++++++++++++- 7 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 base/lasuite/docs-values.yaml diff --git a/base/ingress/pingora-config.yaml b/base/ingress/pingora-config.yaml index e02ab64..aa64b99 100644 --- a/base/ingress/pingora-config.yaml +++ b/base/ingress/pingora-config.yaml @@ -39,8 +39,22 @@ data: [[routes]] host_prefix = "docs" - backend = "http://docs.lasuite.svc.cluster.local:8000" - websocket = true + backend = "http://docs-frontend.lasuite.svc.cluster.local:80" + + # API and admin go to the backend. + [[routes.paths]] + prefix = "/api/" + backend = "http://docs-backend.lasuite.svc.cluster.local:80" + + [[routes.paths]] + prefix = "/admin/" + backend = "http://docs-backend.lasuite.svc.cluster.local:80" + + # Real-time collaboration WebSocket (y-provider / Hocuspocus). + [[routes.paths]] + prefix = "/collaboration/ws/" + backend = "http://docs-y-provider.lasuite.svc.cluster.local:4444" + websocket = true [[routes]] host_prefix = "meet" @@ -113,6 +127,10 @@ data: host_prefix = "admin" backend = "http://kratos-admin-ui.ory.svc.cluster.local:3000" + [[routes]] + host_prefix = "integration" + backend = "http://integration.lasuite.svc.cluster.local:80" + [[routes]] host_prefix = "s3" backend = "http://seaweedfs-filer.storage.svc.cluster.local:8333" diff --git a/base/lasuite/docs-values.yaml b/base/lasuite/docs-values.yaml new file mode 100644 index 0000000..4050ae1 --- /dev/null +++ b/base/lasuite/docs-values.yaml @@ -0,0 +1,162 @@ +# La Suite Numérique — Docs (impress chart). +# Env vars use the chart's dict-based envVars schema: +# string value → rendered as env.value +# map value → rendered as env.valueFrom (configMapKeyRef / secretKeyRef) +# DOMAIN_SUFFIX is substituted by sed at deploy time. +# +# Required secrets (created by seed script): +# oidc-docs — CLIENT_ID, CLIENT_SECRET (created by Hydra Maester) +# docs-db-credentials — password (VaultDynamicSecret, DB engine) +# docs-django-secret — DJANGO_SECRET_KEY (VaultStaticSecret) +# seaweedfs-s3-credentials — S3_ACCESS_KEY, S3_SECRET_KEY (shared) + +fullnameOverride: docs + +backend: + createsuperuser: + # Superuser creation disabled — users authenticate via OIDC. + enabled: false + + envVars: &backendEnvVars + # ── Database ────────────────────────────────────────────────────────────── + DB_NAME: docs_db + DB_USER: docs + DB_HOST: + configMapKeyRef: + name: lasuite-postgres + key: DB_HOST + DB_PORT: + configMapKeyRef: + name: lasuite-postgres + key: DB_PORT + DB_ENGINE: + configMapKeyRef: + name: lasuite-postgres + key: DB_ENGINE + DB_PASSWORD: + secretKeyRef: + name: docs-db-credentials + key: password + + # ── Redis / Celery ──────────────────────────────────────────────────────── + REDIS_URL: + configMapKeyRef: + name: lasuite-valkey + key: REDIS_URL + CELERY_BROKER_URL: + configMapKeyRef: + name: lasuite-valkey + key: CELERY_BROKER_URL + + # ── S3 ──────────────────────────────────────────────────────────────────── + AWS_STORAGE_BUCKET_NAME: sunbeam-docs + AWS_S3_ENDPOINT_URL: + configMapKeyRef: + name: lasuite-s3 + key: AWS_S3_ENDPOINT_URL + AWS_S3_REGION_NAME: + configMapKeyRef: + name: lasuite-s3 + key: AWS_S3_REGION_NAME + AWS_DEFAULT_ACL: + configMapKeyRef: + name: lasuite-s3 + key: AWS_DEFAULT_ACL + AWS_ACCESS_KEY_ID: + secretKeyRef: + name: seaweedfs-s3-credentials + key: S3_ACCESS_KEY + AWS_SECRET_ACCESS_KEY: + secretKeyRef: + name: seaweedfs-s3-credentials + key: S3_SECRET_KEY + + # ── OIDC (Hydra) ────────────────────────────────────────────────────────── + OIDC_RP_CLIENT_ID: + secretKeyRef: + name: oidc-docs + key: CLIENT_ID + OIDC_RP_CLIENT_SECRET: + secretKeyRef: + name: oidc-docs + key: CLIENT_SECRET + OIDC_RP_SIGN_ALGO: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_RP_SIGN_ALGO + OIDC_RP_SCOPES: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_RP_SCOPES + OIDC_OP_JWKS_ENDPOINT: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_OP_JWKS_ENDPOINT + OIDC_OP_AUTHORIZATION_ENDPOINT: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_OP_AUTHORIZATION_ENDPOINT + OIDC_OP_TOKEN_ENDPOINT: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_OP_TOKEN_ENDPOINT + OIDC_OP_USER_ENDPOINT: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_OP_USER_ENDPOINT + OIDC_OP_LOGOUT_ENDPOINT: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_OP_LOGOUT_ENDPOINT + OIDC_VERIFY_SSL: + configMapKeyRef: + name: lasuite-oidc-provider + key: OIDC_VERIFY_SSL + + # ── Django ──────────────────────────────────────────────────────────────── + DJANGO_SECRET_KEY: + secretKeyRef: + name: docs-django-secret + key: DJANGO_SECRET_KEY + DJANGO_CONFIGURATION: Production + ALLOWED_HOSTS: docs.DOMAIN_SUFFIX + DJANGO_ALLOWED_HOSTS: docs.DOMAIN_SUFFIX + DJANGO_CSRF_TRUSTED_ORIGINS: https://docs.DOMAIN_SUFFIX + LOGIN_REDIRECT_URL: / + + # ── Y-Provider ──────────────────────────────────────────────────────────── + # Shared secret for backend ↔ y-provider auth. + COLLABORATION_SERVER_SECRET: + secretKeyRef: + name: docs-collaboration-secret + key: secret + COLLABORATION_SERVER_URL: http://docs-y-provider.lasuite.svc.cluster.local:4444 + +frontend: + envVars: + NEXT_PUBLIC_API_URL: https://docs.DOMAIN_SUFFIX + NEXT_PUBLIC_COLLABORATION_WS_URL: wss://docs.DOMAIN_SUFFIX/collaboration/ws/ + # La Gaufre app launcher — served from our self-hosted integration service. + GAUFREJS_URL: https://integration.DOMAIN_SUFFIX/api/v1/gaufre.js + +yProvider: + envVars: + # Shared secret so y-provider can verify requests from the backend. + COLLABORATION_SERVER_SECRET: + secretKeyRef: + name: docs-collaboration-secret + key: secret + # Impress backend URL for document access verification. + APP_URL: http://docs-backend.lasuite.svc.cluster.local:80 + +ingress: + enabled: false + +ingressCollaborationWS: + enabled: false + +ingressAdmin: + enabled: false + +ingressMedia: + enabled: false diff --git a/base/lasuite/kustomization.yaml b/base/lasuite/kustomization.yaml index e0580e6..345a99c 100644 --- a/base/lasuite/kustomization.yaml +++ b/base/lasuite/kustomization.yaml @@ -13,6 +13,7 @@ resources: - shared-config.yaml - oidc-clients.yaml - vault-secrets.yaml + - integration-deployment.yaml # La Suite Numérique Helm charts. # Charts with a published Helm repo use helmCharts below. @@ -26,3 +27,10 @@ helmCharts: namespace: lasuite valuesFile: people-values.yaml + # helm repo add docs https://suitenumerique.github.io/docs/ + - name: docs + repo: https://suitenumerique.github.io/docs/ + version: "4.5.0" + releaseName: docs + namespace: lasuite + valuesFile: docs-values.yaml diff --git a/base/lasuite/oidc-clients.yaml b/base/lasuite/oidc-clients.yaml index c106230..f3b45fb 100644 --- a/base/lasuite/oidc-clients.yaml +++ b/base/lasuite/oidc-clients.yaml @@ -19,7 +19,7 @@ spec: - code scope: openid email profile redirectUris: - - https://docs.DOMAIN_SUFFIX/oidc/callback/ + - https://docs.DOMAIN_SUFFIX/api/v1.0/callback/ tokenEndpointAuthMethod: client_secret_post secretName: oidc-docs skipConsent: true diff --git a/base/lasuite/vault-secrets.yaml b/base/lasuite/vault-secrets.yaml index 1a07850..7c958aa 100644 --- a/base/lasuite/vault-secrets.yaml +++ b/base/lasuite/vault-secrets.yaml @@ -129,3 +129,73 @@ spec: templates: DJANGO_SECRET_KEY: text: "{{ index .Secrets \"django-secret-key\" }}" +--- +# Docs DB credentials from OpenBao database secrets engine (static role, 24h rotation). +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultDynamicSecret +metadata: + name: docs-db-credentials + namespace: lasuite +spec: + vaultAuthRef: vso-auth + mount: database + path: static-creds/docs + refreshAfter: 1h + rolloutRestartTargets: + - kind: Deployment + name: docs-backend + - kind: Deployment + name: docs-celery-worker + - kind: Deployment + name: docs-y-provider + destination: + name: docs-db-credentials + create: true + overwrite: true + transformation: + excludeRaw: true + templates: + password: + text: "{{ index .Secrets \"password\" }}" +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: docs-django-secret + namespace: lasuite +spec: + vaultAuthRef: vso-auth + mount: secret + type: kv-v2 + path: docs + refreshAfter: 30s + destination: + name: docs-django-secret + create: true + overwrite: true + transformation: + excludeRaw: true + templates: + DJANGO_SECRET_KEY: + text: "{{ index .Secrets \"django-secret-key\" }}" +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: docs-collaboration-secret + namespace: lasuite +spec: + vaultAuthRef: vso-auth + mount: secret + type: kv-v2 + path: docs + refreshAfter: 30s + destination: + name: docs-collaboration-secret + create: true + overwrite: true + transformation: + excludeRaw: true + templates: + secret: + text: "{{ index .Secrets \"collaboration-secret\" }}" diff --git a/overlays/local/kustomization.yaml b/overlays/local/kustomization.yaml index 3f6cc75..14343a1 100644 --- a/overlays/local/kustomization.yaml +++ b/overlays/local/kustomization.yaml @@ -36,6 +36,14 @@ images: - name: lasuite/people-frontend newName: src.DOMAIN_SUFFIX/studio/people-frontend + # amd64-only impress (Docs) images — same mirror pattern. + - name: lasuite/impress-backend + newName: src.DOMAIN_SUFFIX/studio/impress-backend + - name: lasuite/impress-frontend + newName: src.DOMAIN_SUFFIX/studio/impress-frontend + - name: lasuite/impress-y-provider + newName: src.DOMAIN_SUFFIX/studio/impress-y-provider + patches: # Disable SSL verification for OIDC server-side calls — mkcert CA not trusted in pods - path: patch-oidc-verify-ssl.yaml diff --git a/overlays/local/values-resources.yaml b/overlays/local/values-resources.yaml index c7ebe3e..9ac9b1b 100644 --- a/overlays/local/values-resources.yaml +++ b/overlays/local/values-resources.yaml @@ -98,7 +98,9 @@ spec: - name: filer resources: limits: - memory: 256Mi + memory: 512Mi + requests: + memory: 128Mi --- apiVersion: apps/v1 @@ -173,3 +175,83 @@ metadata: namespace: lasuite spec: replicas: 1 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: docs-celery-worker + namespace: lasuite +spec: + replicas: 1 + template: + spec: + containers: + - name: docs + env: + # Celery workers: 2 concurrent workers fits within local memory budget. + - name: CELERY_WORKER_CONCURRENCY + value: "2" + resources: + limits: + memory: 384Mi + requests: + memory: 128Mi + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: docs-backend + namespace: lasuite +spec: + replicas: 1 + template: + spec: + containers: + - name: docs + env: + # 2 uvicorn workers instead of the default 4 to stay within local memory budget. + # Each worker loads the full Django+impress app (~150 MB), so 4 workers + # pushed peak RSS above 384 Mi and triggered OOMKill at startup. + - name: WEB_CONCURRENCY + value: "2" + resources: + limits: + memory: 512Mi + requests: + memory: 192Mi + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: docs-frontend + namespace: lasuite +spec: + replicas: 1 + template: + spec: + containers: + - name: docs + resources: + limits: + memory: 128Mi + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: docs-y-provider + namespace: lasuite +spec: + replicas: 1 + template: + spec: + containers: + - name: docs + resources: + limits: + memory: 256Mi + requests: + memory: 64Mi