Update Manager Cog Documentation¶
Overview¶
The Update Manager cog provides comprehensive automated update and deployment management capabilities for the Discord bot. It handles system updates, dependency management, configuration synchronization, backup operations, and rollback functionality. This cog ensures the bot stays current with the latest features while maintaining system stability through careful update procedures and reliable rollback mechanisms.
Features¶
Core Functionality¶
- Automatic Updates: Scheduled and on-demand bot updates
- Dependency Management: Python package updates and compatibility checking
- Configuration Updates: Sync configuration changes across environments
- Backup Management: Automatic and manual system backups
- Rollback System: Quick reversion to previous stable versions
- Update Notifications: Real-time status updates to administrators
- Update Analytics: Track update success rates and system health
- Version Management: Multi-version support and staging environments
Key Components¶
UpdateManagerclass - Main cog implementation- Update scheduling and execution engine
- Backup and recovery system
- Version control and rollback mechanism
- Configuration management
- Update analytics and monitoring
Commands¶
/update_check¶
Checks for available updates and system information.
Parameters:
- check_type (string, optional): Type of check (all, packages, bot, config, dependencies)
- show_details (boolean, optional, default: false): Show detailed update information
- include_security (boolean, optional, default: true): Include security update information
Usage Examples:
/update_check check_type:"all" show_details:true include_security:true
/update_check check_type:"packages" show_details:false
/update_check check_type:"bot" show_details:true include_security:false
Required Permissions: Bot Owner or Administrator
/update_install¶
Installs available updates with safety checks.
Parameters:
- update_type (string, required): Type of update (all, packages, bot, config, dependencies)
- backup_before (boolean, optional, default: true): Create backup before update
- restart_required (boolean, optional, default: true): Allow bot restart if needed
- force_update (boolean, optional, default: false): Force update even with warnings
- backup_name (string, optional): Custom name for pre-update backup
Usage Examples:
/update_install update_type:"packages" backup_before:true restart_required:true
/update_install update_type:"bot" backup_name:"pre_v2.1.0" force_update:false
/update_install update_type:"all" backup_before:true force_update:false
Required Permissions: Bot Owner only
/update_status¶
Shows current update status and system health information.
Parameters:
- show_history (boolean, optional, default: false): Include recent update history
- show_metrics (boolean, optional, default: true): Include system metrics
- time_range (string, optional): Time range for history (1d, 7d, 30d)
Usage Examples:
/update_status show_history:true show_metrics:true time_range:"7d"
/update_status show_history:false show_metrics:true
/update_status show_history:true time_range:"30d"
Required Permissions: Bot Owner or Administrator
/update_rollback¶
Rolls back to a previous version with confirmation.
Parameters:
- target_version (string, required): Version to rollback to (specific version or last_backup)
- confirm (boolean, required): Confirmation flag (must be true)
- create_backup (boolean, optional, default: true): Create backup before rollback
- restore_config (boolean, optional, default: true): Restore configuration files
Usage Examples```
/update_rollback target_version:"v2.0.0" confirm:true create_backup:true
/update_rollback target_version:"last_backup" confirm:true restore_config:true
Required Permissions: Bot Owner only
/update_schedule¶
Schedules automatic updates with customizable timing.
Parameters:
- schedule_type (string, required): Type of schedule (daily, weekly, monthly, custom)
- update_time (string, optional): Time for updates in HH:MM format
- update_days (string, optional): Days for updates (comma-separated for weekly/monthly)
- update_types (string, required): Types of updates to include
- enabled (boolean, optional, default: true): Whether schedule is enabled
Usage Examples:
/update_schedule schedule_type:"weekly" update_time:"03:00" update_days:"sunday" update_types:"packages,bot" enabled:true
/update_schedule schedule_type:"daily" update_time:"02:00" update_types:"packages" enabled:true
/update_schedule schedule_type:"custom" update_days:"monday,thursday" update_time:"04:00" update_types:"all" enabled:false
Required Permissions: Bot Owner only
/backup_create¶
Creates manual system backup with optional compression.
Parameters:
- backup_type (string, optional): Type of backup (full, database, config, data_only)
- compress (boolean, optional, default: true): Compress backup files
- backup_name (string, optional): Custom name for backup
- include_logs (boolean, optional, default: false): Include log files
- description (string, optional): Backup description
Usage Examples:
/backup_create backup_type:"full" compress:true backup_name:"manual_backup_v2.1.0" description:"Pre-update backup"
/backup_create backup_type:"config" compress:false backup_name:"config_backup"
/backup_create backup_type:"database" compress:true include_logs:false
Required Permissions: Bot Owner or Administrator
/backup_list¶
Lists available backups with filtering options.
Parameters:
- backup_type (string, optional): Filter by backup type
- time_range (string, optional): Time range filter (7d, 30d, 90d, all)
- limit (int, optional, default: 20): Number of backups to display (1-50)
- sort_by (string, optional): Sort method (date, size, name, type)
Usage Examples:
/backup_list backup_type:"full" time_range:"30d" limit:10 sort_by:"date"
/backup_list backup_type:"database" sort_by:"size" limit:15
/backup_list time_range:"all" sort_by:"date" limit:25
Required Permissions: Bot Owner or Administrator
/backup_restore¶
Restores from a specific backup with verification.
Parameters:
- backup_id (string, required): ID or name of backup to restore
- confirm (boolean, required): Confirmation flag (must be true)
- restore_type (string, optional): What to restore (all, database, config, data)
- verify_backup (boolean, optional, default: true): Verify backup integrity before restore
Usage Examples:
/backup_restore backup_id:"backup_20241220_030000" confirm:true restore_type:"full" verify_backup:true
/backup_restore backup_id:"manual_backup_v2.1.0" confirm:true restore_type:"database" verify_backup:true
Required Permissions: Bot Owner only
/update_settings¶
Manages update and backup settings and preferences.
Parameters:
- setting_category (string, required): Category to configure (update, backup, notification, schedule)
- setting_name (string, required): Setting name
- setting_value (string, required): New setting value
- reset_to_default (boolean, optional, default: false): Reset to default value
Usage Examples:
/update_settings setting_category:"update" setting_name:"auto_restart" setting_value:"true"
/update_settings setting_category:"backup" setting_name:"max_backups" setting_value:"10"
/update_settings setting_category:"notification" setting_name:"update_notifications" setting_value:"true"
/update_settings setting_category:"backup" setting_name:"compression_level" setting_value:"6" reset_to_default:false
Required Permissions: Bot Owner only
Technical Implementation¶
Class Structure¶
class UpdateManager(commands.Cog):
def __init__(self, bot)
async def cog_load(self)
# Command handlers
async def update_check_command(self, interaction: discord.Interaction,
check_type: str = "all", show_details: bool = False,
include_security: bool = True)
async def update_install_command(self, interaction: discord.Interaction,
update_type: str, backup_before: bool = True,
restart_required: bool = True, force_update: bool = False,
backup_name: str = None)
async def update_status_command(self, interaction: discord.Interaction,
show_history: bool = False, show_metrics: bool = True,
time_range: str = "7d")
async def update_rollback_command(self, interaction: discord.Interaction,
target_version: str, confirm: bool,
create_backup: bool = True, restore_config: bool = True)
async def update_schedule_command(self, interaction: discord.Interaction,
schedule_type: str, update_time: str = None,
update_days: str = None, update_types: str = "all",
enabled: bool = True)
async def backup_create_command(self, interaction: discord.Interaction,
backup_type: str = "full", compress: bool = True,
backup_name: str = None, include_logs: bool = False,
description: str = None)
async def backup_list_command(self, interaction: discord.Interaction,
backup_type: str = None, time_range: str = "30d",
limit: int = 20, sort_by: str = "date")
async def backup_restore_command(self, interaction: discord.Interaction,
backup_id: str, confirm: bool,
restore_type: str = "all", verify_backup: bool = True)
async def update_settings_command(self, interaction: discord.Interaction,
setting_category: str, setting_name: str,
setting_value: str, reset_to_default: bool = False)
# Core functionality
async def check_for_updates(self, check_type: str) -> Dict[str, Any]
async def install_updates(self, update_type: str, options: Dict[str, Any]) -> bool
async def get_update_status(self) -> Dict[str, Any]
async def rollback_to_version(self, target_version: str, options: Dict[str, Any]) -> bool
async def create_backup(self, backup_type: str, options: Dict[str, Any]) -> str
async def list_backups(self, filters: Dict[str, Any]) -> List[Backup]
async def restore_backup(self, backup_id: str, options: Dict[str, Any]) -> bool
Update Data Models¶
from dataclasses import dataclass
from typing import Optional, List, Dict, Any, Union
from datetime import datetime, timedelta
from enum import Enum
import uuid
import json
class UpdateType(Enum):
ALL = "all"
BOT = "bot"
PACKAGES = "packages"
CONFIG = "config"
DEPENDENCIES = "dependencies"
class UpdateStatus(Enum):
PENDING = "pending"
CHECKING = "checking"
DOWNLOADING = "downloading"
INSTALLING = "installing"
RESTARTING = "restarting"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class BackupType(Enum):
FULL = "full"
DATABASE = "database"
CONFIG = "config"
DATA_ONLY = "data_only"
class BackupStatus(Enum):
CREATING = "creating"
COMPRESSING = "compressing"
COMPLETED = "completed"
FAILED = "failed"
CORRUPTED = "corrupted"
class ScheduleType(Enum):
DAILY = "daily"
WEEKLY = "weekly"
MONTHLY = "monthly"
CUSTOM = "custom"
@dataclass
class UpdateInfo:
update_type: UpdateType
version: str
size: Optional[int]
changelog: Optional[str]
security_update: bool
breaking_changes: bool
dependencies: List[str]
release_date: datetime
download_url: Optional[str]
checksum: Optional[str]
def to_dict(self) -> Dict[str, Any]:
return {
'update_type': self.update_type.value,
'version': self.version,
'size': self.size,
'changelog': self.changelog,
'security_update': self.security_update,
'breaking_changes': self.breaking_changes,
'dependencies': self.dependencies,
'release_date': self.release_date.isoformat(),
'download_url': self.download_url,
'checksum': self.checksum
}
@dataclass
class UpdateSession:
id: str
update_type: UpdateType
status: UpdateStatus
progress: int # 0-100
started_at: datetime
completed_at: Optional[datetime]
target_version: str
current_version: str
error_message: Optional[str]
steps_completed: List[str]
total_steps: int
rollback_available: bool
initiated_by: str
def to_dict(self) -> Dict[str, Any]:
return {
'id': self.id,
'update_type': self.update_type.value,
'status': self.status.value,
'progress': self.progress,
'started_at': self.started_at.isoformat(),
'completed_at': self.completed_at.isoformat() if self.completed_at else None,
'target_version': self.target_version,
'current_version': self.current_version,
'error_message': self.error_message,
'steps_completed': self.steps_completed,
'total_steps': self.total_steps,
'rollback_available': self.rollback_available,
'initiated_by': self.initiated_by
}
@dataclass
class Backup:
id: str
name: str
backup_type: BackupType
status: BackupStatus
file_path: Optional[str]
file_size: Optional[int]
compressed_size: Optional[int]
created_at: datetime
completed_at: Optional[datetime]
description: Optional[str]
checksum: Optional[str]
auto_created: bool
metadata: Dict[str, Any]
def to_dict(self) -> Dict[str, Any]:
return {
'id': self.id,
'name': self.name,
'backup_type': self.backup_type.value,
'status': self.status.value,
'file_path': self.file_path,
'file_size': self.file_size,
'compressed_size': self.compressed_size,
'created_at': self.created_at.isoformat(),
'completed_at': self.completed_at.isoformat() if self.completed_at else None,
'description': self.description,
'checksum': self.checksum,
'auto_created': self.auto_created,
'metadata': self.metadata
}
@dataclass
class UpdateSchedule:
id: str
name: str
schedule_type: ScheduleType
update_time: Optional[str]
update_days: List[str]
update_types: List[UpdateType]
enabled: bool
created_by: str
created_at: datetime
last_run: Optional[datetime]
next_run: Optional[datetime]
max_backups_to_keep: int
def to_dict(self) -> Dict[str, Any]:
return {
'id': self.id,
'name': self.name,
'schedule_type': self.schedule_type.value,
'update_time': self.update_time,
'update_days': self.update_days,
'update_types': [t.value for t in self.update_types],
'enabled': self.enabled,
'created_by': self.created_by,
'created_at': self.created_at.isoformat(),
'last_run': self.last_run.isoformat() if self.last_run else None,
'next_run': self.next_run.isoformat() if self.next_run else None,
'max_backups_to_keep': self.max_backups_to_keep
}
@dataclass
class SystemInfo:
version: str
python_version: str
bot_version: str
last_update: Optional[datetime]
uptime: timedelta
disk_usage: Dict[str, Any]
memory_usage: Dict[str, Any]
update_statistics: Dict[str, Any]
backup_statistics: Dict[str, Any]
def to_dict(self) -> Dict[str, Any]:
return {
'version': self.version,
'python_version': self.python_version,
'bot_version': self.bot_version,
'last_update': self.last_update.isoformat() if self.last_update else None,
'uptime_seconds': self.uptime.total_seconds(),
'disk_usage': self.disk_usage,
'memory_usage': self.memory_usage,
'update_statistics': self.update_statistics,
'backup_statistics': self.backup_statistics
}
Update Engine Implementation¶
import asyncio
import subprocess
import shutil
import zipfile
import tarfile
import hashlib
import logging
from pathlib import Path
class UpdateEngine:
def __init__(self, update_manager):
self.manager = update_manager
self.logger = logging.getLogger(__name__)
self.current_session = None
self.update_lock = asyncio.Lock()
async def check_for_updates(self, check_type: str = "all") -> Dict[str, Any]:
"""Check for available updates of specified types"""
results = {
'bot': None,
'packages': [],
'config': None,
'dependencies': [],
'last_check': datetime.now().isoformat(),
'total_updates': 0,
'security_updates': 0,
'breaking_updates': 0
}
try:
# Check bot updates
if check_type in ["all", "bot"]:
results['bot'] = await self._check_bot_updates()
# Check package updates
if check_type in ["all", "packages"]:
results['packages'] = await self._check_package_updates()
# Check config updates
if check_type in ["all", "config"]:
results['config'] = await self._check_config_updates()
# Check dependency updates
if check_type in ["all", "dependencies"]:
results['dependencies'] = await self._check_dependency_updates()
# Calculate statistics
for update_type, update_data in results.items():
if update_type in ['packages', 'dependencies'] and isinstance(update_data, list):
results['total_updates'] += len(update_data)
results['security_updates'] += sum(1 for u in update_data if u.get('security', False))
results['breaking_updates'] += sum(1 for u in update_data if u.get('breaking_changes', False))
elif update_data and isinstance(update_data, dict):
results['total_updates'] += 1
if update_data.get('security', False):
results['security_updates'] += 1
if update_data.get('breaking_changes', False):
results['breaking_updates'] += 1
return results
except Exception as e:
self.logger.error(f"Error checking for updates: {e}")
await func.report_error(e, "check_updates")
return {'error': str(e), 'last_check': datetime.now().isoformat()}
async def _check_bot_updates(self) -> Optional[UpdateInfo]:
"""Check for bot-specific updates"""
try:
# This would integrate with your bot's update server
# For now, simulate checking
current_version = await self._get_current_version()
# Check against update server (placeholder)
# update_info = await self._fetch_update_info(current_version)
# Simulated update info
update_info = UpdateInfo(
update_type=UpdateType.BOT,
version="2.1.0",
size=1024*1024, # 1MB
changelog="New features and bug fixes",
security_update=False,
breaking_changes=False,
dependencies=[],
release_date=datetime.now() - timedelta(days=1),
download_url=None,
checksum="abc123"
)
# Check if update is available
if self._is_newer_version(update_info.version, current_version):
return update_info
return None
except Exception as e:
self.logger.error(f"Error checking bot updates: {e}")
return None
async def _check_package_updates(self) -> List[Dict[str, Any]]:
"""Check for Python package updates"""
try:
# Run pip list --outdated
result = await asyncio.create_subprocess_exec(
"pip", "list", "--outdated", "--format=json",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await result.communicate()
if result.returncode != 0:
self.logger.error(f"pip list failed: {stderr.decode()}")
return []
# Parse outdated packages
outdated_packages = json.loads(stdout.decode())
updates = []
for package in outdated_packages:
update_info = {
'package': package['name'],
'current_version': package['version'],
'latest_version': package['latest_version'],
'size': None, # Would need additional API calls
'changelog': None, # Would need package API
'security': False, # Would need security audit
'breaking_changes': False, # Would need version analysis
'release_date': datetime.now(), # Placeholder
'requirements': [] # Would need dependency analysis
}
updates.append(update_info)
return updates
except Exception as e:
self.logger.error(f"Error checking package updates: {e}")
return []
async def _check_config_updates(self) -> Optional[Dict[str, Any]]:
"""Check for configuration template updates"""
try:
# Check for updated config templates
config_version = await self._get_config_version()
# Simulate config update check
if self._should_update_config(config_version):
return {
'update_type': 'config',
'version': 'v1.2',
'changes': ['Added new settings', 'Updated defaults'],
'requires_restart': True,
'breaking': False
}
return None
except Exception as e:
self.logger.error(f"Error checking config updates: {e}")
return None
async def _check_dependency_updates(self) -> List[Dict[str, Any]]:
"""Check for system dependency updates"""
try:
# Check system packages, Node.js packages, etc.
updates = []
# Check Node.js packages if applicable
if Path("package.json").exists():
node_updates = await self._check_node_package_updates()
updates.extend(node_updates)
# Check system packages (Linux)
system_updates = await self._check_system_package_updates()
updates.extend(system_updates)
return updates
except Exception as e:
self.logger.error(f"Error checking dependency updates: {e}")
return []
async def install_updates(self, update_type: str, options: Dict[str, Any]) -> bool:
"""Install updates with proper error handling and rollback support"""
async with self.update_lock:
session = UpdateSession(
id=str(uuid.uuid4()),
update_type=UpdateType(update_type),
status=UpdateStatus.PENDING,
progress=0,
started_at=datetime.now(),
completed_at=None,
target_version="",
current_version=await self._get_current_version(),
error_message=None,
steps_completed=[],
total_steps=self._calculate_total_steps(update_type),
rollback_available=False,
initiated_by=options.get('user_id', 'system')
)
self.current_session = session
try:
# Create backup if requested
if options.get('backup_before', True):
await self._create_pre_update_backup(options)
session.steps_completed.append("Pre-update backup created")
# Execute update steps
if update_type in ["all", "bot"]:
success = await self._install_bot_updates(session, options)
if not success:
return False
if update_type in ["all", "packages"]:
success = await self._install_package_updates(session, options)
if not success:
return False
if update_type in ["all", "config"]:
success = await self._install_config_updates(session, options)
if not success:
return False
if update_type in ["all", "dependencies"]:
success = await self._install_dependency_updates(session, options)
if not success:
return False
# Update session status
session.status = UpdateStatus.COMPLETED
session.completed_at = datetime.now()
session.progress = 100
# Restart if needed
if options.get('restart_required', True):
session.status = UpdateStatus.RESTARTING
await self._schedule_restart()
return True
except Exception as e:
session.status = UpdateStatus.FAILED
session.completed_at = datetime.now()
session.error_message = str(e)
self.logger.error(f"Update failed: {e}")
await func.report_error(e, f"install_{update_type}_updates")
return False
finally:
# Save session to database
await self.manager.save_update_session(session)
async def _install_bot_updates(self, session: UpdateSession, options: Dict[str, Any]) -> bool:
"""Install bot-specific updates"""
try:
session.status = UpdateStatus.DOWNLOADING
session.progress = 10
session.steps_completed.append("Starting bot update download")
# Download update
update_info = await self._check_bot_updates()
if not update_info:
return True # No updates to install
# Verify download
downloaded_file = await self._download_update(update_info)
if not await self._verify_checksum(downloaded_file, update_info.checksum):
raise ValueError("Update checksum verification failed")
session.status = UpdateStatus.INSTALLING
session.progress = 30
session.steps_completed.append("Update download verified")
# Stop bot gracefully
await self._stop_bot_services()
# Apply update
await self._apply_bot_update(downloaded_file, update_info)
session.progress = 70
session.steps_completed.append("Bot update applied")
# Restart bot
await self._start_bot_services()
session.progress = 90
session.steps_completed.append("Bot services restarted")
session.target_version = update_info.version
return True
except Exception as e:
session.error_message = f"Bot update failed: {str(e)}"
await func.report_error(e, "install_bot_updates")
return False
async def _install_package_updates(self, session: UpdateSession, options: Dict[str, Any]) -> bool:
"""Install Python package updates"""
try:
session.status = UpdateStatus.INSTALLING
session.progress += 10
# Get outdated packages
outdated_packages = await self._check_package_updates()
if not outdated_packages:
return True
# Install packages in batches to handle dependencies
for i, package in enumerate(outdated_packages):
session.steps_completed.append(f"Updating {package['package']}")
# Update package
await self._update_package(package['package'], package['latest_version'])
# Update progress
progress_increment = 80 / len(outdated_packages)
session.progress += progress_increment
return True
except Exception as e:
session.error_message = f"Package update failed: {str(e)}"
await func.report_error(e, "install_package_updates")
return False
async def rollback_to_version(self, target_version: str, options: Dict[str, Any]) -> bool:
"""Rollback to a previous version"""
try:
# Validate target version
if target_version == "last_backup":
backup = await self._get_latest_backup()
if not backup:
raise ValueError("No backup available for rollback")
target_version = backup.metadata.get('version', 'unknown')
else:
backup = await self._find_backup_by_version(target_version)
if not backup:
raise ValueError(f"No backup found for version {target_version}")
# Create backup before rollback
if options.get('create_backup', True):
await self._create_pre_rollback_backup(target_version)
# Stop services
await self._stop_bot_services()
# Restore from backup
await self._restore_from_backup(backup, options.get('restore_config', True))
# Restart services
await self._start_bot_services()
# Verify rollback
current_version = await self._get_current_version()
if current_version != target_version:
raise ValueError(f"Rollback verification failed: expected {target_version}, got {current_version}")
return True
except Exception as e:
await func.report_error(e, f"rollback_to_{target_version}")
return False
async def _get_current_version(self) -> str:
"""Get current bot version"""
# Implementation would read from VERSION file or git
return "2.0.0"
def _is_newer_version(self, new_version: str, current_version: str) -> bool:
"""Compare version numbers"""
# Simplified version comparison
new_parts = [int(x) for x in new_version.split('.')]
current_parts = [int(x) for x in current_version.split('.')]
return new_parts > current_parts
def _calculate_total_steps(self, update_type: str) -> int:
"""Calculate total steps for update progress tracking"""
steps = {
"all": 20,
"bot": 8,
"packages": 12,
"config": 6,
"dependencies": 10
}
return steps.get(update_type, 10)
async def _stop_bot_services(self):
"""Gracefully stop bot services"""
# Implementation would stop Discord connection, clean up resources
pass
async def _start_bot_services(self):
"""Start bot services"""
# Implementation would start Discord connection, initialize cogs
pass
Backup System Implementation¶
import gzip
import shutil
from pathlib import Path
class BackupSystem:
def __init__(self, update_manager):
self.manager = update_manager
self.backup_dir = Path("data/backups")
self.backup_dir.mkdir(exist_ok=True)
self.max_backups = 50
async def create_backup(self, backup_type: str, options: Dict[str, Any]) -> str:
"""Create a system backup"""
backup_id = str(uuid.uuid4())
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_name = options.get('backup_name', f"backup_{timestamp}")
backup = Backup(
id=backup_id,
name=backup_name,
backup_type=BackupType(backup_type),
status=BackupStatus.CREATING,
file_path=None,
file_size=None,
compressed_size=None,
created_at=datetime.now(),
completed_at=None,
description=options.get('description'),
checksum=None,
auto_created=False,
metadata={
'version': await self._get_current_version(),
'python_version': sys.version,
'backup_type': backup_type
}
)
try:
# Create backup directory
backup_path = self.backup_dir / backup_id
backup_path.mkdir(exist_ok=True)
# Execute backup based on type
if backup_type in ["full", "database"]:
await self._backup_database(backup_path)
if backup_type in ["full", "config"]:
await self._backup_config_files(backup_path)
if backup_type == "full":
await self._backup_data_files(backup_path)
if options.get('include_logs', False):
await self._backup_log_files(backup_path)
# Compress if requested
if options.get('compress', True):
compressed_path = await self._compress_backup(backup_path, backup_id)
await self._cleanup_uncompressed_backup(backup_path)
backup.file_path = str(compressed_path)
else:
backup.file_path = str(backup_path)
# Calculate file sizes
backup.file_size = await self._calculate_backup_size(backup_path)
backup.compressed_size = await self._calculate_backup_size(backup.file_path)
# Generate checksum
backup.checksum = await self._generate_backup_checksum(backup.file_path)
# Update backup status
backup.status = BackupStatus.COMPLETED
backup.completed_at = datetime.now()
# Save backup record
await self.manager.save_backup(backup)
# Clean up old backups
await self._cleanup_old_backups()
return backup_id
except Exception as e:
backup.status = BackupStatus.FAILED
await func.report_error(e, f"create_backup_{backup_type}")
raise
finally:
# Clean up on failure
if backup.status == BackupStatus.FAILED and backup_path.exists():
shutil.rmtree(backup_path)
async def _backup_database(self, backup_path: Path):
"""Backup database files"""
db_path = Path("data")
if db_path.exists():
for db_file in db_path.glob("*.db"):
shutil.copy2(db_file, backup_path / db_file.name)
async def _backup_config_files(self, backup_path: Path):
"""Backup configuration files"""
config_files = [
"bot.py",
"requirements.txt",
"addons/settings.py",
".env",
"base_configs/"
]
for config_file in config_files:
path = Path(config_file)
if path.exists():
if path.is_dir():
shutil.copytree(path, backup_path / path.name)
else:
shutil.copy2(path, backup_path / path.name)
async def _backup_data_files(self, backup_path: Path):
"""Backup user data and generated files"""
data_dirs = ["data", "generated_image.png", "logs.py"]
for data_item in data_dirs:
path = Path(data_item)
if path.exists():
if path.is_dir():
# Skip system directories
if path.name in ['.git', '__pycache__', 'node_modules']:
continue
shutil.copytree(path, backup_path / path.name)
else:
shutil.copy2(path, backup_path / path.name)
async def _backup_log_files(self, backup_path: Path):
"""Backup log files"""
log_files = []
for log_file in log_files:
path = Path(log_file)
if path.exists():
shutil.copy2(path, backup_path / path.name)
async def _compress_backup(self, backup_path: Path, backup_id: str) -> Path:
"""Compress backup directory"""
compressed_path = self.backup_dir / f"{backup_id}.tar.gz"
with tarfile.open(compressed_path, "w:gz") as tar:
tar.add(backup_path, arcname="backup")
return compressed_path
async def _cleanup_uncompressed_backup(self, backup_path: Path):
"""Remove uncompressed backup directory"""
if backup_path.exists():
shutil.rmtree(backup_path)
async def restore_backup(self, backup_id: str, options: Dict[str, Any]) -> bool:
"""Restore from backup"""
backup = await self.manager.get_backup(backup_id)
if not backup:
raise ValueError(f"Backup {backup_id} not found")
if backup.status != BackupStatus.COMPLETED:
raise ValueError(f"Backup {backup_id} is not in a restorable state")
# Verify backup integrity
if options.get('verify_backup', True):
if not await self._verify_backup_integrity(backup):
raise ValueError(f"Backup {backup_id} integrity check failed")
try:
# Stop services
await self._stop_bot_services()
# Extract and restore backup
if backup.file_path.endswith('.tar.gz'):
await self._extract_and_restore_tar_gz(backup.file_path, options)
else:
await self._restore_directory_backup(backup.file_path, options)
# Restore config if requested
if options.get('restore_config', True):
await self._restore_config_files()
# Start services
await self._start_bot_services()
return True
except Exception as e:
await func.report_error(e, f"restore_backup_{backup_id}")
return False
async def _verify_backup_integrity(self, backup: Backup) -> bool:
"""Verify backup file integrity"""
try:
current_checksum = await self._generate_backup_checksum(backup.file_path)
return current_checksum == backup.checksum
except Exception:
return False
async def _cleanup_old_backups(self):
"""Clean up old backups to save space"""
backups = await self.manager.get_all_backups()
# Sort by creation date (oldest first)
backups.sort(key=lambda b: b.created_at)
# Remove oldest backups if over limit
if len(backups) > self.max_backups:
for backup in backups[:-self.max_backups]:
try:
if backup.file_path and Path(backup.file_path).exists():
Path(backup.file_path).unlink()
await self.manager.delete_backup(backup.id)
except Exception as e:
self.logger.warning(f"Failed to remove old backup {backup.id}: {e}")
Error Handling¶
Update Error Management¶
async def handle_update_error(self, interaction, error, context: str, update_id: str = None):
"""Handle update-related errors with user-friendly messages"""
error_messages = {
"update_in_progress": "An update is already in progress. Please wait for it to complete.",
"permission_denied": "You don't have permission to perform updates.",
"backup_failed": "Failed to create backup before update.",
"download_failed": "Failed to download update files.",
"checksum_mismatch": "Update file checksum verification failed.",
"insufficient_space": "Insufficient disk space for update.",
"dependency_conflict": "Dependency conflict detected during update.",
"rollback_failed": "Rollback operation failed. System may be in unstable state.",
"backup_not_found": "Backup not found or corrupted.",
"restore_failed": "Backup restoration failed.",
"service_start_failed": "Failed to restart bot services after update.",
"update_interrupted": "Update was interrupted. System may need manual intervention.",
"version_conflict": "Version conflict detected. Cannot determine current version.",
"network_error": "Network error occurred during update download.",
"permission_error": "Permission denied while accessing update files."
}
# Determine error type and provide appropriate message
error_str = str(error).lower()
if "in progress" in error_str:
message = error_messages["update_in_progress"]
elif "permission" in error_str and "denied" in error_str:
message = error_messages["permission_denied"]
elif "backup" in error_str and "failed" in error_str:
message = error_messages["backup_failed"]
elif "download" in error_str and "failed" in error_str:
message = error_messages["download_failed"]
elif "checksum" in error_str and "mismatch" in error_str:
message = error_messages["checksum_mismatch"]
elif "space" in error_str or "disk" in error_str:
message = error_messages["insufficient_space"]
elif "dependency" in error_str and "conflict" in error_str:
message = error_messages["dependency_conflict"]
elif "rollback" in error_str and "failed" in error_str:
message = error_messages["rollback_failed"]
elif "not found" in error_str and "backup" in error_str:
message = error_messages["backup_not_found"]
elif "restore" in error_str and "failed" in error_str:
message = error_messages["restore_failed"]
elif "start" in error_str and "failed" in error_str:
message = error_messages["service_start_failed"]
elif "interrupted" in error_str:
message = error_messages["update_interrupted"]
elif "network" in error_str or "connection" in error_str:
message = error_messages["network_error"]
else:
message = f"Update operation failed: {str(error)}. Please check system logs for details."
await interaction.response.send_message(message, ephemeral=True)
await func.report_error(error, f"update_{context}_{update_id or 'unknown'}")
Usage Examples¶
Update Management Operations¶
Admin: /update_check check_type:"all" show_details:true include_security:true
Bot: 🔍 **Update Check Results**
### Available Updates: 3
• **Security Updates**: 1
• **Breaking Changes**: 0
• **Last Check**: 2 minutes ago
### Bot Update
• Version: v2.1.0 (current: v2.0.0)
• Size: 1.2 MB
• Changelog: "New features and bug fixes"
• Security: ✅ No
• Breaking: ✅ No
• Release: 2 days ago
### Package Updates (2)
1. **discord.py** v2.3.0 → v2.3.1 (Security Update)
2. **requests** v2.28.0 → v2.28.1
### Config Update
• Version: v1.2
• Changes: Added new settings, updated defaults
• Requires Restart: Yes
Admin: /update_install update_type:"packages" backup_before:true restart_required:true
Bot: 🚀 **Installing Updates**
Progress: 10% - Creating pre-update backup...
✅ Backup created successfully (backup_20241220_030000)
Progress: 30% - Updating discord.py v2.3.0 → v2.3.1
✅ discord.py updated successfully
Progress: 50% - Updating requests v2.28.0 → v2.28.1
✅ requests updated successfully
Progress: 90% - Verifying installations...
✅ All packages verified successfully
Progress: 100% - Update completed!
🎉 **Update Finished Successfully**
Updated packages: 2
Total time: 45 seconds
System restarted: Yes
Use /update_status to view detailed information.
Backup and Restore Operations¶
Admin: /backup_create backup_type:"full" compress:true backup_name:"pre_v2.1.0" description:"Pre-update backup"
Bot: 💾 **Creating Full Backup**
Progress: 20% - Backing up database files...
✅ Database backup completed
Progress: 40% - Backing up configuration files...
✅ Configuration backup completed
Progress: 60% - Backing up user data...
✅ User data backup completed
Progress: 80% - Compressing backup...
✅ Compression completed
Progress: 100% - Backup created successfully!
📦 **Backup Complete**
Backup ID: backup_20241220_030000
Name: pre_v2.1.0
Type: Full
Size: 156 MB (compressed: 45 MB)
Checksum: a1b2c3d4e5f6...
Description: Pre-update backup
Admin: /backup_list time_range:"30d" backup_type:"full" limit:10
Bot: 📂 **Available Backups (Last 30 days)**
1. **pre_v2.1.0** (backup_20241220_030000) - 2 hours ago
📊 Full backup, 156 MB → 45 MB compressed
✅ Verified and restorable
2. **auto_backup_weekly** (backup_20241215_030000) - 5 days ago
📊 Full backup, 142 MB → 41 MB compressed
✅ Verified and restorable
3. **pre_config_change** (backup_20241210_150000) - 10 days ago
📊 Config backup, 12 MB → 3 MB compressed
✅ Verified and restorable
Total backups: 3 (Stored: 89 MB)
Admin: /backup_restore backup_id:"backup_20241215_030000" confirm:true restore_type:"full" verify_backup:true
Bot: 🔄 **Restoring Backup**
⚠️ **This will restore the system to the state from 5 days ago**
Progress: 10% - Stopping bot services...
✅ Services stopped
Progress: 30% - Verifying backup integrity...
✅ Backup integrity verified
Progress: 50% - Restoring database...
✅ Database restored
Progress: 70% - Restoring configuration...
✅ Configuration restored
Progress: 90% - Restoring user data...
✅ User data restored
Progress: 100% - Starting bot services...
🎉 **Restore Completed Successfully**
Restored from: auto_backup_weekly
Restoration time: 2 minutes 15 seconds
System version: v2.0.0 (restored)
Your bot has been successfully rolled back to the previous state.
Schedule Details: • Type: Weekly updates • Time: 03:00 (3:00 AM) • Days: Sunday • Updates: packages, bot • Status: ✅ Enabled • Next Run: December 22, 2024 at 03:00
Automatic Actions: • Create backup before update: Yes • Restart bot if needed: Yes • Notify on completion: Yes • Keep maximum 10 backups
This schedule will automatically update your bot weekly.
Admin: /update_settings setting_category:"backup" setting_name:"max_backups" setting_value:"15" Bot: ⚙️ Settings Updated
Backup Settings: • Max Backups: 15 (previously: 10) • Compression Level: 6 • Auto Cleanup: Enabled • Backup Path: /media/ubuntu/4TB-HDD/ziyue/other/ai-bot/data/backups/
Settings have been applied successfully.
## Configuration Options
### Bot Settings
```python
# Configuration in addons/settings.py
UPDATE_MANAGER_CONFIG = {
"general": {
"auto_updates_enabled": False,
"update_check_interval": 3600, # 1 hour
"max_concurrent_updates": 1,
"require_confirmation": True,
"notify_on_completion": True
},
"backup": {
"enabled": True,
"max_backups": 50,
"auto_cleanup": True,
"compression_enabled": True,
"compression_level": 6,
"backup_retention_days": 90,
"backup_path": "data/backups/",
"verify_backups": True
},
"scheduling": {
"enabled": True,
"default_time": "03:00",
"max_schedules": 5,
"allowed_update_types": ["packages", "bot", "config"],
"require_backup_before_schedule": True
},
"safety": {
"require_backup_before_update": True,
"min_disk_space_mb": 1024, # 1GB
"max_update_size_mb": 100, # 100MB
"verify_downloads": True,
"test_updates": False,
"rollback_on_failure": True
},
"notifications": {
"update_start_notifications": True,
"update_progress_notifications": False,
"update_completion_notifications": True,
"error_notifications": True,
"backup_notifications": True,
"notify_channels": [] # Channel IDs
},
"logging": {
"level": "INFO",
"update_logs_path": "data/update_logs/",
"max_log_files": 10,
"log_rotation_size": 10485760 # 10MB
}
}
Integration Points¶
With Other Cogs¶
# Integration with language manager for localization
from cogs.language_manager import LanguageManager
# Integration with user data for backup preferences
from cogs.userdata import UserData
# Integration with memory systems for update context
from cogs.episodic_memory import EpisodicMemory
External Services¶
- Package Repositories: PyPI, npm, system package managers
- Update Servers: Custom update distribution servers
- Cloud Storage: AWS S3, Google Cloud Storage for backup storage
- Monitoring: System health monitoring and alerting
- CI/CD: Integration with continuous deployment pipelines
Related Files¶
cogs/update_manager.py- Main implementationdata/update_manager.db- SQLite database for updates and backupsdata/backups/- Backup storage directorydata/update_logs/- Update operation logstranslations/en_US/commands/update_manager.json- English translationsLanguageManager- Translation systemaddons.settings- Configuration management
Future Enhancements¶
Potential improvements: - Blue-Green Deployments: Zero-downtime update deployment - Incremental Updates: Partial updates for faster deployment - Cloud-based Backups: Offsite backup storage and recovery - AI-powered Update Testing: Automated testing of updates before deployment - Multi-server Coordination: Update coordination across multiple bot instances - Update Analytics Dashboard: Advanced metrics and reporting - Canary Releases: Gradual rollout of updates - Automated Security Patches: Automatic security update deployment - Configuration Drift Detection: Monitor and fix configuration changes - Performance Impact Monitoring: Track update performance impact - Emergency Update System: Critical update deployment for security patches