Валидация flyway-миграций без СМС и регистрации
Mar 27, 2020 09:28 · 674 words · 4 minute read
В одной из предыдущих статей мы уже рассматривали контроль версий БД и миграции с помощью инструмента Flyway, причем довольно подробно и с конкретным примером, а в конце статьи я обещал показать, как можно организовать валидацию миграций до их применения к БД (фича, доступная только в платной подписке) своими силами и совершенно бесплатно. Давайте разберемся!
Итак, идея в том, чтобы проверять написанные flyway-миграции пользователей еще до того, как они попадут в основную ветку (master) репозитория. Самое подходящее место - в настройках ‘Branch protection rules’ github-репозитория установить чекбокс ‘Require status checks to pass before merging’ и запускать специальную джобу на build-сервере (например, Jenkins) на создание/изменения пуллреквеста.
Что касается самой задачи на build-сервере - будем поднимать docker-контейнер с соответствующей БД (при необходимости модифицируя init-скрипт) и последовательно применять все миграции БД используя docker-контейнер с flyway, начиная с Baseline.
Допустим, у нас несколько БД (в данном примере две), миграции к которым мы хотим проверять. Предполагается, что docker-контейнеров во время валидации миграций у нас будет задействовано больше одного, поэтому есть смысл сразу описывать все в docker-compose.yaml
-файле. Выглядеть он будет, например, так:
Здесь, не забывая о принципах Don’t Repeat Yourself (DRY) мы описываем два сервиса (flyway-migrate-dmp
и flyway-migrate-lsm
), которые будут зависеть от третьего сервиса db
(собственно контейнера с БД). Сервису с БД дополнительно в каталог /docker-entrypoint-initdb.d
(все *.sql
, *.sql.gz
, или *.sh
скрипты из данного каталога будут выполнены сразу после вызова initdb
при старте docker-контейнера) мы монтируем скрипт init-user-db.sh
следующего содержания:
Примечание. Вполне логично, что перед применением миграций к БД dmp
эта база должна существовать, точно так же, как должны быть созданы и соответствующие пользователи/роли, которые используются в наших миграциях.
Jenkinsfile
(описание задачи на build-сервере) в нам случае будет таким:
Здесь, первый и последний этапы (“Prepare environment” и “Cleanup environment”) выглядят абсолютно одинаково и продублированы, так сказать, для надежности. Этапы запуска docker-контейнера с БД и валидации миграций (“Up postgres DB” и “Validate migrations” соответственно) не содержат никакой магии и не нуждаются в дополнительном описании.
Основная логика скрыта в этапе определения статуса (“Set build status”). Могут быть случаи, когда для одной из БД валидация миграций завершится неудачно, в то время как для других - вполне успешно. Jenkins, в таком случае, будет считать этап успешным - это не совсем то, чего мы добиваемся.
Даже если “валидация” (применение) миграции к одной из БД внутри docker-образа завершится неудачно, например:
Creating migrations-validate_flyway-migrate-lsm_1 ...
Attaching to migrations-validate_flyway-migrate-lsm_1
flyway-migrate-lsm_1 | Flyway Community Edition 6.3.2 by Redgate
flyway-migrate-lsm_1 | Database: jdbc:postgresql://postgres:5432/lsm (PostgreSQL 12.1)
flyway-migrate-lsm_1 | Successfully validated 5 migrations (execution time 00:00.041s)
flyway-migrate-lsm_1 | Creating Schema History table "public"."flyway_schema_history" ...
flyway-migrate-lsm_1 | Current version of schema "public": << Empty Schema >>
flyway-migrate-lsm_1 | Migrating schema "public" to version 20200130000000 - Base version
flyway-migrate-lsm_1 | +------------+
flyway-migrate-lsm_1 | | set_config |
flyway-migrate-lsm_1 | +------------+
flyway-migrate-lsm_1 | | |
flyway-migrate-lsm_1 | +------------+
...
flyway-migrate-lsm_1 | Migrating schema "public" to version 20200323112608 - add new field to users table
flyway-migrate-lsm_1 | ERROR: Migration of schema "public" to version 20200323112608 - add new field to users table failed! Changes successfully rolled back.
flyway-migrate-lsm_1 | ERROR:
flyway-migrate-lsm_1 | Migration V20200323112608__add_new_field_to_users_table.sql failed
flyway-migrate-lsm_1 | ------------------------------------------------------------------
flyway-migrate-lsm_1 | SQL State : 42704
flyway-migrate-lsm_1 | Error Code : 0
flyway-migrate-lsm_1 | Message : ERROR: role "test" does not exist
flyway-migrate-lsm_1 | Location : /flyway/sql/V20200323112608__add_new_field_to_users_table.sql (/flyway/sql/V20200323112608__add_new_field_to_users_table.sql)
flyway-migrate-lsm_1 | Line : 1
flyway-migrate-lsm_1 | Statement : REVOKE CONNECT ON DATABASE lsm FROM PUBLIC, lsm, ro_user, test
flyway-migrate-lsm_1 |
migrations-validate_flyway-migrate-lsm_1 exited with code 1
...
то результат выполнения команды docker-compose -f docker-compose.yaml up flyway-migrate-lsm
будет EXIT 0
, то есть успешный - и Jenkins промаркирует данный билд как ‘SUCCESS’.
Чтобы этого не произошло, переопределим текущее поведение - для каждого контейнера с помощью команды docker inspect
будем получать код его завершения из поля .State.ExitCode
, отсекать успешно завершенные (grep -v '^0'
) и суммировать остальные. Если в итоге получим 0
- пометим билд как успешный, в любом другом случае считаем билд неудачным.
Результат выполнения задачи с помощью Post-build action отправляем в slack-канал, для уведомления пользователя(ей) и на github - для установки статуса проверки (check) на странице пуллреквеста.
На этом все - вот так легко, без магии, СМС и регистрации можно сделать валидацию flyway-миграций своими силами.