Окружения¶
Окружение представляет собой именованную копия управляемой модели организации. Физически копия является отдельной схемой PostgreSQL: настоящей схемой, без столбца-дискриминатора и без префикса. Поэтому любой существующий запрос к репозиторию корректен внутри окружения без переписывания, а забытый предикат не может сделать строки одного окружения читаемыми из другого.
Каждая организация начинается с одного окружения по имени prod. Его нельзя удалить или
переименовать. Запрос, не называющий окружения, обслуживается prod; запрос, называющий
несуществующее окружение, отклоняется.
Окружения доступны организациям на платном тарифе.
Как заполнить непроизводственное окружение безопасными для использования данными, описано в Создание тестовых окружений.
Имена окружений¶
Имя должно соответствовать [a-z][a-z0-9_]{1,31}: от двух до тридцати двух строчных букв, цифр и подчёркиваний, начиная с буквы. prod и имена, начинающиеся с pg_, отклоняются. Максимальная длина для организации зависит от её собственного id. PostgreSQL молча обрезает идентификатор длиннее 63 байт, а ограничение защищает от самого длинного имени схемы, которое выводит окружение.
Что несёт копия¶
Каждая таблица в схеме организации относится ровно к одному классу. Классификация устроена как список разрешённого: таблица, добавленная позже, не переносится, пока кто-нибудь не укажет здесь её класс, поэтому забытая таблица проявляется красным тестом.
| Класс | Таблицы | Что происходит при копировании |
|---|---|---|
| CARRIED | domains, naming_rules, registered_tables, table_columns, relationships, metrics, roles, rls_rules, tags, tag_param_values, tag_assignments, glossary terms, materialized_views, calendars, api_endpoints, tracked_functions, tracked_webhooks, table_meta_links | Копируется целиком |
| IDENTITY_ONLY | sources, api_sources, kafka_sources | Поля идентичности и управления путешествуют; значения подключения остаются на месте (см. «Привязки») |
| SEEDED_AT_CREATION | roles, user_role_assignments | Копируется только при первом создании окружения; последующие слияния их не трогают |
| PARTIAL | org_settings | Копируется по ключам: настройки управления путешествуют, ключи, называющие внешнюю цель или среду выполнения конкретного окружения, остаются на месте |
| NEVER_SENSITIVE | org_secrets, user_directory | Не копируется никогда |
| NEVER_RUNTIME | mv_refresh_log, relationship_candidates, admin_audit_log и другие | Не копируется никогда |
SEEDED_AT_CREATION существует, чтобы решить одну конкретную проблему. Новому окружению нужны роли
и назначения, иначе оно откроется без единого, кто мог бы действовать. Но последующее слияние,
принёсшее строку developer из prod, перезаписало бы ограниченную версию, которая может быть
нужна ограниченной ветке, — и тогда путь ревью стал бы маршрутом эскалации привилегий. Поэтому роли
и назначения путешествуют один раз, при создании, а дальше остаются собственным ответом каждого
окружения.
Привязки¶
Привязки представляют собой столбцы, которые говорят, куда на самом деле указывает источник: host, port, database, username и остальные. Они никогда не переносятся в копии. Окружение без привязки помечается unbound, а не остаётся пустым, потому что пустой хост не равен отсутствию хоста, и построитель подключения прочитал бы его как localhost:5432.
Окружение, созданное любым способом, кроме Unbound, копирует подключение каждого источника у родителя
ровно так, как его записал родитель: ссылка ${secret:...} или ${env:...} копируется как эта
ссылка и никогда не разрешается. После создания копия — собственная строка окружения, редактируемая на странице
Источники, и при сборке runtime окружения ничего не разрешается через родителя.
Поле binding каждого источника говорит, откуда пришло его подключение: copied, own (задано в этом
окружении) или unbound. Изменение скопированного подключения делает его own. Скопировать заново у родителя
(POST /admin/orgs/{org}/environments/{name}/sources/recopy) снова копирует подключения родителя.
Слияние или развёртывание не переносит подключений, поэтому источник, который они вводят, приходит как unbound.
Git-проекция¶
Каждая запись в модель фиксирует результат коммитом в git-ветку окружения. Развёртывание дерева требует явного вызова: слитый pull request на git-хостинге сам себя не развёртывает.
Каждая сущность получает один файл. Путь задаётся адресом provisa:// сущности без схемы и организации: provisa://acme/sales/tables/Order становится sales/tables/Order.yaml. Источники попадают в sources/, команды в commands/, метрики в metrics/. Дочерние строки, зависящие от родителя (столбцы, связи, правила RLS), записываются внутри файла родителя, без собственных файлов.
Команды и назначения их тегов переживают полный круг. Тег на команде направляется в собственный файл
команды (commands/<name>.yaml); тег, не принадлежащий никакому файлу, исчезает из проекции и был бы
удалён при следующем развёртывании этого дерева.
Ни один суррогатный ключ не попадает в файл. registered_tables.id является автоинкрементным целым, поэтому одна и та же модель в двух окружениях получает разные числа, и наивный дамп отличался бы сам от себя. Все суррогатные ключи отбрасываются, а каждая ссылка на них записывается как путь цели.
Сериализация детерминирована. Ключи выводятся по алфавиту, дочерние коллекции упорядочены по своему адресу, стиль YAML зафиксирован. Два окружения, содержащие одну и ту же модель, дают побайтово одинаковые деревья.
Слияние¶
Слияние модели одного окружения в другое обновляет по идентичности: каждый объект, который есть в источнике, создаётся или обновляется в цели. Объекты, которых в источнике больше нет, удаляются, только если вызывающий явно запросил удаления. Слияние выполняется одной транзакцией, поэтому, если оно прервётся на полпути, цель остаётся как была.
Перед применением вызовите эндпоинт предпросмотра (GET /{name}/merge-preview) или передайте
dry_run: true. Предпросмотр проходит тем же путём по коду, что и слияние; это эндпоинт GET,
поэтому CI-скрипт, перепутавший флаг, не может случайно применить слияние, которое собирался лишь
осмотреть.
Слияние оставляет привязки, роли и секреты цели ровно такими, какими они были. Окружение разработки не теряет собственных подключений к базам данных, взяв более новую модель из prod. Prod не приобретает грантов dev.
Что называет отчёт¶
Отчёт о слиянии перечисляет по путям, что было добавлено, изменено, удалено и оставлено как есть. Он также называет конфликты: объекты, которые обе стороны изменили после последнего общего коммита. О конфликте сообщается, и побеждает источник, ведь именно это означает слияние в цель. Provisa не предлагает разрешения конфликтов, маркеров слияния и выбора по объектам. Ценность списка конфликтов в сигнале: два человека правили один объект, не зная об этом.
Если обе стороны изменили объект на одно и то же значение, они согласны, и конфликта нет. Если у двух окружений нет общего предка вовсе, база в отчёте равна None, а пустой список конфликтов означает, что ничего не сравнивалось, а не то, что ничего не столкнулось.
Слияние попадает в ветку цели одним сжатым коммитом. Сообщение коммита обязательно и не может быть пустым, потому что это единственное описание работы, которую представляет сжатый коммит. Коммиты источника остаются на месте, и их по-прежнему можно развернуть по SHA.
Pull¶
Pull берёт то, что удалённый репозиторий содержит для окружения, и делает это моделью. Он не перематывает локальную ветку напрямую; он применяет полученное дерево обычным путём развёртывания, поэтому те же валидация и аудит, что управляют ручным развёртыванием, управляют и pull.
Как и слияние, pull сообщает, что он перезаписал: объекты, которые изменило входящее дерево и которые локальное окружение тоже изменило после последнего общего коммита двух линий. Локальное изменение без коммита делает окружение расходящимся (см. «История» ниже), и pull называет его в отчёте как обычное изменение.
Pull отклоняется, когда две линии разошлись, то есть у каждой есть коммиты, которых нет у другой. В отказе перечислены объекты, которые затронули обе стороны, чтобы тот, кому теперь решать, чья работа останется, знал, на какие объекты смотреть.
История¶
Каждое развёртывание сдвигает курсор окружения вперёд по его собственной линии коммитов. Отмена делает шаг назад на один коммит, а повтор снова идёт вперёд к точке, откуда началась отмена. Ни то, ни другое не удаляет коммитов: шаг назад добавляет позицию и оставляет историю нетронутой.
Ветка засевается на вершине окружения, из которого она создана, поэтому undo останавливается в этой точке засева и не уходит на коммиты родительского окружения.
Флаги can_undo и can_redo приходят вместе с ответом списка окружений. Оба равны false, когда в проекции нет коммита, который называет плоскость управления; это состояние допускается устройством и называется расхождением. Узел, чьё хранилище репозитория так и не получило определённый коммит, всё равно перечисляет свои окружения; меняются только ответы об истории.
Авторизация¶
Окружениями управляют два права. Ни одно из них по умолчанию не принадлежит аналитику.
| Право | Кто им обладает (при засеве) | Чем оно управляет |
|---|---|---|
environment_management |
org_admin, developer | Создание и удаление окружений |
environment_switch |
org_admin, developer | Обслуживание любым окружением, кроме prod |
Для prod право не нужно. Он обслуживает запрос, в котором окружение не названо, и отказ в нём означал бы отказ в любом запросе.
Проверка происходит там, где выбирается окружение, до того как достигнут какой-либо маршрут. Участнику без environment_switch отказывают сразу на всех поверхностях (HTTP, GraphQL, SQL и сетевые протоколы), потому что окружение привязывается в middleware, а не в отдельных обработчиках.
Аналитик, не имеющий никакого права на окружения, может запрашивать prod и не видит переключателя
окружений. Подрядчик, которому выдана роль аналитика, не видит поверхности окружений и не может
создать окружение или переключиться в какое-либо, кроме production.
Полномочия владельца окружения¶
Создание окружения является единственным путём, которым участник с правом только на чтение получает права на правку модели. Внутри созданного им окружения создатель имеет возможности роли developer, за вычетом прав на данные (write, full_results, usage): права на построение модели без прав на данные.
Этот грант выводится из environments.created_by во время авторизации и никогда не пишется в
таблицу грантов. Удаление окружения снимает его тем же действием.
Членство в домене по-прежнему ограничивает то, что владелец может менять. Ветвление меняет, что участник может делать; оно никогда не меняет, к каким доменам он может это делать.
Защищённые окружения¶
Окружение можно сделать защищённым. Слияние или развёртывание в защищённое окружение не применяется по запросу; оно предлагается, и кто-то, кроме запросившего, должен его одобрить.
prod становится защищённым автоматически, как только в организации больше одного участника. Организация из одного участника не может выполнить условие «кто-то, кроме автора запроса», поэтому там правило не действует: оно сделало бы слияние в prod невозможным. org_admin может пометить защищённым любое окружение.
Запрос на слияние является сохранённой строкой, и никакое окно подтверждения его не заменяет. Одобряющий по определению не совпадает с автором запроса и в момент запроса не присутствует. Мимолётное подтверждение заставило бы одобрять внутри сессии автора, а именно это правило и запрещает.
Строка запроса несёт отчёт о слиянии рядом с сообщением запросившего. Устаревание выводится во время чтения и никогда не хранится: перепланирование во время чтения и сравнение с сохранённым отчётом — единственный вариант, который не может ошибиться. Устаревший запрос нужно запросить заново. Запросивший не может одобрить собственный запрос.
Состояния жизненного цикла запроса: requested → approved/rejected → applied. stale
выводится.
Развёртывания из ref репозитория проходят через ту же дверь, и запрос фиксирует SHA в момент предложения. Если ref сдвинется между предложением и решением, одобряющий читает отчёт по зафиксированному коммиту, а не по новому.
Note
UI запросов на слияние находится во вкладке Merge requests панели Environments. Колонка Report показывает, что изменилось бы, по количеству; строка разворачивается, показывая подробности по объектам.
Команды CLI env¶
provisa env deploy отправляет модель из ref в окружение. Он завершается с кодом 0, когда развёртывание применено или было пробным прогоном, и с кодом 2, когда окружение защищено и развёртывание только предложено. Конвейер, принявший ожидающее одобрение за выпущенное развёртывание, ошибся бы, и код выхода их различает.
provisa env fetch приносит удалённые ветки организации в локальный репозиторий. После этого
развёртывание может назвать origin/<branch>.
Обе команды принимают --api (URL API Provisa) и --token (bearer-токен). Задайте
PROVISA_API_URL и PROVISA_API_TOKEN в окружении, чтобы не передавать их при каждом вызове.
Типичный CI-конвейер для рабочего процесса с репозиторием:
provisa env fetch --org acme --api "$PROVISA_API_URL" --token "$PROVISA_API_TOKEN"
provisa env deploy --org acme --env prod --ref "origin/main" \
--message "release: $GIT_COMMIT_MSG" \
--api "$PROVISA_API_URL" --token "$PROVISA_API_TOKEN"
Смотрите также¶
- Развёртывание — как поднять плоскость управления, к которой подключаются окружения
- Команды — отслеживаемые функции и вебхуки, появляющиеся в дереве каждого окружения
- Создание тестовых окружений — профилировать производственные данные, объявлять поддельные данные, генерировать синтетические данные для непроизводственного окружения
- Методы поддельных данных — каждый вид поддельных данных и каждый метод, который может объявить колонка