Repository navigation
Add a Docker-based MySQL Flyway migration smoke-test harness - #1103
Conversation
…arsed before mojito-dev.
A same-version SQL or Java migration edit can otherwise match an old image and still pass.
ehoogerbeets
left a comment
There was a problem hiding this comment.
The MySQL 8 stack, the TCP healthcheck, and the image-versus-source check are the right shape for a local rehearsal, and I would leave this out of CI.
Inline comments on the image name, sort locale, seeding real rows before the upgrade, Mac node in Dockerfile-bk21, and bind-mounting recompiled jars with -v so a code change does not require a full image rebuild.
|
|
||
| Set it only to rehearse an upgrade from an older schema. That is two starts on the same database: | ||
|
|
||
| 1. `FLYWAY_TARGET=N docker compose … up -d` — migrate an empty database through version N and stop. Confirm `flyway_schema_history` reached N. The webapp may then exit: this image is the current Mojito build, which expects the latest schema, while the database is still at N. That is expected. The goal of this start is the database at N, not a usable app. MySQL stays up. |
There was a problem hiding this comment.
This first boot is a database at N, and the app is allowed to exit because the image is current Mojito on an older schema. The upgrade path never writes the rows a real upgrade has to keep: a repository, an asset, text units, and imported translations.
An empty migrate accepts a NOT NULL column with no default, a table rebuild, or a type change that fails or drops data once those tables have rows. That is the failure we would hit going from one schema to the next on a populated mojito, and this recipe does not exercise it.
A closer rehearsal, on the same docker/.data/db:
- Boot a build whose own latest migration is N (the revision that introduced
V{N}), with noFLYWAY_TARGET. Code and schema match, so the app stays up.FLYWAY_TARGETon the current image cannot host this step when current code will not run on schema N. The readme already says that boot may exit. - Seed through
docker/smoke/mojito-local:repo-create,pusha small source file,importtranslations for those strings. The source file has to be visible in the container (docker compose cp, or a bind mount). Record the repo id and one translated string. Skiprepo-type-createwhen N is below V69; that table is not there yet. - Leave the data directory in place. Recreate the webapp from the new image with
FLYWAY_TARGETunset, so Flyway applies only N+1 through latest. - Confirm the repo, the asset, and the imported translation are still there (
repo-view, pull or export). Then runcheck.sh, which covers the repo-type API on the upgraded schema.
check.sh only sees the final version, so it cannot tell this populated upgrade from a fresh migrate. The seed has to be checked before the second boot and again after.
There was a problem hiding this comment.
This is now fixed. Script is in docker/smoke/populated-upgrade.sh
It:
- Checks N, then asks you to confirm deletion of docker/.data/db. The answer is yes or y; capital letters and surrounding spaces are accepted. That directory holds the MySQL data and survives docker compose down. The first boot has to migrate an empty database to V{N}. Flyway records applied versions in this directory, so a leftover directory makes this run continue from the schema already stored there.
- Builds the past commit that introduced V{N}. The script puts that commit in a temporary directory. Your checkout stays on the branch you already have. This image starts on an empty database, and Flyway stops at V{N} because that is the newest migration in that old Mojito.
- Seeds a repository and checks the repository id, the messages.properties asset id, and the French translation.
- Builds a second image from the files in your current working tree. This is the branch you already have checked out, not the old commit from step 2. Only the webapp container is replaced. MySQL and docker/.data/db stay, so this webapp opens the populated V{N} database and Flyway applies N+1 through the latest migration in this tree.
- Checks that the same repository id, asset id, and translation are still there.
- Runs docker/smoke/check.sh. That check creates a repository type, so it passes only when the upgraded database includes V69, the migration that adds that table.
(I documented the flow above in readme.md).
There was a problem hiding this comment.
I tested it locally, ran docker/smoke/populated-upgrade.sh locally on Colima. The first image was 214b2f9, the commit that introduced V66. I confirmed deletion of docker/.data/db, seeded one repository, and then booted the current working tree against that same database.
Push reported one asset:
--> asset id: 1, task: 2
After the upgrade:
/opt/smoke-pull/messages_fr-FR.properties:greeting=Bonjour de la mise à niveau du schéma
Repository 1, asset 1, and translation survived the upgrade.
flyway_schema_history max version: 69 (highest migration: 69)
Smoke checks passed (Flyway version 69).
check.sh created, updated, and deleted a repository type. The closing list printed "No repo types found". Compose down removed the containers. docker/.data/db stayed on disk.
Keep the freshness check tied to an explicit image tag instead of Compose's default project-service naming.
Keep the host and container SQL receipts comparable when their locales disagree.
A Darwin node binary on PATH makes the Linux Maven build fail or use the wrong Node.
Mount laptop-built jars without rebuilding the image, and keep check.sh from treating that run as the baked image.
An empty migrate can accept a change that fails once a repository, an asset, and an imported translation are present.
|
Still one comment left to address |
Yep, this is work in progress, I'll let you know once done. |
The V66 image needs the amd64 node-sass binary and a stand-in git.properties, compose exec must not consume a piped recipe, and pull must scan only the source file.
The script confirms deletion of docker/.data/db and checks the selected base before removing it.
The bootstrap user is admin from l10n.bootstrap.default-user, the upgrade script is run from the repository root, and a failed first image build leaves the containers down.
The build record and the two image steps were easy to misread as one checkout.
Reject a leading-zero version, accept a normal delete confirmation, and read repo-view without perl.
Summary
docker/docker-compose-mysql-smoke.ymlruns MySQL 8.0.34 and the current Java 21 webapp (Dockerfile-bk21) together. The webapp is started the way a MySQL deploy is: Flyway on,ddl-auto=none, no HSQL init scripts. Data lives indocker/.data/dbon the host, sodocker compose downkeeps the database for the next run. Existingdocker-compose.ymlis left alone (it still targets Java 8 / MySQL 5.7).docker/smoke/populated-upgrade.sh. It builds the commit that introduced migration N (default 66) forlinux/amd64, boots that image on an empty database, and Flyway stops at N because N is the newest migration in that image. It then seeds one repository, onemessages.propertiesasset, and one French translation. A second image, built from the current working tree, replaces only the webapp.FLYWAY_TARGETstays unset, so that boot applies N+1 through the latest migration in the tree to the populated database. The script records the repository id, the asset id, and the translated string, and requires the same values after the upgrade. Java and SQL migrations both run through Mojito’s own Flyway inside the webapp.docker/smoke/check.shconfirms the app is UP, the running image still matches the sources it was built from,flyway_schema_historyreached the highest integer version in the tree, and repo-type create / list / update / view / delete work. That repo-type check passes only when the database includes V69, the migration that adds the table. CLI calls go throughdocker/smoke/mojito-local, which only talks to the smoke container at localhost:8080.check.shmust not mean “old image, new files on disk.” Flyway checksums migrations in the running jar againstflyway_schema_history; it does not readV*sources from the working tree. Edit a migration and leave an old process on 8080, and Flyway stays quiet.docker/smoke/build-image.shrecords the image id and a fingerprint of everyV*.sql/V*.java;docker/smoke/lib.shhashes those files;docker/smoke/check.shcompares the running container and the current tree to that record (docker/.data/webapp-build.txt, gitignored). Rebuild withbuild-image.shafter changing migrations;up --buildalone does not update the record. The optional host-jar edit loop is refused bycheck.sh.docker/readme.md.Changes by file
.dockerignore— excludesdocker/.datafrom the webapp image build. MySQL runs in a separate container and keeps its files on the host via bind-mount; those files must not be copied into the Mojito image..gitignore— ignoresdocker/local-jars/, the host-built jars copied for the edit loop.docker/Dockerfile-bk21— removes a copied Macnode/node_modulestree before Maven, and readsMOJITO_WEBAPP_JAR/MOJITO_CLI_JARwhen the container starts. Unset, both stay the jars baked into the image.docker/docker-compose-mysql-smoke.yml— the smoke stack: persisted MySQL, production-like Flyway settings, TCP healthcheck so the webapp does not start while port 3306 is still down,FLYWAY_TARGETmapped tospring.flyway.target. The populated-upgrade recipe leaves that variable unset.docker/docker-compose-mysql-smoke.local-jars.yml— opt-in edit loop. It mounts host jars over the baked jars.check.shrefuses this mount, so it is not the trusted smoke run.docker/readme.md— runbook for the harness: fresh DB, populated upgrade from the commit that introduced N, smoke checks, and the integer-version caveat. N is a plain integer with no leading zero. Supported bases start at V66, the first migration introduced by a Java 21 commit.docker/smoke/build-image.sh— builds the webapp image and records its ID plus a fingerprint of everyV*.sql/V*.javafile, so a later check can tell a stale image from the current tree.docker/smoke/lib.sh— shared hashing helpers for that fingerprint (Linuxsha256sumor macOSshasum).docker/smoke/check.sh— the pass/fail smoke: health, image/source freshness, Flyway version, and repo-type CRUD (including that delete actually removed the type).docker/smoke/mojito-local— runs the CLI inside the smoke webapp container and refuses any other host, so a laptopmojitopointed at a real server cannot be used by accident. Color codes are off, sorepo-viewprints a plain repository id.docker/smoke/populated-upgrade.sh— the populated upgrade. It checks N before asking to deletedocker/.data/db, acceptsyesoryin any case with surrounding spaces, builds the historical image, seeds, boots the current working tree on the same database, and compares the repository id, asset id, and translation.docker/smoke/fixtures/populated-upgrade/— the English source and French translation that seed uses.docker/smoke/stage-local-jars.sh— copies the host-built webapp and CLI jars onto stable names for the edit-loop mount.Test evidence
Verified locally on this branch (
flyway-mysql-smoke-harness) with Colima. Sections A, B, and D were run on 2026-10-05. Section C was run on 2026-10-19. CLI calls go throughdocker/smoke/mojito-localagainst the smoke stack. Image builds usedocker/smoke/build-image.shexcept for the historical image in section C, which is built bydocker/smoke/populated-upgrade.sh. Stack:docker/docker-compose-mysql-smoke.yml.A — Fresh database (V1 → latest)
up -dwith noFLYWAY_TARGET,docker/smoke/check.shpasses at Flyway 69. HealthUP; repo-type create / list / update / view / delete; delete removes only the type that run created.success = 1, one boot (installed_on10:29:11–13). Java migrations recorded as JDBC: V9Compute Word Count, V56TUCVAddAssetIdUpdater. V69Add repo type. Tablesrepo_type,repo_type_integrity_checker, and columnrepository.repo_type_idexist.(Full 1–69 captured locally; every version succeeded.)
B — Persistence across
compose downdemo-create -n Demoinserts repository id 1.downremoves containers and leavesdocker/.data/dbon disk. Nextup -d(no rebuild, noFLYWAY_TARGET): still Flyway 69,repo-view -n Demostill id 1.C — Populated upgrade from the commit that introduced V66
docker/smoke/populated-upgrade.shbuilds that commit, migrates an empty database to V66, seeds a repository, then replaces only the webapp with an image of the current working tree.FLYWAY_TARGETstays unset. The second boot applies the later migrations to the populated database.214b2f9670c5ea9ca5969a5794dd55ee0a19fee7. Deletingdocker/.data/dbrequired typingy. The script continued to the seed only after that image's Flyway version was 66.pushcreated one asset, id 1. The number aftertask:is that upload's background job id.check.shpassed at Flyway 69, including repo-type create / list / update / view / delete. Compose down removed the containers and leftdocker/.data/dbon disk.D — Editing an already-applied migration (negative)
Migration checksum mismatch for migration version 67. MySQL stayed up.git checkout --, rebuilt, recreated webapp.check.shpasses at 69. V67 no longer modified.