Валидация flyway-миграций без СМС и регистрации

Mar 27, 2020 09:28 · 674 words · 4 minute read flyway postgres docker jenkins

В одной из предыдущих статей мы уже рассматривали контроль версий БД и миграции с помощью инструмента 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-миграций своими силами.

tweet Share