# Конфигурация проекта ams-sync # Версия: 1.0.0 Документ описывает все переменные окружения и способы конфигурирования сервиса. ## Что делает сервис `ams-sync` — утилита синхронизации пользователей sarex-backend с системой управления доступом (AMS) на базе **Zitadel**. У сервиса два режима работы: - **`run`** — фоновый демон-потребитель Kafka (`src/internal/app/service/sync_service.py`, точка входа `python3 cmd.py run`, это же `CMD` контейнера). Слушает топик событий пользователей и создаёт/обновляет пользователей в Zitadel. Параллельно поднимает TCP-healthcheck. - **CLI-команды** (`migrate` / `sync` / `diff`, реализованы в `src/internal/app/console.py`) — разовые задачи миграции и синхронизации, запускаются вручную. Читают пользователей и компании напрямую из БД sarex-backend. ## Способы конфигурирования Сервис настраивается **только через переменные окружения**. Разбор выполняется библиотекой [`pydantic-settings`](https://docs.pydantic.dev/latest/concepts/pydantic_settings/). В отличие от монолитного `AppSettings`, настройки разбиты на несколько независимых классов, каждый со своим префиксом: | Класс | Файл | Префикс | | --- | --- | --- | | `Settings` (приложение) | `src/internal/app/service/settings.py` | *(без префикса)* | | `KafkaSettings` | `src/internal/app/service/settings.py` | `kafka_` | | `Settings` (console) | `src/internal/app/console.py` | *(без префикса)* | | `Settings` (logger) | `src/internal/adapter/logger/logger.py` | `log_` | | `Settings` (healthcheck) | `src/internal/adapter/tcphealthcheck/healthcheck.py` | `healthcheck_` | | `Settings` (auth) | `src/internal/adapter/auth/http/adapter.py` | `auth_` | | `Settings` (user server) | `src/internal/adapter/users/http/adapter.py` | `user_server_` | | `Settings` (zitadel users) | `src/internal/adapter/users/zitadel/adapter.py` | `zitadel_` | | `Settings` (organizations) | `src/internal/adapter/organizations/adapter.py` | `zitadel_` | | `Settings` (grants) | `src/internal/adapter/grants/adapter.py` | `zitadel_` | | `Settings` (user repo) | `src/internal/repository/users/repository.py` | `user_db_` | | `Settings` (company repo) | `src/internal/repository/companies/repository.py` | `user_db_` | Особенности разбора (`SettingsConfigDict`): - у каждого класса задан `env_file=".env"`, `env_file_encoding="utf-8"` и `extra="ignore"` — при наличии файла `.env` в рабочей директории он загружается автоматически, лишние переменные игнорируются; - вложенного разделителя нет — каждая группа настроек читается отдельным классом по своему префиксу (либо без префикса для классов приложения); - CLI-команды принимают флаг `--env-file=` (по умолчанию `.env`), который прокидывается в конструкторы настроек как `_env_file`; режим `run` всегда читает `.env`. Отдельного конфиг-файла (yaml/toml) у приложения нет. Источники переменных по способам запуска: | Способ запуска | Откуда берутся переменные | | --- | --- | | Локально (CLI) | Файл `.env` (или указанный через `--env-file=`) в директории с `cmd.py` и/или переменные окружения процесса | | Локально (контейнер) | `Dockerfile`: `CMD ["python3", "cmd.py", "run"]`; переменные пробрасываются в окружение контейнера | | Kubernetes (Helm) | `.helm/values.yaml`: блоки `universal-chart.envs` (обычные значения) и `universal-chart.secretEnvs` (значения из k8s-секретов). Базовый чарт — `universal-chart` | | CI/CD (GitLab) | `.gitlab-ci.yml`: переменные пайплайна в `workflow.rules` (`RELEASE_NAME`, `STAND`, `CHART_*`, `IMAGE_NAME`, `HELM_SET_ARGS` и т.п.) | Команды CLI (`python3 cmd.py `, см. `README.md`): | Команда | Аргументы | Назначение | | --- | --- | --- | | `run` | — | Демон-потребитель Kafka + TCP-healthcheck (режим контейнера) | | `migrate` | `--user-id`, `--with-password/--no-with-password` | Миграция пользователя(ей) из БД sarex-backend в Zitadel. Без `--user-id` мигрируются все (bulk) | | `sync` | `--user-id`, `--company-id` | Синхронизация метадаты пользователя(ей) из БД sarex-backend в Zitadel | | `diff` | — | Сравнение множества пользователей БД и Zitadel, отчёт по отсутствующим | ## Переменные приложения Дефолт `—` означает, что значение обязательно (иначе ошибка старта). ### App (без префикса) Класс `Settings` в `src/internal/app/service/settings.py` (режим `run`) и `Settings` в `console.py` (CLI). | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `ENVIRONMENT` | enum | `LOCAL` | Окружение: `LOCAL`/`STAGE`/`PREPROD`/`PRODUCTION` | | `AMS_SYNC_TOPIC` | string | `ams-sync` | Имя топика Kafka с событиями пользователей | | `HOST_ORGANIZATION_ID` | string | — | ID организации-хоста в Zitadel (обязателен и в `run`, и в CLI) | | `VERIFY_USERS` | bool | `False` | При `True` события `model_created` обрабатываются через `import_user` (с верификацией e-mail), иначе через обычный `create` | ### Kafka (`KAFKA_*`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `KAFKA_BOOTSTRAP_SERVERS` | list[str] (JSON) | — | Адреса брокеров, напр. `["host:9091"]` | | `KAFKA_SASL_PLAIN_USERNAME` | string | — | Логин SASL | | `KAFKA_SASL_PLAIN_PASSWORD` | string | — | Пароль SASL | | `KAFKA_SSL_CAFILE` | string | — | Путь к CA-сертификату для SSL | | `KAFKA_SECURITY_PROTOCOL` | string | `SASL_SSL` | Протокол безопасности | | `KAFKA_SASL_MECHANISM` | string | `SCRAM-SHA-512` | Механизм SASL | ### Zitadel / AMS (`ZITADEL_*`) Префикс используют три класса (адаптеры пользователей, организаций и грантов). Общие поля: | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `ZITADEL_SERVICE_ACCESS_TOKEN` | string | — | Service-токен доступа к API Zitadel (Bearer) | | `ZITADEL_HOST` | string | — (у адаптера пользователей); `https://idp.dev.stage.sarex.io` (у адаптеров организаций/грантов) | Базовый URL Zitadel | | `ZITADEL_TIMEOUT` | int / string | `10` (адаптер пользователей, минуты) / `10m` (организации, гранты) | Таймаут. См. замечание о конфликте типов ниже | | `ZITADEL_MAX_BATCH` | int | `2000` | Максимум пользователей в одной пачке bulk-импорта | | `ZITADEL_USERS_MANAGEMENT_ENDPOINT` | string | `management/v1/users` | Базовый путь management-API | | `ZITADEL_USERS_ENDPOINT` | string | `v2/users` | Базовый путь users-API v2 | ### Логирование (`LOG_*`) | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `LOG_LEVEL` | string | `INFO` | Уровень логирования | | `LOG_FORMAT` | string | `%(asctime)s [%(levelname)s]: %(message)s` | Формат строки лога | Логи пишутся в stdout и в файл `latest.log` в рабочей директории. ### TCP Healthcheck (`HEALTHCHECK_*`) Только режим `run`. Поднимает сырой TCP-сокет, отвечающий строкой-ответом на каждое подключение. | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `HEALTHCHECK_HOST` | string | `0.0.0.0` | Адрес прослушивания | | `HEALTHCHECK_PORT` | int | `8008` | Порт | | `HEALTHCHECK_MAX_CONNECTIONS` | int | `10` | Размер очереди подключений (`listen`) | | `HEALTHCHECK_REPLY` | string | `healthy` | Строка-ответ на подключение | ### Auth: sarex-backend (`AUTH_*`) Получение JWT под сервисным админом (используется в обоих режимах — адаптер создаётся и в `run`, и в CLI). | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `AUTH_HOST` | string | — | Базовый URL sarex-backend | | `AUTH_ADMIN_USERNAME` | string | — | Логин админа | | `AUTH_ADMIN_PASSWORD` | string | — | Пароль админа | | `AUTH_AUTH_ENDPOINT` | string | `api/token/` | Путь получения токена | ### Sarex user server (`USER_SERVER_*`) — только CLI Источник метадаты пользователя при миграции/синхронизации. | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `USER_SERVER_HOST` | string | — | Базовый URL сервиса пользователей sarex-backend | | `USER_SERVER_USERS_ENDPOINT` | string | `api/core/admin/users` | Базовый путь эндпоинта пользователей | ### База данных sarex-backend (`USER_DB_*`) — только CLI Один префикс на два репозитория (пользователи и компании). Подключение через `psycopg2`. | Переменная | Тип | Значение по умолчанию | Назначение | | --- | --- | --- | --- | | `USER_DB_NAME` | string | — | Имя базы данных | | `USER_DB_USER` | string | — | Пользователь БД | | `USER_DB_PASSWORD` | string | — | Пароль | | `USER_DB_HOST` | string | — | Хост PostgreSQL | | `USER_DB_PORT` | int | — | Порт PostgreSQL | | `USER_DB_USER_TABLE_NAME` | string | `base_baseuser` | Таблица пользователей | | `USER_DB_COMPANYUSER_TABLE_NAME` | string | `core_companyuser` | Таблица связи пользователь↔компания | | `USER_DB_COMPANY_TABLE_NAME` | string | `core_company` | Таблица компаний | | `USER_DB_ITER_SIZE` | int | `10000` | Размер серверного курсора при итерации | ## Переменные из Helm-чарта (`.helm/values.yaml`) Базовый чарт — `universal-chart`. Обычные значения (`universal-chart.envs`) задаются на окружение (`_default`/`stage`/`preprod`/`production`): | Переменная | Значения по окружениям | | --- | --- | | `AMS_SYNC_TOPIC` | `ams-sync` | | `ENVIRONMENT` | `STAGE` / `PREPROD` / `PRODUCTION` | | `HOST_ORGANIZATION_ID` | stage `339439562105337368`, preprod `337394329179947547`, production `337555824748561429` | | `USER_SERVER_HOST` | stage `https://stage.sarex.io`, preprod `https://preprod.sarex.io`, production `https://.lk.sarex.io` | | `AUTH_HOST` | stage `https://stage.sarex.io`, preprod `https://preprod.sarex.io`, production `https://lk.sarex.io` | | `KAFKA_SECURITY_PROTOCOL` | `SASL_SSL` | | `KAFKA_SASL_MECHANISM` | `SCRAM-SHA-512` | Значения из секретов (`universal-chart.secretEnvs`, монтируются как env через `secretKeyRef`): | Переменная | Секрет (`secretName`) | Ключ (`secretKey`) | | --- | --- | --- | | `ZITADEL_HOST` | `zitadel-token-secret` | `zitadel-host` | | `ZITADEL_SERVICE_ACCESS_TOKEN` | `zitadel-token-secret` | `zitadel-access-token` | | `KAFKA_BOOTSTRAP_SERVERS` | `ams-kafka-secret` | `kafka-bootstrap-servers` | | `KAFKA_SASL_PLAIN_USERNAME` | `ams-kafka-secret` | `kafka-username` | | `KAFKA_SASL_PLAIN_PASSWORD` | `ams-kafka-secret` | `kafka-password` | | `KAFKA_SSL_CAFILE` | `ams-kafka-secret` | `kafka-ca-file` | | `AUTH_ADMIN_USERNAME` | `auth-secret` | `admin-username` | | `AUTH_ADMIN_PASSWORD` | `auth-secret` | `admin-password` | | `USER_DB_NAME` | `user-db-secret` | `user-db-name` | | `USER_DB_USER` | `user-db-secret` | `user-db-user` | | `USER_DB_PASSWORD` | `user-db-secret` | `user-db-password` | | `USER_DB_HOST` | `user-db-secret` | `user-db-host` | | `USER_DB_PORT` | `user-db-secret` | `user-db-port` | Помимо env, чарт монтирует CA-сертификат Kafka из секрета `ya-ca-secret` в `/etc/ca-certificates/Yandex` (`volumes`/`volumeMounts`) и раздаёт `YandexInternalRootCA.crt` через configMap `ya-ca-cert`. Прочие значения чарта (не переменные приложения): `deployment.*` (имя, реплики, ресурсы, порт `8008`), `service.*` (ClusterIP, порт `8008`), `probes.*` (TCP-пробы на `8008`, по умолчанию выключены), `serviceAccount.*`, `imagePullSecrets.*`. ## Переменные в CI (`.gitlab-ci.yml`) Пайплайн подключает общие шаблоны из `generic/common-ci` и переключает окружение по ветке/тегу через `workflow.rules`: | Условие | STAND | Namespace | | --- | --- | --- | | ветка `stage` | `stage` | `platform` (`STAGE_NAMESPACE`) | | ветка `master` | `preprod` | `ams-sync-preprod` | | тег (`CI_COMMIT_TAG`) | `prod` | `ams-sync-prod` | Ключевые переменные пайплайна: `RELEASE_NAME=ams-sync`, `CHART_NAME=ams-sync`, `CHART_VERSION` (`0.0.1-`), `IMAGE_NAME`, `IMAGE_PATH=universal-chart.image.name`, `HELM_SET_ARGS`, `DOCKERFILE_PATH=Dockerfile`, флаги `ENABLE_LINTER`/`ENABLE_BUILD_CHART`/`ENABLE_BUILD_IMAGE`/`ENABLE_STATE_UPDATE`/`ENABLE_DEPLOY`. ## Замечания и потенциальные проблемы - **Конфликт `ZITADEL_TIMEOUT`.** Переменную читают три класса с общим префиксом `zitadel_`, но разного типа: адаптер пользователей ожидает `int` (минуты, дефолт `10`), адаптеры организаций и грантов — `str` (дефолт `10m`). Если задать `ZITADEL_TIMEOUT=10m`, инициализация адаптера пользователей упадёт на разборе `int`. Рекомендуется задавать целое число. - **`ZITADEL_HOST` без дефолта у адаптера пользователей.** В `users/zitadel/adapter.py` поле `host` обязательно (дефолта нет), тогда как в адаптерах организаций/грантов есть дефолт `https://idp.dev.stage.sarex.io`. Для режима `run` переменную нужно задать явно (в Helm приходит из секрета `zitadel-token-secret`). - **Режим `run` не использует БД и user-server.** Демон-потребитель (`sync_service.py`) поднимает только logger, healthcheck, auth-провайдер, адаптер Zitadel и Kafka. Переменные `USER_DB_*` и `USER_SERVER_*` требуются только CLI-командам (`migrate`/`sync`/`diff`), хотя в Helm они проброшены для запуска этих команд через `kubectl exec`. - **Приложение загружает `.env` автоматически** (у всех классов задан `env_file`), в отличие от некоторых других сервисов. CLI дополнительно поддерживает `--env-file=` для альтернативного файла. - **TLS-проверка отключена.** Все исходящие HTTP-запросы (Zitadel, sarex-backend) выполняются с `verify=False`. Отдельного флага в конфиге нет. - **Zitadel org-ID захардкожены в коде.** Помимо `HOST_ORGANIZATION_ID`, в use-case зашиты `glorax_org_id` и `dogma_org_id`, в которые распределяются пользователи по доменам e-mail (см. `usecase/migration/usecase.py`, `usecase/user/usecase.py`). ## Минимальный набор для режима `run` (демон) - `ENVIRONMENT`, `HOST_ORGANIZATION_ID`, `AMS_SYNC_TOPIC` - `KAFKA_BOOTSTRAP_SERVERS`, `KAFKA_SASL_PLAIN_USERNAME`, `KAFKA_SASL_PLAIN_PASSWORD`, `KAFKA_SSL_CAFILE` (+ при необходимости `KAFKA_SECURITY_PROTOCOL`, `KAFKA_SASL_MECHANISM`) - `ZITADEL_HOST`, `ZITADEL_SERVICE_ACCESS_TOKEN` - `AUTH_HOST`, `AUTH_ADMIN_USERNAME`, `AUTH_ADMIN_PASSWORD` ## Дополнительно для CLI (`migrate` / `sync` / `diff`) - `USER_SERVER_HOST` - `USER_DB_NAME`, `USER_DB_USER`, `USER_DB_PASSWORD`, `USER_DB_HOST`, `USER_DB_PORT`