Merakit-Deploy/wordpress/wordpress_deployer/deployment_config_manager.py

154 lines
4.4 KiB
Python

"""
Deployment Configuration Manager
Manages saving and loading deployment configurations for tracking and cleanup
"""
import json
import logging
from dataclasses import asdict, dataclass
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
logger = logging.getLogger(__name__)
@dataclass
class DeploymentMetadata:
"""Metadata for a single deployment"""
subdomain: str
url: str
domain: str
compose_project_name: str
db_name: str
db_user: str
deployment_timestamp: str
dns_record_id: Optional[str] = None
dns_ip: Optional[str] = None
containers: Optional[List[str]] = None
volumes: Optional[List[str]] = None
networks: Optional[List[str]] = None
env_file_path: Optional[str] = None
class DeploymentConfigManager:
"""Manages deployment configuration persistence"""
def __init__(self, config_dir: Path = Path("deployments")):
"""
Initialize deployment config manager
Args:
config_dir: Directory to store deployment configs
"""
self.config_dir = config_dir
self.config_dir.mkdir(exist_ok=True)
self._logger = logging.getLogger(f"{__name__}.DeploymentConfigManager")
def save_deployment(self, metadata: DeploymentMetadata) -> Path:
"""
Save deployment configuration to disk
Args:
metadata: DeploymentMetadata instance
Returns:
Path to saved config file
"""
# Create filename based on subdomain and timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{metadata.subdomain}_{timestamp}.json"
config_path = self.config_dir / filename
# Convert to dict and save as JSON
config_data = asdict(metadata)
with open(config_path, 'w') as f:
json.dump(config_data, f, indent=2)
self._logger.info(f"Saved deployment config: {config_path}")
return config_path
def load_deployment(self, config_file: Path) -> DeploymentMetadata:
"""
Load deployment configuration from disk
Args:
config_file: Path to config file
Returns:
DeploymentMetadata instance
Raises:
FileNotFoundError: If config file doesn't exist
ValueError: If config file is invalid
"""
if not config_file.exists():
raise FileNotFoundError(f"Config file not found: {config_file}")
with open(config_file, 'r') as f:
config_data = json.load(f)
return DeploymentMetadata(**config_data)
def list_deployments(self) -> List[Path]:
"""
List all deployment config files
Returns:
List of config file paths sorted by modification time (newest first)
"""
config_files = list(self.config_dir.glob("*.json"))
return sorted(config_files, key=lambda p: p.stat().st_mtime, reverse=True)
def find_deployment_by_subdomain(self, subdomain: str) -> Optional[Path]:
"""
Find the most recent deployment config for a subdomain
Args:
subdomain: Subdomain to search for
Returns:
Path to config file or None if not found
"""
matching_files = list(self.config_dir.glob(f"{subdomain}_*.json"))
if not matching_files:
return None
# Return most recent
return max(matching_files, key=lambda p: p.stat().st_mtime)
def find_deployment_by_url(self, url: str) -> Optional[Path]:
"""
Find deployment config by URL
Args:
url: Full URL to search for
Returns:
Path to config file or None if not found
"""
for config_file in self.list_deployments():
try:
metadata = self.load_deployment(config_file)
if metadata.url == url:
return config_file
except (ValueError, json.JSONDecodeError) as e:
self._logger.warning(f"Failed to load config {config_file}: {e}")
continue
return None
def delete_deployment_config(self, config_file: Path) -> None:
"""
Delete deployment config file
Args:
config_file: Path to config file
"""
if config_file.exists():
config_file.unlink()
self._logger.info(f"Deleted deployment config: {config_file}")