60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import asyncio
|
|||
|
|
import logging
|
|||
|
|
from datetime import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from .compose_manager import ComposeManager
|
|||
|
|
from .models import CommandResult
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MysqlBackupManager:
|
|||
|
|
def __init__(self, *, enabled: bool, backup_dir: str, dump_command: str, timeout_seconds: int, compose_manager: ComposeManager) -> None:
|
|||
|
|
self.enabled = enabled
|
|||
|
|
self.backup_dir = Path(backup_dir)
|
|||
|
|
self.dump_command = dump_command
|
|||
|
|
self.timeout_seconds = timeout_seconds
|
|||
|
|
self.compose_manager = compose_manager
|
|||
|
|
if self.enabled:
|
|||
|
|
self.backup_dir.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
async def backup_before_upgrade(self, target_release: str) -> CommandResult:
|
|||
|
|
if not self.enabled:
|
|||
|
|
return CommandResult(success=True, stdout="mysql backup skipped", returncode=0)
|
|||
|
|
|
|||
|
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|||
|
|
backup_file = self.backup_dir / f"mysql-backup-{target_release}-{timestamp}.sql"
|
|||
|
|
command = self.dump_command.replace("{backup_file}", str(backup_file))
|
|||
|
|
logger.info("开始执行MySQL备份,目标文件: %s", backup_file)
|
|||
|
|
result = await self._run_shell_command(command)
|
|||
|
|
if result.success:
|
|||
|
|
logger.info("MySQL备份完成: %s", backup_file)
|
|||
|
|
result.stdout = str(backup_file)
|
|||
|
|
else:
|
|||
|
|
logger.error("MySQL备份失败: %s", result.stderr or result.stdout)
|
|||
|
|
return result
|
|||
|
|
|
|||
|
|
async def _run_shell_command(self, command: str) -> CommandResult:
|
|||
|
|
process = await asyncio.create_subprocess_shell(
|
|||
|
|
command,
|
|||
|
|
cwd=str(self.compose_manager.working_dir),
|
|||
|
|
stdout=asyncio.subprocess.PIPE,
|
|||
|
|
stderr=asyncio.subprocess.PIPE,
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=self.timeout_seconds)
|
|||
|
|
except asyncio.TimeoutError:
|
|||
|
|
process.kill()
|
|||
|
|
await process.wait()
|
|||
|
|
return CommandResult(success=False, stderr=f"mysql backup timeout after {self.timeout_seconds}s", returncode=-1)
|
|||
|
|
|
|||
|
|
return CommandResult(
|
|||
|
|
success=process.returncode == 0,
|
|||
|
|
stdout=stdout.decode("utf-8", errors="ignore"),
|
|||
|
|
stderr=stderr.decode("utf-8", errors="ignore"),
|
|||
|
|
returncode=process.returncode or 0,
|
|||
|
|
)
|