From e55958f7cbd9313d32c4b54c2db2ff8b4e855290 Mon Sep 17 00:00:00 2001 From: emelinda Date: Sun, 9 Aug 2026 00:15:46 +0300 Subject: [PATCH] =?UTF-8?q?Workspaces=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D1=83=D1=80=D0=B5=20aero:=20api=20=D0=B8=20=D1=84=D1=80=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=BD=D0=B4-=D1=80=D0=B5=D0=BC=D0=BE=D1=83?= =?UTF-8?q?=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apps/workspaces/aero — оверлей поверх base: * DJANGO_HOST в base неверен дважды: сервиса backend не существует (он backend-svc), и 8000 — это targetPort контейнера, а сервис принимает на 80. Проверено в кластере: backend-svc:80 отдаёт 200, backend-svc:8000 таймаутит, backend:8000 не резолвится. Та же ошибка была в base у processing/engine-low. * ENVIRONMENT/DJANGO_ORIGINATOR — «prod» заменён на aero: значение уезжает в Sentry и в заголовки запросов к смежным сервисам. * DOCUMENTATION_HOST переписан на честное имя внутри кластера. Выключить интеграцию нечем: DOCUMENTATION_LOGGER_FEATURE гасит только логирование, а GetDocumentByWS вызывается из обработчика GET workspace безусловно. Сервиса documentations в контуре нет и не будет — карточка воркспейса отдаётся без документа. Фронтенд идёт из base без патчей и наружу НЕ публикуется: это Module Federation remote srx_workspaces, его подгружает главный фронтенд платформы. Версия — v2, и это не выбор: в бандле sarex-frontend-dev:contour_5.22.0 лежит строка /workspaces-v2/module/remoteEntry.js и имя ремоута srx_workspaces, обращений к v1 нет вовсе. У v1 (platform/workspaces-frontend) к тому же нет ни одной contour-сборки. nginx-configmap: исправлен порядок директив в трёх location'ах. set обязан идти ДО rewrite ... break — обе принадлежат ngx_http_rewrite_module, и флаг break прекращает обработку его директив, поэтому set после него не выполнялся. Переменная оставалась пустой, nginx писал «no host in upstream :80» и отдавал 500. Так падали и /workspaces-v2/..., и /workflows/... — маршрут / при этом работал, из-за чего дефект и не был замечен раньше. Обвязка: regcred и роль Vault для namespace workspaces, apps/workspaces в gitea_sync_paths, секреты secrets/apps/workspaces/postgres и secrets/vault/common/django_auth. Последний — вопреки имени пути НЕ токен Zitadel: workspaces читает оттуда ключ key и кладёт его в DJANGO_BASIC_AUTH, то есть это base64 логина и пароля администратора django. Совпадение проверено в кластере по sha256. --- aero/roles/sarex_stack/defaults/main.yml | 27 +++++++++ apps/django/aero/nginx-configmap.yaml | 14 ++++- apps/workspaces/aero/kustomization.yaml | 72 ++++++++++++++++++++++++ clusters/aero/apps/kustomization.yaml | 3 + 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 apps/workspaces/aero/kustomization.yaml diff --git a/aero/roles/sarex_stack/defaults/main.yml b/aero/roles/sarex_stack/defaults/main.yml index 5091dcd..b1dabb5 100644 --- a/aero/roles/sarex_stack/defaults/main.yml +++ b/aero/roles/sarex_stack/defaults/main.yml @@ -87,6 +87,7 @@ registry_secrets: - {name: regcred, namespace: django} - {name: regcred, namespace: measurements} - {name: regcred, namespace: processing} + - {name: regcred, namespace: workspaces} # Поднимать стек (docker compose up -d). Требует запущенной службы docker и # docker login в cr.yandex — по умолчанию выключено. @@ -133,6 +134,7 @@ gitea_sync_paths: - apps/django - apps/measurements - apps/processing + - apps/workspaces # Рабочая копия репозитория gitea на хосте (клон, живёт между прогонами). gitea_sync_workdir: "{{ deploy_dir }}/gitea-sync" @@ -218,6 +220,7 @@ vault_k8s_roles: - {name: django, namespaces: [django]} - {name: processing, namespaces: [processing]} - {name: measurements, namespaces: [measurements]} + - {name: workspaces, namespaces: [workspaces]} # Боевые креды инфраструктурных сервисов. Пути и имена ключей заданы чартами # (см. vaultRoot у minio и auth.vault у rabbitmq в infrastructure/*/aero) — @@ -326,6 +329,24 @@ vault_app_secrets: username: "{{ superuser_name }}" password: "{{ superuser_password }}" email: "{{ superuser_name }}@{{ platform_domain }}" + # --- Секреты приложения workspaces ------------------------------------------ + # Пути и имена ключей заданы vault-шаблонами в apps/workspaces/base/backend.yaml. + - path: secrets/apps/workspaces/postgres + data: + host: postgresql.postgresql.svc.cluster.local + port: "5432" + database: "{{ sarex_postgres_workspaces_db }}" + username: "{{ sarex_postgres_workspaces_user }}" + password: "{{ sarex_secrets.SAREX_WORKSPACE_DB_PASSWORD }}" + # ЭТО НЕ ТОКЕН ZITADEL, вопреки имени пути и соседнему ключу. + # workspaces читает отсюда ключ key и подставляет его в DJANGO_BASIC_AUTH — + # то есть содержимое обязано быть base64("логин:пароль") администратора + # django, той самой учётки из secrets/apps/django/superuser выше. Из этого же + # пути django в base читает django_zitadel_access_token, но в контуре Zitadel + # нет и та аннотация удалена в apps/django/aero — здесь удалять её НЕ нужно. + - path: secrets/vault/common/django_auth + data: + key: "{{ (superuser_name ~ ':' ~ superuser_password) | b64encode }}" # JWT-ключи RS512. Те же самые, что роль генерирует для compose-стека, — # контур переезжает, и токены должны остаться совместимыми. - path: secrets/vault/common/rsa_keys @@ -357,6 +378,12 @@ sarex_postgres_django_user: django sarex_postgres_processing_db: workflow_db sarex_postgres_processing_user: processing +# То же для workspaces. Должно совпадать с записью workspace_db в +# contour.databases (infrastructure/postgresql/aero). Имена в единственном +# числе — так они заведены в бутстрапе СУБД, namespace же во множественном. +sarex_postgres_workspaces_db: workspace_db +sarex_postgres_workspaces_user: workspace + # Бакет медиафайлов django. ВНИМАНИЕ: чарт MinIO бакеты не создаёт — его нужно # завести отдельно, иначе загрузка файлов будет падать. sarex_django_s3_bucket: sarex-media-storage diff --git a/apps/django/aero/nginx-configmap.yaml b/apps/django/aero/nginx-configmap.yaml index 9a679bc..89243bb 100644 --- a/apps/django/aero/nginx-configmap.yaml +++ b/apps/django/aero/nginx-configmap.yaml @@ -16,6 +16,14 @@ # сейчас эти пути отдают 502, а когда сервисы приедут — заработают сами, # без правки конфига и перезапуска. # +# ПОРЯДОК ДИРЕКТИВ ВНУТРИ ТАКОГО location ЗНАЧИМ: set обязан идти ДО +# rewrite ... break. Обе директивы принадлежат ngx_http_rewrite_module, а +# флаг break прекращает обработку его директив в этом location — set после +# него просто не выполняется, переменная остаётся пустой, и запрос падает с +# [error] no host in upstream ":80" +# отдавая наружу 500. Именно так и было: / работал, а /workspaces-v2/... и +# /workflows/... возвращали 500, пока set стоял ниже rewrite. +# # 3. backend-svc оставлен прямой ссылкой: он в этом же namespace и существует # всегда, откладывать его резолвинг незачем — наоборот, ошибка в имени # обнаружится сразу при старте. @@ -100,16 +108,16 @@ data: location ~^/workspaces-v2/(.+).js { proxy_http_version 1.1; proxy_set_header Connection ""; - rewrite /workspaces-v2/(.+) /$1 break; set $up_workspaces frontend-svc.workspaces.svc.cluster.local; + rewrite /workspaces-v2/(.+) /$1 break; proxy_pass http://$up_workspaces:80; } location ~^/workspaces-v2/(.+)\.wasm$ { proxy_http_version 1.1; proxy_set_header Connection ""; - rewrite ^/workspaces-v2/(.+) /$1 break; set $up_workspaces_wasm frontend-svc.workspaces.svc.cluster.local; + rewrite ^/workspaces-v2/(.+) /$1 break; proxy_pass http://$up_workspaces_wasm:80; } @@ -124,8 +132,8 @@ data: location ~^/workflows/(.+).js { proxy_http_version 1.1; proxy_set_header Connection ""; - rewrite /workflows/(.+) /$1 break; set $up_processing frontend-svc.processing.svc.cluster.local; + rewrite /workflows/(.+) /$1 break; proxy_pass http://$up_processing:80; } diff --git a/apps/workspaces/aero/kustomization.yaml b/apps/workspaces/aero/kustomization.yaml new file mode 100644 index 0000000..0ed40ae --- /dev/null +++ b/apps/workspaces/aero/kustomization.yaml @@ -0,0 +1,72 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: workspaces +resources: + - ../base + +patches: + # --- workspaces-api --------------------------------------------------------- + # Патчи адресуются по индексу, поэтому перед каждым replace идёт op: test на + # имя переменной. Если в base порядок envs изменится, сборка упадёт явно, + # вместо того чтобы молча перезаписать соседнее значение. + - target: {kind: HelmRelease, name: backend} + patch: | + # В base адрес django неверный ДВАЖДЫ: сервиса backend не существует + # (он называется backend-svc), и порт 8000 — это targetPort контейнера, + # а сам сервис принимает на 80. Проверено в кластере: + # backend-svc...:80 -> HTTP 200 + # backend-svc...:8000 -> таймаут + # backend...:8000 -> имя не резолвится + # Ровно та же ошибка была в base у processing/engine-low. + - op: test + path: /spec/values/services/backend/envs/11/name + value: DJANGO_HOST + - op: replace + path: /spec/values/services/backend/envs/11/value/_default + value: http://backend-svc.django.svc.cluster.local:80 + + # ENVIRONMENT/ORIGINATOR в base говорят «prod» — это контур, а не прод. + # Значение уезжает в Sentry и в заголовки запросов к смежным сервисам. + - op: test + path: /spec/values/services/backend/envs/10/name + value: ENVIRONMENT + - op: replace + path: /spec/values/services/backend/envs/10/value/_default + value: aero + - op: test + path: /spec/values/services/backend/envs/12/name + value: DJANGO_ORIGINATOR + - op: replace + path: /spec/values/services/backend/envs/12/value/_default + value: aero_ws + + # documentations в контуре нет и не будет. Выключить интеграцию целиком + # нечем: DOCUMENTATION_LOGGER_FEATURE (уже "0" в base) гасит только + # логирование, а GetDocumentByWS вызывается из обработчика GET workspace + # безусловно. Поэтому адрес оставлен «говорящим»: в логах будет видно имя + # несуществующего сервиса, а не молчаливый таймаут в никуда. + # Практическое следствие: карточка воркспейса отдаёт данные без документа. + - op: test + path: /spec/values/services/backend/envs/7/name + value: DOCUMENTATION_HOST + - op: replace + path: /spec/values/services/backend/envs/7/value/_default + value: http://documentations-api-svc.documentations.svc.cluster.local:80 + + # --- frontend --------------------------------------------------------------- + # Идёт из base без единого патча — и это осознанно, а не «руки не дошли». + # + # Фронтенд workspaces — это Module Federation remote с именем srx_workspaces, + # точка входа /module/remoteEntry.js. Его подгружает главный фронтенд + # платформы, поэтому наружу он НЕ публикуется: маршрут ему не нужен, нужен + # проксирующий location внутри nginx главного фронтенда. Он уже есть — + # apps/django/aero/nginx-configmap.yaml, location ~^/workspaces-v2/(.+).js, + # rewrite снимает префикс и проксирует в frontend-svc.workspaces:80. + # + # Версия — именно v2, хотя в репозитории есть и v1 (workspaces-frontend-static + # в apps/workspaces/dsinv). Так решает не выбор, а собранный бандл платформы: + # в образе sarex-frontend-dev:contour_5.22.0 лежит строка + # "/workspaces-v2/module/remoteEntry.js" и имя ремоута srx_workspaces, + # обращений к v1 в нём нет вовсе. Плюс у v1 нет ни одной contour-сборки: + # в его CI (platform/workspaces-frontend) слова contour не встречается. diff --git a/clusters/aero/apps/kustomization.yaml b/clusters/aero/apps/kustomization.yaml index 25a8d19..388022c 100644 --- a/clusters/aero/apps/kustomization.yaml +++ b/clusters/aero/apps/kustomization.yaml @@ -9,3 +9,6 @@ resources: # Движок workflow. Развёрнут частично: только api и engine-low, без # собственного фронтенда — подробности в apps/processing/aero. - ../../../apps/processing/aero + # Воркспейсы: api и фронтенд-ремоут. Фронтенд наружу не публикуется — его + # подгружает главный фронтенд платформы через свой nginx (/workspaces-v2/). + - ../../../apps/workspaces/aero