Compare commits

...

17 Commits

Author SHA1 Message Date
emelinda
da2a0b33a7 Reload: литеральный скаляр вместо свёрнутого, иначе команды склеиваются
Задача падала на первом же прогоне:
  error: unknown flag: --timeout
Свёрнутый скаляр (>-) схлопывает переносы строк в пробелы, две команды
превращаются в одну —
  kubectl rollout restart deploy/x kubectl rollout status deploy/x --timeout=...
и флаг уезжает к restart, который его не знает. С литеральным (|) переносы
сохраняются.

Баг был с самого добавления задачи: до сих пор её никто не запускал, оба
переката до этого делались вручную.
2026-08-10 08:26:24 +03:00
emelinda
cb4130ad9d Reload: добавлен django/celery — он держит django-configmap
celery монтирует production.py из django-configmap, но его envs в оверлее не
правятся, поэтому шаблон пода не меняется и Kubernetes его не пересоздаёт.
backend в такой же ситуации перекатывается сам — у него envs поправлены.

Без переката celery остался бы со старым HOST и строил бы ссылки на домен
чужого контура, тогда как backend уже отдаёт правильные.
2026-08-10 08:23:10 +03:00
emelinda
b5471e664d Django: убран домен чужого контура из настроек и адресов backend
Браузер получал 404 на
  https://sarex.contour.infra.sarex.tech/media/state/previews/....png
то есть backend отдавал АБСОЛЮТНЫЕ ссылки на чужой хост. Файлы при этом
лежали у нас — уходил не тот домен.

Источников оказалось три, и все в base:

  * django-configmap, HOST = "https://sarex.contour.infra.sarex.tech" —
    именно по нему строятся абсолютные ссылки на медиа;
  * там же CSRF_TRUSTED_ORIGINS с тем же доменом: без нашего домена django
    отклонял бы POST-формы админки по проверке Referer;
  * envs backend SERVER_API_HOST и SERVER_HOST. У celery те же две
    переменные я поправил раньше, а у backend пропустил.

Конфигмап скопирован в оверлей целиком: все настройки лежат одним значением
data.production.py на 331 строку, kustomize заменяет его только целиком —
та же причина, что у nginx-configmap рядом. Изменены ровно две строки,
остальное побайтно совпадает с base, так что diff остаётся читаемым.

После правки домена sarex.contour.infra.sarex.tech в рендере
clusters/aero/apps не остаётся ни одного вхождения.
2026-08-10 08:13:07 +03:00
emelinda
2474d82cea Istio: маршруту /media/ нужен rewrite, иначе ключ уезжает с префиксом
s3-proxy не срезает префикс сам — он отображает путь URL прямо в ключ
объекта. Маршрут отдавал ему путь как есть, поэтому он искал
media/orthophotos/..., тогда как в бакете лежит orthophotos/..., и отвечал
NoSuchKey (404).

Проверено на существующем объекте через сам s3-proxy:
  /orthophotos/NTG230721_DEM/14/10920/4937.png       -> 200, 17831 байт
  /media/orthophotos/NTG230721_DEM/14/10920/4937.png -> 404 NoSuchKey

В корне бакета лежат geotiff_dems/, las_pointclouds/, orthophotos/,
pointclouds/ — префикса media/ нет вовсе.
2026-08-10 01:44:53 +03:00
emelinda
1f336b6f93 Django: s3-proxy ходил в ЧУЖОЙ MinIO
Раздача медиа отвечала 500 на каждую плитку, а под капотом был настоящий
ответ S3:
  SignatureDoesNotMatch: The request signature we calculated does not match
  the signature you provided
  status code: 403, request id: 18CA4386CBFA3E5D

Ключ к разгадке — что ответ пришёл БЫСТРО (0.07 с) и с настоящим request id.
То есть это не таймаут до недоступного хоста: s3-proxy реально достучался до
чужого MinIO. В base адрес зашит внутри vault-шаблона:
  AWS_API_ENDPOINT=https://minio.contour.infra.sarex.tech
и это домен другого контура. Наши ключи там не подходят — к счастью, иначе
контур молча читал бы и писал в чужое хранилище.

Ровно та же ловушка, что была у backend и celery: значение внутри шаблона
аннотации, через values не перекрывается, поэтому заменяется аннотация
целиком. Тогда я поправил два релиза из трёх и до s3-proxy не дошёл.

После правки чужой домен в рендере clusters/aero/apps не встречается ни разу.
2026-08-10 01:35:27 +03:00
emelinda
be205b3b14 Processing: один секрет на все файлы задач, добавлен django-auth.json
Следующая задача падала на
  run.py:91  data = json.load(file)
  json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
то есть снова читала пустой файл — на этот раз django-auth.json.

Аннотации пода показали, почему: VAULT_MOUNT_PATH у движка ОДИН НА ВСЕ
файлы. Он подставляет его в каждую аннотацию, меняя только имя ключа:

  agent-inject-file-django-auth:     django-auth.json
  agent-inject-template-django-auth: {{- with secret
                                     "secrets/data/apps/processing/yc-s3" -}}

Ключа django-auth.json в том секрете не было, агент отрендерил пустоту.

Поэтому путь переименован в secrets/apps/processing/job-files: имя yc-s3
описывало один из файлов и врало о содержимом. В секрет добавлен ключ
django-auth.json — объект {"token": "..."}, а не голая строка, потому что
задача читает файл целиком через json.load. Значение то же, что у
workspaces в DJANGO_BASIC_AUTH: base64 от логина и пароля администратора.

Правило на будущее: появился новый файл, который движок кладёт в под
задачи, — добавляется ключ в этот же секрет, с именем ровно как у файла.
2026-08-10 01:11:53 +03:00
emelinda
3640bc8229 Istio: убран маршрут /workflows/, он перехватывал навигацию SPA
Адрес вида /workflows/<uuid> — это путь ВНУТРИ SPA платформы, который
должна обработать она сама, отдав index.html. Маршрут /workflows/ на
frontend-svc.processing уводил его в nginx микрофронтенда, где такого файла
нет, и браузер получал

  404 Not Found — nginx/1.19.6

Статика микрофронтенда доезжает и без этого маршрута: nginx платформы сам
проксирует /workflows/*.js в processing (apps/django/aero/nginx-configmap).
Это уже проверялось — 200, 282339 байт.

Маршрут /workflows/api/ оставлен: его nginx платформы не обслуживает, а
бандл ходит по нему относительным путём.
2026-08-10 01:05:37 +03:00
emelinda
f3c2fa194b Processing: у секрета задач своя схема JSON, не как у движка
403 Forbidden на HeadObject объяснялся не кредами и не отсутствием объекта —
и то и другое я проверил, они в порядке. Дело в схеме файла.

Движок читает /vault/secrets/processing-s3 с ключами host/bucket/verify, а
под задачи разбирает свой файл моделью S3AccountModel с ключами
endpoint/bucket_name/access_key_id/secret_access_key/use_ssl. Совпадают
только два имени из пяти.

Коварство в том, что pydantic на формате движка НЕ падает: access_key_id и
secret_access_key подходят по имени, а endpoint и bucket_name молча берут
значения по умолчанию. Поэтому вместо понятной ошибки конфигурации задача
шла в чужое хранилище и получала 403 — креды при этом верные.

endpoint пишется без схемы, протокол задаёт use_ssl; у нас MinIO внутри
кластера по http.
2026-08-10 00:50:03 +03:00
emelinda
ab086a86b4 Processing: S3 задачам через Vault и секрет реестра dockerhub
Два шага к тому, чтобы под задачи наконец поехал.

--- 1. S3 через vault-аннотации, а не Secret в namespace ---

Аннотации созданного пода показали точное поведение движка:
  agent-inject-secret-yc-s3:   <VAULT_MOUNT_PATH>
  agent-inject-template-yc-s3: {{- with secret "<VAULT_MOUNT_PATH>" -}}
                               {{ index .Data.data "yc-s3-service-account.json" }}
  secret-volume-path-yc-s3:    /etc/sarex/yc-s3

То есть VAULT_MOUNT_PATH вопреки имени — не корень KV, а ПОЛНЫЙ путь
секрета, а имя ключа в нём должно совпадать с именем файла. Путь исправлен
на secrets/data/apps/processing/yc-s3 (форма чтения KV v2), и заведён
соответствующий секрет с ключом yc-s3-service-account.json.

Инжекция уже подтверждена на живом поде: agent-inject-status: injected,
том vault-secrets-custom-0, Secret yc-s3 в namespace больше не монтируется.

--- 2. Секрет реестра для подов задач ---

Под задачи вставал в Init:0/1:
  Unable to retrieve some image pull secrets (dockerhub)
Движок проставляет Job'ам imagePullSecrets: dockerhub, и это имя зашито в
бинарник — настраиваемой переменной нет, из pull-настроек существует только
DEFAULT_IMAGE_PULL_POLICY. Поэтому заводим секрет с таким именем в namespace
задач через существующий механизм registry_secrets; содержимое то же, что у
regcred.
2026-08-10 00:22:48 +03:00
emelinda
36313e8f6f Processing: vault-аннотации для подов задач (VAULT_ROLE/SA/MOUNT_PATH)
Сборка contour_a03d37da-dirty2 умеет не монтировать в Job готовый Secret, а
вешать на него аннотации vault-agent — тогда файл с кредами кладёт агент, и
Secret в namespace задач не нужен. Так это и работает в UGMK, где никакого
yc-s3 в namespace нет.

В master workflows-engine этого кода нет (сборка с невлитой ветки), поэтому
набор переменных восстановлен по строкам бинарника: VAULT_ROLE, VAULT_SA,
VAULT_MOUNT_PATH плюс аннотации role/secret-volume-path/agent-inject-*.

В base задан только VAULT_USE=true, остальных трёх нет ни в одном кластере
репозитория. Оттого аннотации выходили неполными, агент в под задачи ничего
не клал и файл читался как пустой.

Значения предварительные: точный путь Vault, который движок запрашивает,
будет виден в аннотациях созданного пода задачи — по нему и уточним.
2026-08-10 00:10:31 +03:00
emelinda
0c0c3ebd9d Processing: поправлено обоснование про IGNORE_TAINTS_AND_NODE_SELECTOR
В комментарии стоял тег contour_3ef5b462 — он был взят грепом по репозиторию
ДО первого слияния с master, а master поднял образ до contour_a03d37da-dirty2
(коммит f55f254), и в кластере всё это время крутится именно он. Утверждение
"образ о переменной не знает" поэтому необоснованно: сборка ручная и сделана
тем же коммитом, что добавил переменную.

Сам вывод не меняется: селектор задаётся явно, потому что проверить поддержку
переменной нечем, а не потому что её точно нет.
2026-08-09 23:58:50 +03:00
emelinda
c8555d432c Merge remote-tracking branch 'origin/master' into aero 2026-08-09 23:38:14 +03:00
ivan
bba2c08416 ++ 2026-08-09 15:02:31 +05:00
ivan
12f219c19b ++ 2026-08-09 14:54:02 +05:00
ivan
2f2289bd89 ++ 2026-08-09 14:52:30 +05:00
ivan
1106008b19 ++ 2026-08-09 13:50:12 +05:00
ivan
b629d7a699 ++ 2026-08-09 13:30:52 +05:00
19 changed files with 1441 additions and 28 deletions

View File

@ -89,6 +89,13 @@ registry_secrets:
- {name: regcred, namespace: processing}
- {name: regcred, namespace: workspaces}
- {name: regcred, namespace: bim}
# Поды задач workflows. Имя dockerhub выбрано не нами: движок проставляет
# его создаваемым Job'ам жёстко, строка зашита в бинарник, настраиваемой
# переменной нет (из pull-настроек есть только DEFAULT_IMAGE_PULL_POLICY).
# Без него под задачи встаёт в Init:0/1 с
# Unable to retrieve some image pull secrets (dockerhub)
# Содержимое то же, что у regcred, — доступ в тот же cr.yandex.
- {name: dockerhub, namespace: processing}
# Поднимать стек (docker compose up -d). Требует запущенной службы docker и
# docker login в cr.yandex — по умолчанию выключено.
@ -168,6 +175,14 @@ sarex_reload_workloads:
- namespace: django
deployment: frontend
why: nginx-configmap
# celery монтирует django-configmap (настройки production.py). У backend та
# же зависимость, но он перекатывается сам: его envs правятся в оверлее, а
# значит меняется шаблон пода. У celery envs не менялись — шаблон прежний, и
# Kubernetes причин пересоздавать под не видит. Итог: backend уже работает с
# новым доменом, а celery держал бы старый и строил ссылки на чужой контур.
- namespace: django
deployment: celery
why: django-configmap
# --- GitOps-подложка контура (k3s + gitea + vault + flux) -------------------
# Параметры gitea и Flux. Попадают в .env (их читает docker-compose) — менять
@ -381,6 +396,45 @@ vault_app_secrets:
access_key_id: "{{ sarex_minio_k8s_user }}"
secret_access_key: "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}"
region: us-east-1
# S3 для ПОДОВ ЗАДАЧ workflows — отдельно от секрета самого движка.
#
# Движок (VAULT_USE=true) вешает на создаваемый Job аннотации vault-agent и
# подставляет VAULT_MOUNT_PATH в шаблон как полный путь секрета, а имя ключа
# берёт равным имени файла. Поэтому здесь ключ называется именно
# yc-s3-service-account.json, а не как-нибудь читаемо, и путь обязан
# совпадать с VAULT_MOUNT_PATH в apps/processing/aero.
#
# СХЕМА ЗДЕСЬ ДРУГАЯ, чем у секрета самого движка, и это главная ловушка.
# Движок читает /vault/secrets/processing-s3 с ключами host/bucket/verify, а
# задача разбирает свой файл моделью S3AccountModel с ключами
# endpoint / bucket_name / access_key_id / secret_access_key / use_ssl
# Совпадают только два имени из пяти. Если положить сюда формат движка,
# pydantic НЕ упадёт — access_key_id и secret_access_key подойдут, — а
# endpoint и bucket_name молча возьмут значения по умолчанию, и задача
# получит 403 Forbidden на HeadObject: креды верные, адрес и бакет чужие.
#
# endpoint БЕЗ схемы: протокол задаёт use_ssl. У нас MinIO внутри кластера по
# http, поэтому use_ssl: false.
#
# Путь ОДИН НА ВСЕ файлы задач: движок подставляет VAULT_MOUNT_PATH в каждую
# аннотацию, меняя только имя ключа. Добавляется файл — добавляется ключ
# СЮДА, с именем ровно как у файла.
- path: secrets/apps/processing/job-files
data:
# Basic-токен администратора django. Задача читает файл целиком как JSON
# (run.py: json.load), поэтому здесь именно объект с ключом token, а не
# голая строка. Значение — то же, что у workspaces в DJANGO_BASIC_AUTH:
# base64 от "логин:пароль" учётки, которую заводит роль.
# Без ключа файл рендерится пустым и задача падает на
# json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
django-auth.json: >-
{"token": "{{ (superuser_name ~ ':' ~ superuser_password) | b64encode }}"}
yc-s3-service-account.json: >-
{"endpoint": "minio.minio.svc.cluster.local:9000",
"bucket_name": "{{ sarex_django_s3_bucket }}",
"access_key_id": "{{ sarex_minio_k8s_user }}",
"secret_access_key": "{{ sarex_secrets.SAREX_MINIO_ROOT_PASSWORD }}",
"use_ssl": false}
# --- Секреты приложения measurements ---------------------------------------
# Структура задана vault-шаблоном в apps/measurements/base/backend.yaml: он
# собирает из этих ключей одну переменную S3_JSON_SETTINGS и читает endpoint

View File

@ -27,11 +27,16 @@
# uv run poe reload -- -e "sarex_reload_workloads=[{'namespace':'django','deployment':'frontend'}]"
- name: Перекатить нагрузки с изменённой конфигурацией
# Скаляр ЛИТЕРАЛЬНЫЙ (|), а не свёрнутый (>-). Со свёрнутым переносы строк
# схлопываются в пробелы, две команды склеиваются в одну и получается
# kubectl rollout restart deploy/x kubectl rollout status deploy/x --timeout=...
# то есть флаг уезжает к restart, который его не знает:
# error: unknown flag: --timeout
ansible.builtin.shell:
cmd: >-
cmd: |
{{ compose_cmd }} exec -T k3s-server sh -ec '
kubectl -n {{ item.namespace }} rollout restart deploy/{{ item.deployment }}
kubectl -n {{ item.namespace }} rollout status deploy/{{ item.deployment }} --timeout={{ sarex_reload_timeout }}
kubectl -n {{ item.namespace }} rollout restart deploy/{{ item.deployment }}
kubectl -n {{ item.namespace }} rollout status deploy/{{ item.deployment }} --timeout={{ sarex_reload_timeout }}
'
chdir: "{{ deploy_dir }}"
loop: "{{ sarex_reload_workloads }}"

View File

@ -0,0 +1,348 @@
# Копия django-configmap из base с заменой домена на домен контура.
#
# Патчем этого не сделать: все настройки лежат ОДНИМ значением data.production.py
# (331 строка python), kustomize умеет заменить его только целиком. Ровно та же
# причина, что у nginx-configmap.yaml рядом.
#
# ЧТО ИЗМЕНЕНО — две строки, обе про домен sarex.contour.infra.sarex.tech,
# принадлежащий ДРУГОМУ контуру:
#
# HOST — по нему backend строит АБСОЛЮТНЫЕ ссылки на медиа.
# Браузер получал
# https://sarex.contour.infra.sarex.tech/media/state/previews/....png
# и, разумеется, 404: чужой хост, наших файлов там нет.
# CSRF_TRUSTED_ORIGINS — без нашего домена django отклонял бы POST-формы
# админки с ошибкой проверки Referer.
#
# Больше в файле ничего не менялось: сравнивать с base удобно обычным diff.
apiVersion: v1
kind: ConfigMap
metadata:
name: django-configmap
namespace: django
data:
production.py: |
import ast
import os
from .base import *
from logging.handlers import SysLogHandler
from datetime import timedelta
def _load_env_file(path):
try:
with open(path, "r", encoding="utf-8") as f:
for raw_line in f:
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'):
try:
value = ast.literal_eval(value)
except (ValueError, SyntaxError):
value = value[1:-1]
if key and key not in os.environ:
os.environ[key] = value
except FileNotFoundError:
pass
def _read_secret_file(path, default=""):
try:
with open(path, "r", encoding="utf-8") as f:
return f.read().strip()
except FileNotFoundError:
return default
# Fallback for manage.py launched via `kubectl exec` (outside entrypoint),
# so Django can still read DB/JWT values from Vault-injected files.
_load_env_file("/vault/secrets/django-postgresql")
_load_env_file("/vault/secrets/django-rabbitmq")
_load_env_file("/vault/secrets/django-s3")
_load_env_file("/vault/secrets/django-kafka")
_load_env_file("/vault/secrets/django-common")
if not os.environ.get("JWT_PRIVATE_KEY"):
os.environ["JWT_PRIVATE_KEY"] = _read_secret_file("/vault/secrets/django-jwt-private")
if not os.environ.get("JWT_PUBLIC_KEY"):
os.environ["JWT_PUBLIC_KEY"] = _read_secret_file("/vault/secrets/django-jwt-public")
ALLOWED_HOSTS = ["*"]
FILE_UPLOAD_PERMISSIONS = 0o644
DEBUG = False
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ["https://sarex.local.lonsdaleites.ru", "http://sarex.local.lonsdaleites.ru"]
SESSION_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = False
SECRET_KEY = 't2=9+($2f%7ptsdy4!rby$)mcfl1l%o2e@vs^d(g&(wwi&%k1v'
CORS_ORIGIN_ALLOW_ALL = True
SERVERSETTINGS.cache_enabled = True
INSTALLED_APPS = list(INSTALLED_APPS) + ['corsheaders']
CORS_ALLOW_METHODS = (
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
)
BASIC_USER_ID = 2
CORS_ALLOW_HEADERS = (
'accept',
'accept-encoding',
'authorization',
'content-type',
'user-agent',
'x-csrftoken',
'x-requested-with',
'x-token',
'Bearer',
)
HOST = "https://sarex.local.lonsdaleites.ru"
POSTGRES_DATABASE = os.environ.get('DJANGO_POSTGRES_DATABASE')
POSTGRES_USER = os.environ.get('DJANGO_POSTGRES_USER')
POSTGRES_PASSWORD = os.environ.get('DJANGO_POSTGRES_PASSWORD')
POSTGRES_HOST = os.environ.get('DJANGO_POSTGRES_HOST')
POSTGRES_PORTS = os.environ.get('DJANGO_POSTGRES_PORTS', "5432")
DATABASES = {
'default': {
'ENGINE': 'django_prometheus.db.backends.postgresql',
'NAME': POSTGRES_DATABASE,
'USER': POSTGRES_USER,
'PASSWORD': POSTGRES_PASSWORD,
'HOST': POSTGRES_HOST,
'PORT': POSTGRES_PORTS,
}
}
MEASUREMENTSETTINGS = MeasurementSettings()
WORKFLOWSSETTINGS = WorkFlowsSettings()
S3SETTINGS = S3Settings()
GATEWAYSETTINGS = GateWaySetttings()
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
}
},
'formatters': {
'verbose': {
'format': '[contactor] %(levelname)s %(asctime)s %(message)s',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
'sentry': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'logging.StreamHandler',
},
},
'loggers': {
'': {
'handlers': ['console', 'sentry'],
'level': 'INFO',
'propagate': False,
},
}
}
COMPARATOR_JWT = os.environ.get("COMPARATOR_JWT", "default_jwt")
COMPARATOR_URL = os.environ.get("COMPARATOR_URL", "https://wb.sarex.io/comparator")
COMPARATOR_SECTION = os.environ.get("COMPARATOR_SECTION", "sarex-production-storage")
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'RS512',
'SIGNING_KEY': os.environ.get("JWT_PRIVATE_KEY", "").replace("\\n", "\n"),
'VERIFYING_KEY': os.environ.get("JWT_PUBLIC_KEY", "").replace("\\n", "\n"),
'AUDIENCE': None,
'ISSUER': os.environ.get('SIMPLE_JWT_ISSUER', 'default_issuer'),
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
DEFAULT_FILE_STORAGE = 'sarex.core.storages.CustomS3Boto3Storage'
DATA_UPLOAD_MAX_MEMORY_SIZE = 268435456
if not os.environ.get('ISOLATED', False):
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn="https://3df2f4b8d3d14595a06c92e9d7c562cb@sentry.io/1501541",
integrations=[DjangoIntegration()],
environment=os.environ.get('SENTRY_ENVIRONMENT', 'production'),
send_default_pii=True,
)
COMPARISON_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/comparisons"
DOCUMENTATION_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/documentations"
PDM_FILES_API_URL = f"{os.environ.get('WORKFLOWSSETTINGS_HOST')}/files"
WORKFLOWS_TASKS = {
"update_orthomosaic_data": {
"image": f"{os.environ.get('WORKFLOWSSETTINGS_REGISTRY')}/update-orthomosaic-data:dev",
"service_requests": ["django-auth"],
"backoff_limit": 3,
},
}
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': (
'rest_framework.pagination.LimitOffsetPagination' ),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
'PAGE_SIZE': 1000, 'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend' ],
'DEFAULT_AUTHENTICATION_CLASSES': [
'sarex.authentication.backends.ZitadelJWTAuthentication',
'rest_framework.authentication.RemoteUserAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'sarex.authentication.backends.JWTAuthentication' ],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', ] }
AUTHENTICATION_BACKENDS = [
'sarex.authentication.backends.CustomRemoteUserBackend',
'django.contrib.auth.backends.ModelBackend',
'guardian.backends.ObjectPermissionBackend',
]
MIDDLEWARE = [
'django_prometheus.middleware.PrometheusBeforeMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
#'django_keycloak.middlewares.AuthorizationHeaderMiddleware',
#'django_keycloak.middlewares.KeycloakSessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
#'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_user_agents.middleware.UserAgentMiddleware',
'simple_history.middleware.HistoryRequestMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware', ]
class KeyCloakSettings(BaseSettings):
client_id: str = "client_id"
client_secret: str = "client_secret"
discovery_url: str = "https://login.wb.sarex.io/realms/sarex/.well-known/openid-configuration"
staff: Optional[str] = "Sarex staff"
superuser: Optional[str] = "Sarex superusers"
sync_with_django: bool = True
sync_admin: bool = False
group_prefix: str = 'Sarex-Role'
company_prefix: str = 'Sarex-Company'
department_prefix: str = 'Sarex-Department'
position_prefix: str = 'Sarex-Position'
separator: str = '__'
sync_user_groups: bool = False
sync_user_positions: bool = False
sync_user_departments: bool = False
sync_user_companies: bool = False
use_redirect_logout: bool = False
logout_redirect_uri: str = "/"
default_group_name: Optional[str] = 'Тест'
default_company_name: Optional[str] = 'Брусника'
trusted_uri: List[str] = ['/api/core/orthophotos/', '/api/token', '/api/token/me']
trusted_uri: List[str] = []
class Config:
env_prefix = "KC_"
KEYCLOAKSETTINGS = KeyCloakSettings()
REMOTE_USER_DEFAULT_COMPANY_ID = 1
SAREX_MODULES = [
{
"name": "Замечания",
"uri": "/remarks"
},
# {
# "name": "Управление проектами",
# "uri": "/management/projects",
# },
{
"name": "Замечания V2",
"uri": "/issues"
},
{
"name": "Документация",
"uri": "/documentations",
},
{
"name": "Согласование документов",
"uri": "/reviews"
},
{
"name": "Рабочие процессы",
"uri": "/processes"
},
{
"name": "Запросы",
"uri": "/rfi"
},
{
"name": "Управление проектами",
"uri": "/management/projects"
},
# {
# "name": "Обзор",
# "uri": "/projects"
# },
{
"name": "Передача документации",
"uri": "/transmittal"
},
]
AUTH_SETTINGS = {
"refresh_token": False,
"refresh_token_uri": "/api/token/me",
"refresh_oauth_token": True,
"refresh_oauth_token_uri": "/oauth/token",
"refresh_time": 240,
}
DEBUG=True
WEB_APP_AUTH_MODE='jwt-session-based'
SAREX_MODULES_SETTINGS = {
"aero": {
"enable_new_media": True
},
"sso_logout_redirect": True
}

View File

@ -19,6 +19,15 @@ patches:
target:
kind: ConfigMap
name: nginx-configmap
# Настройки django: в base захардкожен домен ЧУЖОГО контура. По нему backend
# строил абсолютные ссылки на медиа, и браузер уходил на
# https://sarex.contour.infra.sarex.tech/media/state/previews/....png
# получая 404. Причина копии та же, что у nginx-configmap: настройки лежат
# одним значением data.production.py, заменить можно только целиком.
- path: django-configmap.yaml
target:
kind: ConfigMap
name: django-configmap
# --- celery: те же правки, что и у backend --------------------------------
# Worker крутит тот же образ и те же настройки, поэтому и расхождения с base
# у него ровно те же: нет Kafka, нет Zitadel, MinIO живёт внутри кластера.
@ -101,6 +110,32 @@ patches:
path: /spec/values/services/celery/image/name/_default
value: cr.yandex/crp3ccidau046kdj8g9q/backend:production_f813140d
# --- s3-proxy: S3 внутри кластера ------------------------------------------
# В base адрес MinIO зашит ВНУТРИ vault-шаблона:
# AWS_API_ENDPOINT=https://minio.contour.infra.sarex.tech
# Это чужой контур. Через values не перекрыть — значение внутри шаблона
# аннотации, поэтому заменяем аннотацию целиком, как у backend и celery.
#
# Проявлялось не отказом в соединении, а тем, что s3-proxy ДОСТУЧАЛСЯ до
# чужого MinIO и получил от него настоящий ответ S3:
# SignatureDoesNotMatch: The request signature we calculated does not
# match the signature you provided
# наружу это выглядело как 500 на каждую плитку /media/orthophotos/...
# Наши ключи в чужом хранилище, разумеется, не подходят — и хорошо, что так:
# иначе контур молча читал бы и писал в чужой S3.
- target: {kind: HelmRelease, name: s3-proxy}
patch: |
- op: replace
path: /spec/values/services/s3-proxy/podAnnotations/_default/vault.hashicorp.com~1agent-inject-template-s3
value: |
{{- with secret "secrets/data/minio/apps/django" }}
AWS_API_ENDPOINT=http://minio.minio.svc.cluster.local:9000
{{- $buckets := index .Data.data "buckets" }}
AWS_S3_BUCKET={{ if gt (len $buckets) 0 }}{{ index (index $buckets 0) "name" }}{{ else }}django{{ end }}
AWS_ACCESS_KEY_ID={{ index .Data.data "access_key" }}
AWS_SECRET_ACCESS_KEY={{ index .Data.data "secret_key" }}
{{- end }}
# --- backend: убираем зависимости, которых в контуре нет ------------------
- target: {kind: HelmRelease, name: backend}
patch: |
@ -153,6 +188,21 @@ patches:
path: /spec/values/services/backend/envs/2/value/_default
value: "False"
# Адреса на чужой контур. У celery те же две переменные поправлены выше,
# у backend они оставались с sarex.contour.infra.sarex.tech.
- op: test
path: /spec/values/services/backend/envs/17/name
value: SERVER_API_HOST
- op: replace
path: /spec/values/services/backend/envs/17/value/_default
value: https://sarex.local.lonsdaleites.ru
- op: test
path: /spec/values/services/backend/envs/18/name
value: SERVER_HOST
- op: replace
path: /spec/values/services/backend/envs/18/value/_default
value: https://sarex.local.lonsdaleites.ru
# --- Администратор платформы --------------------------------------------
# Учётка приезжает из Vault под именами, которые ждёт штатная
# createsuperuser --noinput. Секрет кладёт ansible

View File

@ -42,7 +42,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/flows-backend:production_858029dd
_default: cr.yandex/crp3ccidau046kdj8g9q/flows-backend:production_a7dc8216
pullPolicy:
_default: IfNotPresent
deployment:

View File

@ -42,7 +42,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/flows-backend_worker:production_858029dd
_default: cr.yandex/crp3ccidau046kdj8g9q/flows-backend_worker:production_a7dc8216
pullPolicy:
_default: IfNotPresent

View File

@ -0,0 +1,167 @@
---
# Патч backend для контура УГМК — подключение к Kafka.
#
# ВНИМАНИЕ: envs — список, kustomize заменяет его ЦЕЛИКОМ (JSON merge patch),
# поэтому здесь продублирован весь набор из apps/flows/base/backend.yaml.
# args — тоже список, продублирован весь скрипт с добавленным sourcing flows-kafka.
# podAnnotations — map, мержится по ключам, поэтому переопределён только
# один ключ agent-inject-template-flows-kafka; остальные vault-аннотации берутся из base.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: backend
namespace: flows
spec:
values:
services:
backend:
deployment:
args:
_default:
- |
set -a
[ -f /vault/secrets/flows-postgresql ] && . /vault/secrets/flows-postgresql
[ -f /vault/secrets/flows-documentations-db ] && . /vault/secrets/flows-documentations-db
[ -f /vault/secrets/flows-rabbitmq ] && . /vault/secrets/flows-rabbitmq
[ -f /vault/secrets/flows-django-auth ] && . /vault/secrets/flows-django-auth
[ -f /vault/secrets/flows-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/flows-jwt-public)"
[ -f /vault/secrets/flows-kafka ] && . /vault/secrets/flows-kafka
set +a
exec /opt/entrypoint.sh
envs:
- name: LOG_LEVEL
value:
_default: "DEBUG"
- name: BASE_HOST
value:
_default: "https://srx.wb.ru"
- name: CELERY_QUEUE
value:
_default: "flow"
- name: EAV_HOST
value:
_default: "http://backend-svc.eav.svc.cluster.local:80"
- name: DJANGO_HOST
value:
_default: "http://backend-svc.django.svc.cluster.local:80/api"
- name: PLANNING_HOST
value:
_default: "http://backend-svc.pm.svc.cluster.local:8000/api/pm/msp"
- name: PLANNING_USE
value:
_default: "True"
- name: DOCUMENTATION_HOST
value:
_default: "http://backend-api-svc.documentations.svc.cluster.local:80/internal/v1"
- name: DOCUMENTATION_EXTERNAL_HOST
value:
_default: "http://backend-api-svc.documentations.svc.cluster.local:80/api/v1"
- name: ENABLE_ANALYTICS
value:
_default: "1"
- name: ENABLE_CELERY
value:
_default: "1"
- name: ENABLE_MAILGUN
value:
_default: "0"
- name: ENABLE_METRICS
value:
_default: "0"
- name: FROM_EMAIL
value:
_default: "sarex@rwb.ru"
- name: GATEWAY_URL
value:
_default: "http://pdm-svc.documentations.svc.cluster.local:80"
- name: RESOURCE_URL
value:
_default: "http://iam-backend.iam.svc.cluster.local:8000"
- name: SERVICE_HOST
value:
_default: "https://srx.wb.ru/flows/api/v1"
- name: SMTP_HOST
value:
_default: "mail.rwb.ru"
- name: CHECKLIST_HOST
value:
_default: "http://checklists-backend-service.checklists.svc.cluster.local:80"
- name: SMTP_PORT
value:
_default: "465"
- name: SYNC_RESOURCE_ID
value:
_default: "1"
- name: TIMEOUT
value:
_default: "120"
- name: WORKFLOWS_HOST
value:
_default: "http://backend-svc.processing.svc.cluster.local:80/api/v1"
- name: WORKFLOWS_TIMEOUT
value:
_default: "60"
- name: DOCUMENTATION_TIMEOUT
value:
_default: "60"
- name: KAFKA_HOST
value:
_default: "kafka-kafka-contour.kafka.svc.cluster.local:9092"
- name: KAFKA_SSL_CERT
value:
_default: |
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIQI30u4fwgUnGBxyLicuU5EzANBgkqhkiG9w0BAQsFADAT
MREwDwYDVQQDEwhrYWZrYS1jYTAeFw0yNjA3MTMxNzQwNDJaFw0yNzA3MTMxNzQw
NDJaMBMxETAPBgNVBAMTCGthZmthLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAudTWG0AwdqIMqbqGwjf9CY0BMMQm+HDPRkzMnXqVCSqAYLBLuPBC
h4+qm+IKAIpiJrO5pCDxeU1mVqMgRnXcdYyuefCzlTAKMNuM1CVM4ou695B0zKiy
D+7NHsdhuoH/ne3jCa8VpylDfKWsxcq3tDCdtZ5drPK80g2o0eo2LHx4erJtSABV
tqCrcIuy1nClAwGY4ejO+psr+gIzrHdYzve6YRRFBqVp6sJHcW1Cj6wOpHax4pcM
Srh5EcZ4dk434UNOZy2Ebji/9Lwxn0CiXtPTHYGTgNLhujurylFM1nUx69QgsR3C
GRZSY3R8Kegd4bzzhHfoBMQvl5Py+0o85QIDAQABo2EwXzAOBgNVHQ8BAf8EBAMC
AqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFKhTGCqho/YjUZK1ndypuYs970kcMA0GCSqGSIb3DQEBCwUA
A4IBAQBvIzVhkzib6LJxt3tUkDFy87Yf1gG+rRVZiL/ZGrOsQlvBt4eLzZIytAvL
QaZHY+imIyzURj57IFfyFM1fEPRhc9et0VaRYxGwapLljez0jqPedM8rCsunJmVP
KrQxAfvbmHvcZ8k51tpD+YydmoARjBmoRdRIuLTDsuLv0Szg45M/7I4px2ycGN2j
kF7jVwF56u2eIrUGybnsH2qg+MY7devv3rG1Y1YV7HEwMAAeN1n5JLxxuA1AIW/j
TyZD8hICp8OLzWGTG9b3GZJK9FXbDHSKXemJr0hyjoxsW27nHsXcmV5Oe2OkTKw8
fdA17/ibw6gFrH2b3vZKcx8D455V
-----END CERTIFICATE-----
podAnnotations:
_default:
vault.hashicorp.com/agent-inject-secret-flows-kafka: secrets/data/kafka/apps/flows
vault.hashicorp.com/agent-inject-template-flows-kafka: |-
{{- with secret "secrets/data/kafka/apps/flows" -}}
KAFKA_USERNAME={{ index .Data.data "username" }}
KAFKA_PASSWORD={{ index .Data.data "password" }}
{{- end -}}

View File

@ -0,0 +1,168 @@
---
# Патч celery для контура УГМК — подключение к Kafka.
#
# ВНИМАНИЕ: envs — список, kustomize заменяет его ЦЕЛИКОМ (JSON merge patch),
# поэтому здесь продублирован весь набор из apps/flows/base/celery.yaml.
# args — тоже список, продублирован весь скрипт с добавленным sourcing flows-kafka.
# podAnnotations — map, мержится по ключам, поэтому переопределён только
# один ключ agent-inject-template-flows-kafka; остальные vault-аннотации берутся из base.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: celery
namespace: flows
spec:
values:
services:
celery:
deployment:
args:
_default:
- |
set -a
[ -f /vault/secrets/flows-postgresql ] && . /vault/secrets/flows-postgresql
[ -f /vault/secrets/flows-documentations-db ] && . /vault/secrets/flows-documentations-db
[ -f /vault/secrets/flows-rabbitmq ] && . /vault/secrets/flows-rabbitmq
[ -f /vault/secrets/flows-django-auth ] && . /vault/secrets/flows-django-auth
[ -f /vault/secrets/flows-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/flows-jwt-public)"
[ -f /vault/secrets/flows-kafka ] && . /vault/secrets/flows-kafka
set +a
exec celery -A src.worker worker -l INFO -E --concurrency=1 -Q flow
envs:
- name: LOG_LEVEL
value:
_default: "DEBUG"
- name: BASE_HOST
value:
_default: "https://srx.wb.ru"
- name: CELERY_QUEUE
value:
_default: "flow"
- name: EAV_HOST
value:
_default: "http://backend-svc.eav.svc.cluster.local:80"
- name: DJANGO_HOST
value:
_default: "http://backend-svc.django.svc.cluster.local:80/api"
- name: PLANNING_HOST
value:
_default: "http://backend-service.pm.svc.cluster.local:80/api/pm/msp"
- name: PLANNING_USE
value:
_default: "True"
- name: DOCUMENTATION_HOST
value:
_default: "http://backend-api-svc.documentations.svc.cluster.local:80/internal/v1"
- name: DOCUMENTATION_EXTERNAL_HOST
value:
_default: "http://backend-api-svc.documentations.svc.cluster.local:80/api/v1"
- name: ENABLE_ANALYTICS
value:
_default: "1"
- name: ENABLE_CELERY
value:
_default: "1"
- name: ENABLE_MAILGUN
value:
_default: "0"
- name: ENABLE_METRICS
value:
_default: "0"
- name: FROM_EMAIL
value:
_default: "sarex@rwb.ru"
- name: GATEWAY_URL
value:
_default: "http://pdm-api.documentations.svc.cluster.local:8080"
- name: RESOURCE_URL
value:
_default: "http://resources-service.resources.svc.cluster.local:8000"
- name: SERVICE_HOST
value:
_default: "https://srx.wb.ru/flows/api/v1"
- name: SMTP_HOST
value:
_default: "mail.rwb.ru"
- name: CHECKLIST_HOST
value:
_default: "http://checklists-backend-service.checklists.svc.cluster.local:80"
- name: SMTP_PORT
value:
_default: "465"
- name: SYNC_RESOURCE_ID
value:
_default: "1"
- name: TIMEOUT
value:
_default: "120"
- name: WORKFLOWS_HOST
value:
_default: "http://workflows-api-service.workflow.svc.cluster.local:8000/api/v1"
- name: WORKFLOWS_TIMEOUT
value:
_default: "60"
- name: DOCUMENTATION_TIMEOUT
value:
_default: "60"
# См. подробные комментарии в apps/flows/d8-ugmk-prod/backend.yaml.
- name: KAFKA_HOST
value:
_default: "kafka-kafka-contour.kafka.svc.cluster.local:9092"
- name: KAFKA_SSL_CERT
value:
_default: |
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIQI30u4fwgUnGBxyLicuU5EzANBgkqhkiG9w0BAQsFADAT
MREwDwYDVQQDEwhrYWZrYS1jYTAeFw0yNjA3MTMxNzQwNDJaFw0yNzA3MTMxNzQw
NDJaMBMxETAPBgNVBAMTCGthZmthLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAudTWG0AwdqIMqbqGwjf9CY0BMMQm+HDPRkzMnXqVCSqAYLBLuPBC
h4+qm+IKAIpiJrO5pCDxeU1mVqMgRnXcdYyuefCzlTAKMNuM1CVM4ou695B0zKiy
D+7NHsdhuoH/ne3jCa8VpylDfKWsxcq3tDCdtZ5drPK80g2o0eo2LHx4erJtSABV
tqCrcIuy1nClAwGY4ejO+psr+gIzrHdYzve6YRRFBqVp6sJHcW1Cj6wOpHax4pcM
Srh5EcZ4dk434UNOZy2Ebji/9Lwxn0CiXtPTHYGTgNLhujurylFM1nUx69QgsR3C
GRZSY3R8Kegd4bzzhHfoBMQvl5Py+0o85QIDAQABo2EwXzAOBgNVHQ8BAf8EBAMC
AqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFKhTGCqho/YjUZK1ndypuYs970kcMA0GCSqGSIb3DQEBCwUA
A4IBAQBvIzVhkzib6LJxt3tUkDFy87Yf1gG+rRVZiL/ZGrOsQlvBt4eLzZIytAvL
QaZHY+imIyzURj57IFfyFM1fEPRhc9et0VaRYxGwapLljez0jqPedM8rCsunJmVP
KrQxAfvbmHvcZ8k51tpD+YydmoARjBmoRdRIuLTDsuLv0Szg45M/7I4px2ycGN2j
kF7jVwF56u2eIrUGybnsH2qg+MY7devv3rG1Y1YV7HEwMAAeN1n5JLxxuA1AIW/j
TyZD8hICp8OLzWGTG9b3GZJK9FXbDHSKXemJr0hyjoxsW27nHsXcmV5Oe2OkTKw8
fdA17/ibw6gFrH2b3vZKcx8D455V
-----END CERTIFICATE-----
podAnnotations:
_default:
vault.hashicorp.com/agent-inject-secret-flows-kafka: secrets/data/kafka/apps/flows
vault.hashicorp.com/agent-inject-template-flows-kafka: |-
{{- with secret "secrets/data/kafka/apps/flows" -}}
KAFKA_USERNAME={{ index .Data.data "username" }}
KAFKA_PASSWORD={{ index .Data.data "password" }}
{{- end -}}

View File

@ -8,3 +8,11 @@ patches:
target:
kind: Namespace
name: flows
- path: backend.yaml
target:
kind: HelmRelease
name: backend
- path: celery.yaml
target:
kind: HelmRelease
name: celery

View File

@ -42,7 +42,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/issues:production_17c438aa
_default: cr.yandex/crp3ccidau046kdj8g9q/issues:production_2dc38909
pullPolicy:
_default: IfNotPresent

View File

@ -42,7 +42,7 @@ spec:
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/issues:production_17c438aa
_default: cr.yandex/crp3ccidau046kdj8g9q/issues:production_2dc38909
pullPolicy:
_default: IfNotPresent

View File

@ -48,9 +48,9 @@ data:
WORKFLOWS_URL = "http://workflows-api-service.workflow.svc.cluster.local:8000"
DOCUMENTATIONS_URL = "http://backend-api-svc.documentations.svc.cluster.local:80"
RESOURCES_API_HOST = os.getenv("RESOURCES_API_HOST", default="http://resources-service.resources.svc.cluster:8000")
KAFKA_HOST = "wb-stage-kafka-bootstrap.kafka.svc.cluster.local:9093"
KAFKA_USERNAME = "sarex"
KAFKA_PASSWORD = "nK36sasvSfoItJnXQ4qxav2OUWIPX5ZC"
KAFKA_HOST = os.getenv("KAFKA_HOST", "wb-stage-kafka-bootstrap.kafka.svc.cluster.local:9093")
KAFKA_USERNAME = os.getenv("KAFKA_USERNAME", "sarex")
KAFKA_PASSWORD = os.getenv("KAFKA_PASSWORD", "nK36sasvSfoItJnXQ4qxav2OUWIPX5ZC")
KAFKA_SSL_CAFILE = os.getenv("KAFKA_SSL_CAFILE", "/usr/local/share/ca-certificates/kafka.crt")
KAFKA_EAV_ASSETS_TOPIC = os.getenv("KAFKA_EAV_ASSETS_TOPIC", "sarex")
KAFKA_ISSUES_TOPIC = os.getenv("KAFKA_ISSUES_TOPIC", "sarex-issues")

View File

@ -1,4 +1,7 @@
---
# ВНИМАНИЕ: envs и args — списки, kustomize заменяет их ЦЕЛИКОМ (JSON merge patch),
# поэтому здесь продублирован весь набор из apps/issues/base/backend.yaml.
# podAnnotations — map, мержится по ключам.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -8,6 +11,126 @@ spec:
values:
services:
backend:
deployment:
args:
_default:
- |
set -a
[ -f /vault/secrets/issues-db ] && . /vault/secrets/issues-db
[ -f /vault/secrets/issues-rabbitmq ] && . /vault/secrets/issues-rabbitmq
[ -f /vault/secrets/issues-s3 ] && . /vault/secrets/issues-s3
[ -f /vault/secrets/issues-django-auth ] && . /vault/secrets/issues-django-auth
[ -f /vault/secrets/issues-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/issues-jwt-private)"
[ -f /vault/secrets/issues-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/issues-jwt-public)"
[ -f /vault/secrets/issues-kafka ] && . /vault/secrets/issues-kafka
set +a
exec /src/entrypoint.sh
volumes:
_default:
- name: production-configmap
mountPath:
_default: /src/config/settings/production.py
subPath:
_default: production.py
readOnly:
_default: true
configMap:
name:
_default: production-configmap
items:
- key: production.py
path:
_default: production.py
- name: kafka-ca-cert
mountPath:
_default: /usr/local/share/ca-certificates/kafka.crt
subPath:
_default: kafka.crt
readOnly:
_default: true
configMap:
name:
_default: kafka-ca-cert
items:
- key: kafka.crt
path:
_default: kafka.crt
envs:
- name: ENVIRONMENT
value:
_default: "production"
- name: AERO_PUBLIC_HOST
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: AERO_HOST
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: BASE_AERO_URL
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: BASE_AUTH_URL
value:
_default: "http://backend-svc.django.svc.cluster.local:80"
- name: WORKFLOWS_HOST
value:
_default: "http://backend-svc.workflow.svc.cluster.local:80"
- name: WORKFLOWS_URL
value:
_default: "http://backend-svc.workflow.svc.cluster.local:80"
- name: RESOURCES_API_HOST
value:
_default: "http://iam-backend.iam.svc.cluster.local:8000"
- name: EAV_HOST
value:
_default: "http://backend-svc.eav.svc.cluster.local:80"
- name: SAREX_API
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: DOCUMENTATIONS_URL
value:
_default: "http://documentations-api-svc.documentations.svc.cluster.local:80"
- name: DJANGO_SETTINGS_MODULE
value:
_default: "config.settings.production"
- name: API_ADDRESS
value:
_default: "8000"
# Kafka — код (kafka_app/app.py) ждёт KAFKA_HOST единой строкой "host:port",
# KAFKA_SSL_CAFILE — путь к файлу (не PEM-текст, в отличие от flows-backend).
# Хост/порт/CA — те же проверенные значения, что у message-hub/pm/flows.
- name: KAFKA_HOST
value:
_default: "kafka-kafka-contour.kafka.svc.cluster.local:9092"
- name: KAFKA_SSL_CAFILE
value:
_default: "/usr/local/share/ca-certificates/kafka.crt"
# Топики совпадают с тем, что уже провижено на брокере (см. topics
# в секрете kafka/apps/pm) и с SETTINGS_TOPICS у message-hub.
- name: KAFKA_ISSUES_TOPIC
value:
_default: "issues_broadcast_prod"
- name: KAFKA_EAV_ASSETS_TOPIC
value:
_default: "assets_broadcast"
podAnnotations:
_default:
vault.hashicorp.com/agent-inject-secret-issues-s3: secrets/data/minio/apps/issues
@ -18,3 +141,9 @@ spec:
YC_S3_BUCKET_NAME=sarex-issues
YC_S3_ENDPOINT_URL=https://s3.uralmine.com
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-kafka: secrets/data/kafka/apps/issues
vault.hashicorp.com/agent-inject-template-issues-kafka: |-
{{- with secret "secrets/data/kafka/apps/issues" -}}
KAFKA_USERNAME={{ index .Data.data "username" }}
KAFKA_PASSWORD={{ index .Data.data "password" }}
{{- end -}}

View File

@ -1,4 +1,7 @@
---
# ВНИМАНИЕ: envs и args — списки, kustomize заменяет их ЦЕЛИКОМ (JSON merge patch),
# поэтому здесь продублирован весь набор из apps/issues/base/celery.yaml.
# podAnnotations — map, мержится по ключам.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
@ -8,6 +11,122 @@ spec:
values:
services:
celery:
deployment:
args:
_default:
- |
set -a
[ -f /vault/secrets/issues-db ] && . /vault/secrets/issues-db
[ -f /vault/secrets/issues-rabbitmq ] && . /vault/secrets/issues-rabbitmq
[ -f /vault/secrets/issues-s3 ] && . /vault/secrets/issues-s3
[ -f /vault/secrets/issues-django-auth ] && . /vault/secrets/issues-django-auth
[ -f /vault/secrets/issues-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/issues-jwt-private)"
[ -f /vault/secrets/issues-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/issues-jwt-public)"
[ -f /vault/secrets/issues-kafka ] && . /vault/secrets/issues-kafka
set +a
exec celery -A config worker -l info -E
volumes:
_default:
- name: production-configmap
mountPath:
_default: /src/config/settings/production.py
subPath:
_default: production.py
readOnly:
_default: true
configMap:
name:
_default: production-configmap
items:
- key: production.py
path:
_default: production.py
- name: kafka-ca-cert
mountPath:
_default: /usr/local/share/ca-certificates/kafka.crt
subPath:
_default: kafka.crt
readOnly:
_default: true
configMap:
name:
_default: kafka-ca-cert
items:
- key: kafka.crt
path:
_default: kafka.crt
envs:
- name: ENVIRONMENT
value:
_default: "production"
- name: AERO_PUBLIC_HOST
value:
_default: "https://srx.wb.ru"
- name: AERO_HOST
value:
_default: "https://srx.wb.ru"
- name: BASE_AERO_URL
value:
_default: "https://srx.wb.ru"
- name: BASE_AUTH_URL
value:
_default: "http://backend-svc.django.svc.cluster.local:80"
- name: WORKFLOWS_HOST
value:
_default: "http://workflows-api-service.workflow.svc.cluster.local:8000"
- name: WORKFLOWS_URL
value:
_default: "http://workflows-api-service.workflow.svc.cluster.local:8000"
- name: RESOURCES_API_HOST
value:
_default: "http://iam-backend.iam.svc.cluster.local:8000"
- name: EAV_HOST
value:
_default: "http://backend-svc.eav.svc.cluster.local:80"
- name: SAREX_API
value:
_default: "https://srx.wb.ru"
- name: DOCUMENTATIONS_URL
value:
_default: "http://backend-api-svc.documentations.svc.cluster.local:80"
- name: DJANGO_SETTINGS_MODULE
value:
_default: "config.settings.production"
- name: API_ADDRESS
value:
_default: "8000"
# См. подробные комментарии в apps/issues/d8-ugmk-prod/backend-s3.yaml.
- name: KAFKA_HOST
value:
_default: "kafka-kafka-contour.kafka.svc.cluster.local:9092"
- name: KAFKA_SSL_CAFILE
value:
_default: "/usr/local/share/ca-certificates/kafka.crt"
- name: KAFKA_ISSUES_TOPIC
value:
_default: "issues_broadcast_prod"
- name: KAFKA_EAV_ASSETS_TOPIC
value:
_default: "assets_broadcast"
podAnnotations:
_default:
vault.hashicorp.com/agent-inject-secret-issues-s3: secrets/data/minio/apps/issues
@ -18,3 +137,9 @@ spec:
YC_S3_BUCKET_NAME=sarex-issues
YC_S3_ENDPOINT_URL=https://s3.uralmine.com
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-kafka: secrets/data/kafka/apps/issues
vault.hashicorp.com/agent-inject-template-issues-kafka: |-
{{- with secret "secrets/data/kafka/apps/issues" -}}
KAFKA_USERNAME={{ index .Data.data "username" }}
KAFKA_PASSWORD={{ index .Data.data "password" }}
{{- end -}}

View File

@ -0,0 +1,263 @@
---
# Отдельный процесс-консьюмер Kafka для issues (src/run_kafka_app.py, слушает
# eav_router — see kafka_app/consumers/eav_assets_consumer.py). В base этого
# сервиса нет вообще, аналогов в других контурах тоже — только для УГМК.
# Использует тот же образ/DB/S3-креды, что backend (django.setup() требует
# полный конфиг), плюс отдельно креды Kafka.
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kafka-app
namespace: issues
spec:
interval: 10m
chart:
spec:
chart: universal-chart
version: "0.1.9"
sourceRef:
kind: HelmRepository
name: yc-oci-charts
namespace: flux-system
interval: 10m
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
global:
env: _default
services:
kafka-app:
enabled: true
serviceAccount:
enabled:
_default: true
name:
_default: issues-vault
image:
name:
_default: cr.yandex/crp3ccidau046kdj8g9q/issues:production_17c438aa
pullPolicy:
_default: IfNotPresent
deployment:
enabled: true
name:
_default: kafka-app
replicaCount:
_default: 1
port:
_default: 8000
command:
_default: ["/bin/sh", "-ec"]
args:
_default:
- |
set -a
[ -f /vault/secrets/issues-db ] && . /vault/secrets/issues-db
[ -f /vault/secrets/issues-rabbitmq ] && . /vault/secrets/issues-rabbitmq
[ -f /vault/secrets/issues-s3 ] && . /vault/secrets/issues-s3
[ -f /vault/secrets/issues-django-auth ] && . /vault/secrets/issues-django-auth
[ -f /vault/secrets/issues-jwt-private ] && export JWT_PRIVATE_KEY="$(cat /vault/secrets/issues-jwt-private)"
[ -f /vault/secrets/issues-jwt-public ] && export JWT_PUBLIC_KEY="$(cat /vault/secrets/issues-jwt-public)"
[ -f /vault/secrets/issues-kafka ] && . /vault/secrets/issues-kafka
set +a
exec python3 run_kafka_app.py
resources:
requests:
cpu:
_default: 25m
memory:
_default: 128Mi
probes:
liveness:
enabled: false
readiness:
enabled: false
service:
enabled: false
imagePullSecrets:
enabled:
_default: true
name:
_default: regcred
volumes:
_default:
- name: production-configmap
mountPath:
_default: /src/config/settings/production.py
subPath:
_default: production.py
readOnly:
_default: true
configMap:
name:
_default: production-configmap
items:
- key: production.py
path:
_default: production.py
- name: kafka-ca-cert
mountPath:
_default: /usr/local/share/ca-certificates/kafka.crt
subPath:
_default: kafka.crt
readOnly:
_default: true
configMap:
name:
_default: kafka-ca-cert
items:
- key: kafka.crt
path:
_default: kafka.crt
envs:
- name: ENVIRONMENT
value:
_default: "production"
- name: AERO_PUBLIC_HOST
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: AERO_HOST
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: BASE_AERO_URL
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: BASE_AUTH_URL
value:
_default: "http://backend-svc.django.svc.cluster.local:80"
- name: WORKFLOWS_HOST
value:
_default: "http://backend-svc.workflow.svc.cluster.local:80"
- name: WORKFLOWS_URL
value:
_default: "http://backend-svc.workflow.svc.cluster.local:80"
- name: RESOURCES_API_HOST
value:
_default: "http://iam-backend.iam.svc.cluster.local:8000"
- name: EAV_HOST
value:
_default: "http://backend-svc.eav.svc.cluster.local:80"
- name: SAREX_API
value:
_default: "https://sarex.contour.infra.sarex.tech"
- name: DOCUMENTATIONS_URL
value:
_default: "http://documentations-api-svc.documentations.svc.cluster.local:80"
- name: DJANGO_SETTINGS_MODULE
value:
_default: "config.settings.production"
- name: API_ADDRESS
value:
_default: "8000"
- name: KAFKA_HOST
value:
_default: "kafka-kafka-contour.kafka.svc.cluster.local:9092"
- name: KAFKA_SSL_CAFILE
value:
_default: "/usr/local/share/ca-certificates/kafka.crt"
- name: KAFKA_ISSUES_TOPIC
value:
_default: "issues_broadcast_prod"
- name: KAFKA_EAV_ASSETS_TOPIC
value:
_default: "assets_broadcast"
podAnnotations:
_default:
traffic.sidecar.istio.io/excludeOutboundPorts: "8200"
vault.hashicorp.com/agent-init-first: "true"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: issues
vault.hashicorp.com/agent-inject-secret-issues-db: secrets/data/apps/issues/postgres
vault.hashicorp.com/agent-inject-template-issues-db: |-
{{- with secret "secrets/data/apps/issues/postgres" -}}
DATABASE_PORT={{ index .Data.data "port" }}
DATABASE_HOST={{ index .Data.data "host" }}
DATABASE_USER={{ index .Data.data "username" }}
DATABASE_PASSWORD={{ index .Data.data "password" }}
DATABASE_NAME={{ index .Data.data "database" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-rabbitmq: secrets/data/rabbitmq/apps/issues
vault.hashicorp.com/agent-inject-template-issues-rabbitmq: |-
{{- with secret "secrets/data/rabbitmq/apps/issues" -}}
RABBITMQ_VHOST={{ index .Data.data "vhost" }}
RABBITMQ_USERNAME={{ index .Data.data "username" }}
RABBITMQ_HOSTNAME=rabbitmq.rabbitmq.svc.cluster.local
RABBITMQ_PASSWORD={{ index .Data.data "password" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-s3: secrets/data/minio/apps/issues
vault.hashicorp.com/agent-inject-template-issues-s3: |-
{{- with secret "secrets/data/minio/apps/issues" -}}
YC_S3_ACCESS_KEY_ID={{ index .Data.data "access_key" }}
YC_S3_SECRET_ACCESS_KEY={{ index .Data.data "secret_key" }}
YC_S3_BUCKET_NAME=sarex-issues
YC_S3_ENDPOINT_URL=https://s3.uralmine.com
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-django-auth: secrets/data/vault/common/django_auth
vault.hashicorp.com/agent-inject-template-issues-django-auth: |-
{{- with secret "secrets/data/vault/common/django_auth" -}}
DJANGO_TOKEN={{ index .Data.data "key" }}
SAREX_USERNAME={{ index .Data.data "username" }}
SAREX_PASSWORD={{ index .Data.data "password" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-jwt-private: secrets/data/vault/common/rsa_keys
vault.hashicorp.com/agent-inject-template-issues-jwt-private: |-
{{- with secret "secrets/data/vault/common/rsa_keys" -}}
{{ index .Data.data "private_key" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-jwt-public: secrets/data/vault/common/rsa_keys
vault.hashicorp.com/agent-inject-template-issues-jwt-public: |-
{{- with secret "secrets/data/vault/common/rsa_keys" -}}
{{ index .Data.data "public_key" }}
{{- end -}}
vault.hashicorp.com/agent-inject-secret-issues-kafka: secrets/data/kafka/apps/issues
vault.hashicorp.com/agent-inject-template-issues-kafka: |-
{{- with secret "secrets/data/kafka/apps/issues" -}}
KAFKA_USERNAME={{ index .Data.data "username" }}
KAFKA_PASSWORD={{ index .Data.data "password" }}
{{- end -}}
commitSha: ""
gitlabUri: ""
gitlabJobUrl: ""
owner: ""

View File

@ -0,0 +1,27 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kafka-ca-cert
namespace: issues
data:
kafka.crt: |
-----BEGIN CERTIFICATE-----
MIIDETCCAfmgAwIBAgIQI30u4fwgUnGBxyLicuU5EzANBgkqhkiG9w0BAQsFADAT
MREwDwYDVQQDEwhrYWZrYS1jYTAeFw0yNjA3MTMxNzQwNDJaFw0yNzA3MTMxNzQw
NDJaMBMxETAPBgNVBAMTCGthZmthLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAudTWG0AwdqIMqbqGwjf9CY0BMMQm+HDPRkzMnXqVCSqAYLBLuPBC
h4+qm+IKAIpiJrO5pCDxeU1mVqMgRnXcdYyuefCzlTAKMNuM1CVM4ou695B0zKiy
D+7NHsdhuoH/ne3jCa8VpylDfKWsxcq3tDCdtZ5drPK80g2o0eo2LHx4erJtSABV
tqCrcIuy1nClAwGY4ejO+psr+gIzrHdYzve6YRRFBqVp6sJHcW1Cj6wOpHax4pcM
Srh5EcZ4dk434UNOZy2Ebji/9Lwxn0CiXtPTHYGTgNLhujurylFM1nUx69QgsR3C
GRZSY3R8Kegd4bzzhHfoBMQvl5Py+0o85QIDAQABo2EwXzAOBgNVHQ8BAf8EBAMC
AqQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFKhTGCqho/YjUZK1ndypuYs970kcMA0GCSqGSIb3DQEBCwUA
A4IBAQBvIzVhkzib6LJxt3tUkDFy87Yf1gG+rRVZiL/ZGrOsQlvBt4eLzZIytAvL
QaZHY+imIyzURj57IFfyFM1fEPRhc9et0VaRYxGwapLljez0jqPedM8rCsunJmVP
KrQxAfvbmHvcZ8k51tpD+YydmoARjBmoRdRIuLTDsuLv0Szg45M/7I4px2ycGN2j
kF7jVwF56u2eIrUGybnsH2qg+MY7devv3rG1Y1YV7HEwMAAeN1n5JLxxuA1AIW/j
TyZD8hICp8OLzWGTG9b3GZJK9FXbDHSKXemJr0hyjoxsW27nHsXcmV5Oe2OkTKw8
fdA17/ibw6gFrH2b3vZKcx8D455V
-----END CERTIFICATE-----

View File

@ -3,6 +3,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
- kafka-ca-configmap.yaml
- kafka-app.yaml
patches:
- path: namespace.yaml
target:

View File

@ -179,9 +179,12 @@ patches:
# на несуществующий taint безвреден. Запрашиваемые ресурсы трогать не
# надо: в base это 1 CPU и 1Gi.
#
# IGNORE_TAINTS_AND_NODE_SELECTOR (появился в base из master) для этого
# НЕ годится: в исходниках workflows-engine такой переменной нет вовсе,
# образ contour_3ef5b462 о ней не знает.
# IGNORE_TAINTS_AND_NODE_SELECTOR (появился в base из master) полагаться
# на него не стоит: в master workflows-engine такой переменной нет. Образ
# при этом contour_a03d37da-dirty2 — ручная сборка, поднятая тем же
# коммитом master, что добавил переменную, так что она там, вероятно,
# поддержана. Проверить нечем, поэтому селектор задан явно, а не через
# неё; вреда от того, что она выставлена в base, нет.
- op: test
path: /spec/values/services/backend/envs/53/name
value: TOLERATION_KEY
@ -231,6 +234,62 @@ patches:
path: /spec/values/services/backend/envs/71/value/_default
value: linux
# --- Vault для ПОДОВ ЗАДАЧ, а не для самого движка ----------------------
# В сборке contour_a03d37da-dirty2 движок умеет не монтировать в Job
# готовый Secret, а вешать на него аннотации vault-agent — тогда файл
# с кредами кладёт агент, и Secret в namespace задач не нужен вовсе.
# Именно так это работает в UGMK, где никакого yc-s3 в namespace нет.
#
# В master workflows-engine этого кода нет (сборка с невлитой ветки),
# поэтому набор переменных восстановлен по строкам бинарника:
# VAULT_ROLE, VAULT_SA, VAULT_MOUNT_PATH
# vault.hashicorp.com/{role,secret-volume-path,agent-inject-file-*,
# agent-inject-secret-*,agent-inject-template-*}
# yc-s3, yc-s3-service-account.json, /etc/sarex/yc-s3
#
# В base задан только VAULT_USE=true — остальных трёх нет НИ В ОДНОМ
# кластере репозитория, поэтому аннотации выходили неполными, агент в под
# задачи ничего не клал, и файл читался как пустой:
# ValueError: SERVICE_S3 is not valid
# Expecting value: line 1 column 1 (char 0)
#
# Переменных нет в base, поэтому op: add с "-" (добавление в конец
# списка), а не replace по индексу.
- op: add
path: /spec/values/services/backend/envs/-
value:
name: VAULT_ROLE
value:
_default: processing
- op: add
path: /spec/values/services/backend/envs/-
value:
name: VAULT_SA
value:
_default: processing-vault
# VAULT_MOUNT_PATH — вопреки имени НЕ корень KV, а ПОЛНЫЙ путь секрета:
# движок подставляет значение в шаблон целиком. Проверено по аннотациям
# созданного пода задачи:
# agent-inject-secret-yc-s3: <VAULT_MOUNT_PATH>
# agent-inject-template-yc-s3:
# {{- with secret "<VAULT_MOUNT_PATH>" -}}
# {{ index .Data.data "yc-s3-service-account.json" }}
# Отсюда два следствия: путь пишется в форме чтения KV v2 (с /data/), а в
# секрете обязан быть ключ с именем ФАЙЛА.
#
# Путь ОДИН НА ВСЕ ФАЙЛЫ, которые движок кладёт в поды задач: он
# подставляет VAULT_MOUNT_PATH в каждую аннотацию, меняя только имя
# ключа. Так, для django-auth аннотация ссылается на этот же путь и ищет
# в нём ключ django-auth.json. Поэтому имя пути нейтральное (job-files),
# а не по имени одного из файлов, — иначе оно врёт о содержимом.
# Сам секрет кладёт ansible, vault_app_secrets.
- op: add
path: /spec/values/services/backend/envs/-
value:
name: VAULT_MOUNT_PATH
value:
_default: secrets/data/apps/processing/job-files
# S3 из Vault, как у api.
- op: add
path: /spec/values/services/backend/podAnnotations/_default/vault.hashicorp.com~1agent-inject-secret-processing-s3

View File

@ -107,33 +107,41 @@ spec:
# Range-запросы, которых у S3 API «как есть» нет. Backend
# формирует ссылки вида /media/<key>, поэтому маршрут должен
# идти ПЕРЕД правилом для /.
#
# rewrite ОБЯЗАТЕЛЕН. s3-proxy не срезает префикс сам — он
# отображает путь URL прямо в ключ объекта. Без rewrite он
# искал бы media/orthophotos/... , тогда как в бакете лежит
# orthophotos/... , и отвечал бы
# NoSuchKey: The specified key does not exist (404)
# Проверено на живом объекте: /orthophotos/... отдаёт 200,
# /media/orthophotos/... — 404.
- path:
prefix: /media/
rewrite: /
service: s3-proxy-svc.django.svc.cluster.local
port: 80
# Микрофронтенд workflows и его API. Порядок внутри пары
# значим: /workflows/api/ обязан идти перед /workflows/, иначе
# запросы к API уйдут в раздачу статики.
# API микрофронтенда workflows. Висит на домене платформы, а
# не на отдельном поддомене: адреса вшиты в JS-бандл на этапе
# сборки и указывают на тот же origin относительными путями.
#
# Оба маршрута висят на домене платформы, а не на отдельном
# поддомене, потому что адреса бэкендов вшиты в JS-бандл на
# этапе сборки (константа __BUILD_ENV__) и указывают на тот же
# origin относительными путями. Отдельный поддомен означал бы
# пересборку образа.
#
# Маршруты живут в ЭТОМ VirtualService, а не в своём: Istio не
# Маршрут живёт в ЭТОМ VirtualService, а не в своём: Istio не
# сливает несколько VirtualService, привязанных к одной паре
# хост+gateway, — часть правил молча потерялась бы.
#
# ЗДЕСЬ НЕТ МАРШРУТА /workflows/ НА frontend-svc.processing, и
# это осознанно. Такой маршрут был и ломал навигацию: адрес
# вида /workflows/<uuid> — это путь ВНУТРИ SPA платформы,
# который должен отдать её index.html, а istio уводил его в
# nginx микрофронтенда, где такого файла нет:
# 404 Not Found — nginx/1.19.6
# Статика микрофронтенда при этом доезжает и без него: nginx
# платформы сам проксирует /workflows/*.js в processing
# (apps/django/aero/nginx-configmap.yaml). Проверено — 200.
- path:
prefix: /workflows/api/
rewrite: /api/
service: backend-svc.processing.svc.cluster.local
port: 80
- path:
prefix: /workflows/
rewrite: /
service: frontend-svc.processing.svc.cluster.local
port: 80
- path:
prefix: /api/
service: backend-svc.django.svc.cluster.local