# Конфигурация проекта sarex-resources

Документ описывает все переменные окружения и способы конфигурирования сервиса ресурсов (`sarex-resources`, backend модуля «Ресурсы/Проекты»).

## Способы конфигурирования

Сервис — приложение на **Django 4.1 + Django REST Framework** (GeoDjango/PostGIS). В отличие от сервисов на `pydantic-settings`, конфигурация задаётся **классическими Django settings-модулями** в `server/config/settings/`, а не единым классом настроек:

- `base.py` — общие настройки (приложения, middleware, DRF, S3, интеграция с Sarex, OpenTelemetry);
- `production.py` — наследует `base.py` (`from .base import *`), задаёт `DEBUG=False`, `ALLOWED_HOSTS`, БД, CORS, S3, статику;
- `test.py` — наследует `base.py`, отдельная тестовая БД, снятие permission-классов DRF.

Активный модуль выбирается переменной **`DJANGO_SETTINGS_MODULE`**. По умолчанию (`server/config/wsgi.py`, `server/manage.py`) — `config.settings.production`.

Особенности разбора:

- **префикса у переменных нет** — имена плоские (`DATABASE_HOST`, `YC_S3_BUCKET_NAME` и т.п.);
- значения читаются напрямую через `os.getenv(...)`; вложенных секций/делимитеров (как `__` в pydantic-сервисах) нет;
- **типизация и валидация окружения отсутствуют** — всё приходит строками; булевы флаги (`USE_OTEL`, `USE_INSECURE`) проверяются на «truthy», поэтому **любая непустая строка, включая `"False"`, считается истиной** (см. «Замечания»);
- `.env`-файл приложением **не загружается автоматически** — переменные должны быть в окружении процесса (docker-compose `environment`, k8s `env`/`secretKeyRef`, либо `set -a && . ./.env && set +a`).

> **Важно для прод-развёртывания.** В кластере файл `server/config/settings/production.py` **подменяется** содержимым ConfigMap `django-configmap` (монтируется на `/server/config/settings/production.py`, см. `infra/iac/apps/resources/base/django-configmap.yaml` и `.helm/templates/api.yaml`). Именно версия из ConfigMap определяет фактические `ALLOWED_HOSTS`, `CORS_*`, `SERVICE_ACCOUNTS_HOST`, cookie-имена и часть значений Sarex. Репозиторный `production.py` — это шаблон/локальный вариант.

Источники переменных по способам запуска:

| Способ запуска | Откуда берутся переменные |
| --- | --- |
| Локально (docker-compose) | `docker-compose.yml` — сервисы `postgres`/`api`; переменные Postgres задаются в `environment`, приложение читает окружение контейнера |
| Локально (тесты) | `docker-compose.test.yml` + `pytest.ini` (`DJANGO_SETTINGS_MODULE=config.settings.test`), переменные `DJANGO_POSTGRES_*` |
| Kubernetes (Helm-чарт репозитория) | `.helm/values-<env>.yaml`: блоки `envs` (обычные значения) и `secrets` (из k8s-секретов); шаблон `.helm/templates/api.yaml` |
| Kubernetes (infra, GitOps) | `infra/iac/apps/resources/*` — kustomize `base` (Vault-инъекция env в аннотациях Deployment + ConfigMaps) и оверлеи `brusnika-stage`/`brusnika-prod` (`helmrelease.yaml`, universal-chart) |
| CI/CD (GitLab) | `.gitlab-ci.yml`: подключает общие шаблоны `generic/common-ci`, переключает окружение по ветке/тегу через `workflow.rules` |

Способы запуска процессов:

| Команда | Точка входа | Назначение |
| --- | --- | --- |
| `uwsgi --ini /opt/server/uwsgi.ini` | `config.wsgi:application` | HTTP API (uWSGI, порт 8000) |
| `python manage.py migrate` | `manage.py` | Применение миграций (в `entrypoint.sh` перед стартом uWSGI) |
| `python manage.py <command>` | `manage.py` | Служебные Django-команды (`createsuperuser`, `shell`, миграции и т.п.) |
| `pytest` | `config.settings.test` | Юнит-тесты (`compose/test_server/entrypoint.sh`) |

Порядок запуска в контейнере (`compose/server/entrypoint.sh`): сначала `python manage.py migrate`, затем `opentelemetry-instrument uwsgi --plugin python3 --ini /opt/server/uwsgi.ini` с `DJANGO_SETTINGS_MODULE=config.settings.production`.

## Переменные приложения

Все переменные плоские (без префикса). Дефолт `—` означает, что значение при обращении вернёт `None`/пусто (Django/psycopg2 или интеграции могут упасть уже в рантайме, а не на старте).

### Выбор настроек

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DJANGO_SETTINGS_MODULE` | string | `config.settings.production` | Модуль настроек Django. Значения: `config.settings.production` / `config.settings.test` |

### Окружение и трейсинг (`base.py`)

Блок OpenTelemetry активируется целиком по `USE_OTEL` (`django-otel-tools`): добавляется `OtelMiddleware`, настраивается трейсер и OTEL-логгер.

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `USE_OTEL` | bool-ish | `False` (выкл.) | Включение трейсинга. Truthy-проверка: любая непустая строка включает |
| `SERVICE_NAME` | string | `resources-backend.sarex-resources` | Имя сервиса в трейсах |
| `TRACER_ENDPOINT` | string | `localhost:4375` | Адрес OTLP-коллектора |
| `USE_INSECURE` | bool-ish | `False` | Небезопасное (без TLS) подключение к коллектору; та же truthy-проверка |
| `ENVIRONMENT` | string | `prod` | Метка окружения в атрибутах трейсинга (`stage`/`preprod`/`prod`) |
| `MODULE` | string | `resources` | Атрибут `module` в трейсинге |
| `TEAM` | string | `platform_team` | Атрибут `team` в трейсинге |
| `COMPONENT` | string | `backend` | Атрибут `component` в трейсинге |

### База данных (`production.py`, `django.contrib.gis.db.backends.postgis`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DATABASE_HOST` | string | — | Хост PostgreSQL/PostGIS |
| `DATABASE_PORT` | int (string) | — | Порт PostgreSQL |
| `DATABASE_NAME` | string | — | Имя базы данных |
| `DATABASE_USER` | string | — | Пользователь БД |
| `DATABASE_PASSWORD` | string | — | Пароль пользователя БД |

> БД требует расширения PostGIS (образ `postgis/postgis`), т.к. используются гео-поля (`Location.geometry`/`geography`) и `django.contrib.gis`. TLS к БД настраивается на уровне libpq: в Helm-чарте секрет `yc-pg-certificate` монтируется как `/root/.postgresql/root.crt` (в settings отдельного `sslmode` нет).

### База данных для тестов (`test.py`)

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `DJANGO_POSTGRES_DB` | string | `resources` | Имя тестовой БД |
| `DJANGO_POSTGRES_USER` | string | `postgres` | Пользователь тестовой БД |
| `DJANGO_POSTGRES_PASSWORD` | string | `12345678dev` | Пароль тестовой БД |
| `DJANGO_POSTGRES_HOST` | string | `test_postgres` | Хост тестовой БД |
| `DJANGO_POSTGRES_PORT` | int (string) | `5432` | Порт тестовой БД |

Дополнительно `test.py` выставляет `DJANGO_ALLOW_ASYNC_UNSAFE=true` в коде.

### S3 / объектное хранилище (`base.py` и `production.py`, `django-storages` + `boto3`)

Файлы (`ResourcePhoto.image` и т.п.) хранятся в S3-совместимом хранилище; `DEFAULT_FILE_STORAGE=storages.backends.s3boto3.S3Boto3Storage`, `AWS_DEFAULT_ACL="public-read"`.

| Переменная | Тип | Значение по умолчанию | Назначение (Django-настройка) |
| --- | --- | --- | --- |
| `YC_S3_ACCESS_KEY_ID` | string | — | `AWS_ACCESS_KEY_ID` |
| `YC_S3_SECRET_ACCESS_KEY` | string | — | `AWS_SECRET_ACCESS_KEY` |
| `YC_S3_BUCKET_NAME` | string | — | `AWS_STORAGE_BUCKET_NAME` |
| `YC_S3_ENDPOINT_URL` | string | — | `AWS_S3_ENDPOINT_URL` |

### Интеграция с ядром Sarex (`base.py`)

Используется для получения токена (`/api/token/`) и списка пользователей (`/api/core/users/`) в эндпоинтах группировки пользователей/ресурсов (`users-grouped-by-resource`, `users-with-resources`).

| Переменная | Тип | Значение по умолчанию | Назначение |
| --- | --- | --- | --- |
| `SAREX_HOST` | string | `https://lk.sarex.io` | Базовый хост Sarex (`SAREX_BASE_HOST`) |
| `SAREX_ADMIN_USERNAME` | string | — | Логин сервисной учётки Sarex |
| `SAREX_ADMIN_PASSWORD` | string | — | Пароль сервисной учётки Sarex |
| `SERVICE_ACCOUNTS_HOST` | string | `https://lk.sarex.io/api/core` | Определяется в `production.py`; кодом приложения напрямую не читается (см. «Замечания») |

## Переменные инфраструктуры, сборки и вспомогательных утилит

Не читаются кодом приложения, но участвуют в запуске/сборке/деплое.

| Переменная | Где используется | Назначение |
| --- | --- | --- |
| `API_ADDRESS` | `.helm/values-*.yaml`, `infra/.../backend-deployment.yaml` | Задаётся в окружении (`8000`), кодом не читается — порт фактически берётся из `uwsgi.ini` (`http = 0.0.0.0:8000`) |
| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | `docker-compose.yml`, `docker-compose.test.yml` | Инициализация контейнера Postgres (не путать с `DATABASE_*`, которые читает Django) |
| `PYTHONUNBUFFERED` | `Dockerfile` | Небуферизованный stdout/stderr |
| build-настройки uWSGI | `compose/server/uwsgi.ini` | `processes=8`, `http=0.0.0.0:8000`, `harakiri`, `buffer-size`, `static-map` для `/static` и `/media` |

Приватный индекс пакетов для сборки (`django-otel-tools`) задан прямо в `requirements/base.txt` через `--extra-index-url` (nexus.infra.sarex.io).

## Переменные из Helm-чарта репозитория (`.helm/values-<env>.yaml`)

Обычные значения — блок `envs`, секреты — блок `secrets` (монтируются как env через `secretKeyRef`). Значения различаются по окружениям (`stage`/`preprod`/`production`): адрес БД (`DATABASE_HOST`), `SERVICE_NAME`, `TRACER_ENDPOINT`, `ENVIRONMENT`, число реплик.

Значения из секретов:

| Переменная | Секрет (`secret_name`) | Ключ (`secret_key`) |
| --- | --- | --- |
| `DATABASE_USER` | `ya-pg-secret` | `user` |
| `DATABASE_PASSWORD` | `ya-pg-secret` | `password` |
| `YC_S3_ACCESS_KEY_ID` | `yc-s3-secret` | `key_id` |
| `YC_S3_SECRET_ACCESS_KEY` | `yc-s3-secret` | `access_key` |
| `YC_S3_BUCKET_NAME` | `yc-s3-secret` | `storage_bucket_name` |
| `YC_S3_ENDPOINT_URL` | `yc-s3-secret` | `endpoint_url` |
| `SAREX_ADMIN_USERNAME` | `sarex-auth-secret` | `username` |
| `SAREX_ADMIN_PASSWORD` | `sarex-auth-secret` | `password` |

Помимо env, чарт монтирует CA-сертификат PostgreSQL из секрета `yc-pg-certificate` (ключ `certificate`) как файл `/root/.postgresql/root.crt`, а также ConfigMap `uwsgi-configmap` (файл `uwsgi.ini`). Прочие значения чарта (не переменные приложения): `deployment.*` (имя, образ, порт, реплики, ресурсы), `service_*`, `api_host` (istio `VirtualService`, prefix `/resource-management`).

## Переменные из infra (GitOps, kustomize `base`)

В `infra/iac/apps/resources/base/backend-deployment.yaml` секреты БД и S3 подаются через **HashiCorp Vault Agent** (аннотации `vault.hashicorp.com/*`), который рендерит файлы `/vault/secrets/resources-db` и `/vault/secrets/resources-s3`; они подгружаются в окружение в `args` контейнера (`set -a && . /vault/secrets/... && set +a`) перед запуском `entrypoint.sh`.

| Переменная | Источник (Vault path / значение) |
| --- | --- |
| `DATABASE_HOST` | `postgresql.resources.svc.cluster.local` (шаблон Vault) |
| `DATABASE_PORT` | `5432` |
| `DATABASE_NAME` | `resources_db` |
| `DATABASE_USER` | `secrets/data/postgresql/apps/resources` → `username` |
| `DATABASE_PASSWORD` | `secrets/data/postgresql/apps/resources` → `password` |
| `YC_S3_ENDPOINT_URL` | `secrets/data/minio/apps/resources` → `client.endpoint` |
| `YC_S3_BUCKET_NAME` | `resources` |
| `YC_S3_ACCESS_KEY_ID` | `secrets/data/minio/apps/resources` → `access_key` |
| `YC_S3_SECRET_ACCESS_KEY` | `secrets/data/minio/apps/resources` → `secret_key` |
| `DJANGO_SETTINGS_MODULE` | `config.settings.production` (env Deployment) |
| `API_ADDRESS` | `8000` (env Deployment) |

Оверлеи `brusnika-stage`/`brusnika-prod` используют `HelmRelease` (universal-chart): env `DJANGO_SETTINGS_MODULE`, `DATABASE_HOST/PORT/NAME`, `API_ADDRESS`, `YC_S3_ENDPOINT_URL`, `YC_S3_BUCKET_NAME`; секреты `DATABASE_USER/PASSWORD` (`postgres-secret`), `YC_S3_ACCESS_KEY_ID/SECRET_ACCESS_KEY` (`yc-s3-secret`, ключи `key-id`/`access-key`).

## Переменные в CI (`.gitlab-ci.yml`)

Пайплайн подключает общие шаблоны из `generic/common-ci` (`universal-pipeline-*`, `common-security-scan`, `common-build`) и переключает окружение по ветке/тегу:

| Условие | STAND | Namespace | RELEASE_NAME |
| --- | --- | --- | --- |
| ветка `master` | `preprod` | `resources-preprod` | `resources` |
| ветка `stage` | `stage` | `resources-stage` | `sarex-resources` |
| тег (`CI_COMMIT_TAG`) | `prod` | `resources-prod` | `sarex-resources` |

Ключевые переменные пайплайна: `CHART_NAME=sarex-resources`, `CHART_VERSION`, `DOCKERFILE_PATH=./compose/server/Dockerfile`, `IMAGE_PATH=deployment.image`, `HELM_SET_ARGS` (`--set deployment.image=…`), флаги `ENABLE_LINTER`/`ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_STATE_UPDATE`/`ENABLE_DEPLOY`. Job `linter` запускает `flake8` (образ `python:3.8-buster`).

## Замечания и потенциальные проблемы

- **Truthy-флаги.** `USE_OTEL` и `USE_INSECURE` читаются как `os.getenv(name, False)` без приведения типа. Любая непустая строка — истина, включая `"False"`, `"0"`, `"no"`. Чтобы **выключить** трейсинг, переменную `USE_OTEL` нужно **не задавать вовсе** (а не ставить в `False`). В Helm-values она задана строкой `"True"`.
- **`.env` не загружается автоматически.** В settings нет `python-dotenv`/`env_file`; переменные должны попадать в окружение процесса (compose `environment`, k8s `env`/Vault, ручной `export`).
- **`production.py` подменяется в кластере.** Репозиторный `production.py` содержит хардкод `ALLOWED_HOSTS`, `CORS_*`, тестовый `SECRET_KEY` и др.; в проде используется версия из ConfigMap `django-configmap`. Различия: `ALLOWED_HOSTS=['*']`, другой `SERVICE_ACCOUNTS_HOST`, хардкод `SAREX_ADMIN_*`/`SAREX_BASE_HOST`, cookie-имена `resource-sessionid`/`resource-csrftoken`.
- **`SECRET_KEY` захардкожен** в `base.py`/`production.py` (в т.ч. пометка `# Delete after Test`) — секрет не берётся из окружения. Для реального прода его следует вынести в секрет.
- **`SERVICE_ACCOUNTS_HOST`** определяется, но напрямую в коде не используется (интеграции ходят по `SAREX_BASE_HOST`); переменная задаётся «на вырост».
- **`DATABASE_*` без дефолтов.** При отсутствии переменных Django получит `None` и подключение к БД упадёт в рантайме (не на импортстарте). Пять переменных БД обязательны для рабочего запуска.
- **Аутентификация DRF отключена.** `DEFAULT_AUTHENTICATION_CLASSES=[]`, permission-классы не заданы (эффективно `AllowAny`) — доступ ограничивается только сетевым слоем/istio. Учитывать при публикации.
- **Имя релиза различается между окружениями** (`resources` на preprod vs `sarex-resources` на stage/prod) — при работе с Helm/namespace это легко перепутать.

## Минимальный набор для локального запуска

Postgres (PostGIS) поднимается через `docker-compose up postgres`, приложение — через сервис `api` (`docker-compose.yml`) или локально `uwsgi`/`manage.py runserver`. Минимально необходимо задать:

- `DJANGO_SETTINGS_MODULE=config.settings.production` (или свой dev-модуль)
- `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`
- `YC_S3_*` — если задействуется загрузка файлов/статики в S3 (иначе операции с файлами упадут)
- `SAREX_HOST`, `SAREX_ADMIN_USERNAME`, `SAREX_ADMIN_PASSWORD` — если нужны эндпоинты интеграции с пользователями Sarex
- OTEL — по умолчанию не задавать (`USE_OTEL` отсутствует); при включении — `USE_OTEL=1`, `SERVICE_NAME`, `TRACER_ENDPOINT`, при необходимости `USE_INSECURE`

Для тестов достаточно поднять `docker-compose.test.yml` — переменные `DJANGO_POSTGRES_*` имеют рабочие дефолты, `pytest.ini` уже указывает `config.settings.test`.

Готовые значения-примеры для всех переменных приведены в `.env.example`.
