fix(backup)!: read the instance from one engine-name set, and trust it

Two shapes fell through the inline regex, which knew `database`, `db` and
`postgres` only. A container named exactly after its engine - what a compose
file writes as `container_name: postgres` - carries no separator before the
token, so it resolved to nothing and 6.0.0 stopped dumping it without saying
so. And a swarm task of a central MariaDB reads `mariadb_mariadb.1.<id>`,
where `_mariadb` was no token at all, so that database has never been dumped
under swarm at all.

ENGINE_NAMES states the set once and serves both readings: carried as a
suffix it makes the rest the instance, being one outright makes the container
its own instance.

backup_mariadb_or_postgres stops calling an application container a database.
container_engine recognises an engine by its client tools, which an
application image often ships, so refusing the dump alone would have recorded
the volume as `database: true, dumped: false` - the exact shape a restore
drill reads as a database that was missed. Without an instance there is no
database to record.

BREAKING CHANGE: `mariadb` and `mysql` join the suffix tokens, so a container
named `<app>-mariadb` resolves to the instance `<app>` rather than to its own
name. A databases.csv keyed on the full container name has to move to the
application name, or name the container in --database-containers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-18 05:11:46 +02:00
parent 704481a505
commit f97efb10c4
4 changed files with 56 additions and 13 deletions

View File

@@ -13,10 +13,11 @@ by its own launcher and ships pg_dumpall, so a dump command starts there and
writes a file that looks like a backup and holds none of the data.
"""
import json
import unittest
from pathlib import Path
from baudolo.generation import DUMP_SUFFIX, FILES_DIR, SQL_DIR
from baudolo.generation import DUMP_SUFFIX, FILES_DIR, MANIFEST_FILE, SQL_DIR
from .helpers import (
POSTGRES_DATA_DIR,
@@ -158,6 +159,26 @@ class TestE2EAppContainerShipsClientTools(unittest.TestCase):
self.assertTrue(marker.is_file(), f"expected a file backup at {marker}")
self.assertIn(MARKER, marker.read_text(encoding="utf-8"))
def test_the_manifest_does_not_call_the_application_volume_a_database(self) -> None:
manifest = json.loads(
(self.volume_dir(self.app_volume).parent / MANIFEST_FILE).read_text(
encoding="utf-8"
)
)
entry = manifest["volumes"][self.app_volume]
self.assertFalse(entry["database"], entry)
self.assertFalse(entry["dumped"], entry)
def test_the_manifest_records_the_engine_volume_as_dumped(self) -> None:
manifest = json.loads(
(self.volume_dir(self.engine_volume).parent / MANIFEST_FILE).read_text(
encoding="utf-8"
)
)
entry = manifest["volumes"][self.engine_volume]
self.assertTrue(entry["database"], entry)
self.assertTrue(entry["dumped"], entry)
if __name__ == "__main__":
unittest.main()