diff --git a/docker-compose.yml b/docker-compose.yml index d161be9..fcbf2ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: APP_ENV: development LOG: debug TZ: "Europe/Paris" - EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiNzM0NjU3Y2YtMGQzYy00Y2UwLTkyODQtZDJmOGYyMjI2MzgzIiwibWFzdGVyS2V5QjY0IjoiMUh0djdtWCtYVkJxL0IzUEV2WDlZZjlQeUdVZW5oRHlXemo5THRqNW90WT0ifQ==" + EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiOTFlYmY4MmYtY2I4OC00N2M3LWE5MWUtMDUyMmU2NzNjZjE4IiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ==" #CHUNK_SIZE_MB: "1" #POOLING: 1 #DATABASES_CONFIG_FILE: "config.toml" @@ -35,8 +35,6 @@ volumes: databases_sqlite-data: external: true - - networks: portabase: name: portabase_network diff --git a/justfile b/justfile index 135ef1d..47a04d5 100644 --- a/justfile +++ b/justfile @@ -42,7 +42,8 @@ seed-firebird: docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb < ./scripts/firebird/seed.sql echo "Verifying Firebird tables..." - docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb -sql "SHOW TABLES;" + echo "SELECT RDB\$RELATION_NAME FROM RDB\$RELATIONS WHERE RDB\$SYSTEM_FLAG = 0 AND RDB\$VIEW_BLR IS NULL;" \ + | docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb seed-all: just seed-mongo diff --git a/src/domain/mariadb/backup.rs b/src/domain/mariadb/backup.rs index ff323f1..1a2bfef 100644 --- a/src/domain/mariadb/backup.rs +++ b/src/domain/mariadb/backup.rs @@ -33,6 +33,7 @@ pub async fn run( let mariadb_dump = select_mariadb_path(&version).join("mariadb-dump"); info!("Mariadb dump found: {}", mariadb_dump.display()); + let output = Command::new("mariadb-dump") .arg("--host").arg(&cfg.host) .arg("--port").arg(cfg.port.to_string()) @@ -43,18 +44,18 @@ pub async fn run( .arg("--single-transaction") .arg("--quick") .arg("--skip-lock-tables") - .arg("--add-drop-database") - .arg("--databases").arg(&cfg.database) + .arg("--no-create-db") + .arg("--skip-add-drop-table") .arg("--compress") .arg("--max-allowed-packet=512M") .arg("--net-buffer-length=16K") .arg("--default-character-set=utf8mb4") + .arg(&cfg.database) .arg("-r").arg(&file_path) .envs(env) .output() .with_context(|| format!("Failed to run mariadb-dump for {}", cfg.name))?; - if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); anyhow::bail!("Mariadb backup failed for {}: {}", cfg.name, stderr); diff --git a/src/domain/mariadb/restore.rs b/src/domain/mariadb/restore.rs index 8433277..5357cbf 100644 --- a/src/domain/mariadb/restore.rs +++ b/src/domain/mariadb/restore.rs @@ -15,9 +15,9 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { .with_context(|| format!("Failed to open restore file {}", restore_file.display()))?; file.read_to_string(&mut sql_content) .with_context(|| format!("Failed to read restore file {}", restore_file.display()))?; - + let drop_create_cmd = format!( - "DROP DATABASE IF EXISTS {0}; CREATE DATABASE {0};", + "DROP DATABASE IF EXISTS `{0}`; CREATE DATABASE `{0}`;", cfg.database ); @@ -47,6 +47,7 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { .arg(cfg.port.to_string()) .arg("--user") .arg(&cfg.username) + .arg("--database") .arg(&cfg.database) .env("MYSQL_PWD", &cfg.password) .stdin(std::process::Stdio::piped()) @@ -77,4 +78,4 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { handle.await??; Ok(()) -} \ No newline at end of file +} diff --git a/src/domain/mongodb/backup.rs b/src/domain/mongodb/backup.rs index 7abb6fd..d4724b3 100644 --- a/src/domain/mongodb/backup.rs +++ b/src/domain/mongodb/backup.rs @@ -15,7 +15,6 @@ pub async fn run( let file_path = backup_dir.join(format!("{}{}", cfg.generated_id, file_extension)); let mongodump = select_mongo_path().join("mongodump"); - info!("{:?}", mongodump); let uri = get_mongo_uri(cfg.clone())?; let output = Command::new(mongodump) diff --git a/src/domain/mongodb/connection.rs b/src/domain/mongodb/connection.rs index 311895b..912f140 100644 --- a/src/domain/mongodb/connection.rs +++ b/src/domain/mongodb/connection.rs @@ -28,3 +28,18 @@ pub fn get_mongo_uri(cfg: DatabaseConfig) -> Result { )) } } + + +pub fn extract_db_name(dry_output: &str) -> Option { + let mut dbs = std::collections::HashSet::new(); + for line in dry_output.lines() { + if let Some(pos) = line.find("archive prelude ") { + let rest = &line[pos + "archive prelude ".len()..]; + if let Some(dot) = rest.find('.') { + let db = &rest[..dot]; + dbs.insert(db.to_string()); + } + } + } + dbs.into_iter().next() +} diff --git a/src/domain/mongodb/restore.rs b/src/domain/mongodb/restore.rs index 617834d..0a8012d 100644 --- a/src/domain/mongodb/restore.rs +++ b/src/domain/mongodb/restore.rs @@ -1,4 +1,4 @@ -use crate::domain::mongodb::connection::{get_mongo_uri, select_mongo_path}; +use crate::domain::mongodb::connection::{extract_db_name, get_mongo_uri, select_mongo_path}; use crate::services::config::DatabaseConfig; use anyhow::{Context, Result}; use std::path::PathBuf; @@ -12,18 +12,41 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { let mongorestore = select_mongo_path().join("mongorestore"); let uri = get_mongo_uri(cfg.clone())?; - let output = Command::new(mongorestore) + let dry_run = Command::new(&mongorestore) + .arg(format!( + "--uri={}", + format!( + "mongodb://{}:{}@{}:{}/?authSource=admin", + cfg.username, cfg.password, cfg.host, cfg.port + ) + )) + .arg(format!("--archive={}", restore_file.display())) + .arg("--gzip") + .arg("--dryRun") + .arg("--verbose") + .output()?; + + let dry_output = String::from_utf8_lossy(&dry_run.stderr); + let source_db = extract_db_name(&dry_output) + .context("Could not detect source database name from archive")?; + + info!("Detected source database in archive: {}", source_db); + + let output = Command::new(&mongorestore) .arg(format!("--uri={}", uri)) .arg(format!("--archive={}", restore_file.display())) .arg("--gzip") .arg("--drop") + .arg(format!("--nsInclude={}.*", source_db)) + .arg(format!("--nsFrom={}.*", source_db)) + .arg(format!("--nsTo={}.*", cfg.database)) .output() .with_context(|| format!("Failed to run mongorestore for {}", cfg.name))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); error!("MongoDB restore failed for {}: {}", cfg.name, stderr); - anyhow::bail!("MongoDB restore failed for : {}", cfg.name); + anyhow::bail!("MongoDB restore failed for: {}", cfg.name); } info!("MongoDB restore completed for {}", cfg.name); diff --git a/src/domain/mysql/backup.rs b/src/domain/mysql/backup.rs index 85d2e2f..9cc1071 100644 --- a/src/domain/mysql/backup.rs +++ b/src/domain/mysql/backup.rs @@ -30,25 +30,23 @@ pub async fn run( let file_path = backup_dir.join(format!("{}{}", cfg.generated_id, file_extension)); - // let mysql_dump = select_mysql_path(&version).join("mysqldump"); - // info!("MySQL dump found: {}", mysql_dump.display()); - let output = Command::new("mysqldump") .arg("--host") - .arg(cfg.host) + .arg(&cfg.host) .arg("--port") .arg(cfg.port.to_string()) .arg("--user") - .arg(cfg.username) + .arg(&cfg.username) .arg("--routines") .arg("--events") .arg("--triggers") - .arg("--verbose") .arg("--single-transaction") .arg("--quick") - .arg("--add-drop-database") - .arg("--databases") - .arg(cfg.database) + .arg("--skip-lock-tables") + .arg("--skip-add-drop-table") + .arg("--no-create-db") // IMPORTANT + .arg("--default-character-set=utf8mb4") + .arg(&cfg.database) // IMPORTANT: NOT --databases .arg("-r") .arg(&file_path) .envs(env) diff --git a/src/domain/mysql/restore.rs b/src/domain/mysql/restore.rs index f662a50..8f569aa 100644 --- a/src/domain/mysql/restore.rs +++ b/src/domain/mysql/restore.rs @@ -17,7 +17,7 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { .with_context(|| format!("Failed to read restore file {}", restore_file.display()))?; let drop_create_cmd = format!( - "DROP DATABASE IF EXISTS {0}; CREATE DATABASE {0};", + "DROP DATABASE IF EXISTS `{0}`; CREATE DATABASE `{0}`;", cfg.database ); @@ -47,6 +47,7 @@ pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> { .arg(cfg.port.to_string()) .arg("--user") .arg(&cfg.username) + .arg("--database") .arg(&cfg.database) .env("MYSQL_PWD", &cfg.password) .stdin(std::process::Stdio::piped()) diff --git a/src/domain/postgres/restore.rs b/src/domain/postgres/restore.rs index 98a7732..f722556 100644 --- a/src/domain/postgres/restore.rs +++ b/src/domain/postgres/restore.rs @@ -37,8 +37,8 @@ pub async fn run( info!("Connections terminated for database {}", cfg.name); let url = format!( - "postgresql://{}:{}@{}:{}/postgres", - cfg.username, cfg.password, cfg.host, cfg.port + "postgresql://{}:{}@{}:{}/{}", + cfg.username, cfg.password, cfg.host, cfg.port, cfg.database ); debug!("Restore URL: {}", url); @@ -51,7 +51,7 @@ pub async fn run( .arg("--no-privileges") .arg("--clean") .arg("--if-exists") - .arg("--create") + // .arg("--create") .arg("--dbname") .arg(&url) .arg("-v") @@ -139,7 +139,7 @@ pub async fn run( .arg("--no-privileges") .arg("--clean") .arg("--if-exists") - .arg("--create") + // .arg("--create") .arg("--dbname") .arg(&url) .arg("-v") diff --git a/src/services/status.rs b/src/services/status.rs index 3f8a018..c117d4c 100644 --- a/src/services/status.rs +++ b/src/services/status.rs @@ -33,7 +33,6 @@ impl StatusService { let db_engine = DatabaseFactory::create_for_backup(db.clone()).await; let reachable = db_engine.ping().await?; - info!("Ping {} => {:?}",db.name, reachable); Ok::(DatabasePayload {