deb-mock/deb_mock/plugins/registry.py
robojerk 5e7f4b0562
Some checks failed
Build Deb-Mock Package / build (push) Successful in 55s
Lint Code / Lint All Code (push) Failing after 3s
Test Deb-Mock Build / test (push) Failing after 53s
Fix sbuild integration and clean up codebase
- Fix environment variable handling in sbuild wrapper
- Remove unsupported --log-dir and --env options from sbuild command
- Clean up unused imports and fix linting issues
- Organize examples directory with official Debian hello package
- Fix YAML formatting (trailing spaces, newlines)
- Remove placeholder example files
- All tests passing (30/30)
- Successfully tested build with official Debian hello package
2025-08-04 04:34:32 +00:00

361 lines
12 KiB
Python

"""
Plugin Registry for Deb-Mock Plugin System
This module provides the plugin registration and management functionality
for the Deb-Mock plugin system, inspired by Fedora's Mock plugin architecture.
"""
import importlib
import logging
from typing import Any, Dict, Optional, Type
from .base import BasePlugin
logger = logging.getLogger(__name__)
class PluginRegistry:
"""
Manages plugin registration and instantiation.
This class provides the functionality for registering plugin classes
and creating plugin instances, following Mock's plugin system pattern.
"""
def __init__(self):
"""Initialize the plugin registry."""
self.plugins: Dict[str, Type[BasePlugin]] = {}
self.plugin_metadata: Dict[str, Dict[str, Any]] = {}
# Auto-register built-in plugins
self._register_builtin_plugins()
def register(
self,
plugin_name: str,
plugin_class: Type[BasePlugin],
metadata: Optional[Dict[str, Any]] = None,
) -> None:
"""
Register a plugin class.
Args:
plugin_name: Name of the plugin
plugin_class: Plugin class to register
metadata: Optional metadata about the plugin
Raises:
ValueError: If plugin_name is already registered
TypeError: If plugin_class is not a subclass of BasePlugin
"""
if not issubclass(plugin_class, BasePlugin):
raise TypeError("Plugin class must inherit from BasePlugin")
if plugin_name in self.plugins:
raise ValueError(f"Plugin '{plugin_name}' is already registered")
self.plugins[plugin_name] = plugin_class
self.plugin_metadata[plugin_name] = metadata or {}
logger.debug(f"Registered plugin '{plugin_name}' with class {plugin_class.__name__}")
def unregister(self, plugin_name: str) -> bool:
"""
Unregister a plugin.
Args:
plugin_name: Name of the plugin to unregister
Returns:
True if plugin was unregistered, False if not found
"""
if plugin_name not in self.plugins:
return False
del self.plugins[plugin_name]
del self.plugin_metadata[plugin_name]
logger.debug(f"Unregistered plugin '{plugin_name}'")
return True
def get_plugin_class(self, plugin_name: str) -> Optional[Type[BasePlugin]]:
"""
Get a registered plugin class.
Args:
plugin_name: Name of the plugin
Returns:
Plugin class if found, None otherwise
"""
return self.plugins.get(plugin_name)
def get_plugins(self) -> Dict[str, Type[BasePlugin]]:
"""
Get all registered plugins.
Returns:
Dictionary of registered plugin names and classes
"""
return self.plugins.copy()
def get_plugin_names(self) -> list:
"""
Get list of registered plugin names.
Returns:
List of registered plugin names
"""
return list(self.plugins.keys())
def create(self, plugin_name: str, config: Any, hook_manager: Any) -> Optional[BasePlugin]:
"""
Create a plugin instance.
Args:
plugin_name: Name of the plugin to create
config: Configuration object
hook_manager: Hook manager instance
Returns:
Plugin instance if successful, None if plugin not found
"""
plugin_class = self.get_plugin_class(plugin_name)
if not plugin_class:
logger.warning(f"Plugin '{plugin_name}' not found")
return None
try:
plugin_instance = plugin_class(config, hook_manager)
logger.debug(f"Created plugin instance '{plugin_name}'")
return plugin_instance
except Exception as e:
logger.error(f"Failed to create plugin '{plugin_name}': {e}")
return None
def create_all_enabled(self, config: Any, hook_manager: Any) -> Dict[str, BasePlugin]:
"""
Create instances of all enabled plugins.
Args:
config: Configuration object
hook_manager: Hook manager instance
Returns:
Dictionary of plugin names and instances
"""
enabled_plugins = {}
for plugin_name in self.get_plugin_names():
plugin_instance = self.create(plugin_name, config, hook_manager)
if plugin_instance and plugin_instance.enabled:
enabled_plugins[plugin_name] = plugin_instance
logger.debug(f"Created {len(enabled_plugins)} enabled plugin instances")
return enabled_plugins
def get_plugin_info(self, plugin_name: str) -> Dict[str, Any]:
"""
Get information about a registered plugin.
Args:
plugin_name: Name of the plugin
Returns:
Dictionary with plugin information
"""
if plugin_name not in self.plugins:
return {"error": f'Plugin "{plugin_name}" not found'}
plugin_class = self.plugins[plugin_name]
metadata = self.plugin_metadata[plugin_name]
info = {
"name": plugin_name,
"class": plugin_class.__name__,
"module": plugin_class.__module__,
"metadata": metadata,
"docstring": plugin_class.__doc__ or "No documentation available",
}
return info
def get_all_plugin_info(self) -> Dict[str, Dict[str, Any]]:
"""
Get information about all registered plugins.
Returns:
Dictionary mapping plugin names to their information
"""
return {name: self.get_plugin_info(name) for name in self.get_plugin_names()}
def load_plugin_from_module(self, module_name: str, plugin_name: str) -> bool:
"""
Load a plugin from a module.
Args:
module_name: Name of the module to load
plugin_name: Name of the plugin class in the module
Returns:
True if plugin was loaded successfully, False otherwise
"""
try:
module = importlib.import_module(module_name)
plugin_class = getattr(module, plugin_name)
# Use module name as plugin name if not specified
self.register(plugin_name, plugin_class)
return True
except ImportError as e:
logger.error(f"Failed to import module '{module_name}': {e}")
return False
except AttributeError as e:
logger.error(f"Plugin class '{plugin_name}' not found in module '{module_name}': {e}")
return False
except Exception as e:
logger.error(f"Failed to load plugin from '{module_name}.{plugin_name}': {e}")
return False
def load_plugins_from_config(self, config: Any) -> Dict[str, BasePlugin]:
"""
Load plugins based on configuration.
Args:
config: Configuration object with plugin settings
Returns:
Dictionary of loaded plugin instances
"""
loaded_plugins = {}
if not hasattr(config, "plugins") or not config.plugins:
return loaded_plugins
for plugin_name, plugin_config in config.plugins.items():
if not isinstance(plugin_config, dict):
continue
if plugin_config.get("enabled", False):
# Try to load from built-in plugins first
plugin_instance = self.create(plugin_name, config, None)
if plugin_instance:
loaded_plugins[plugin_name] = plugin_instance
else:
# Try to load from external module
module_name = plugin_config.get("module")
if module_name:
if self.load_plugin_from_module(module_name, plugin_name):
plugin_instance = self.create(plugin_name, config, None)
if plugin_instance:
loaded_plugins[plugin_name] = plugin_instance
return loaded_plugins
def _register_builtin_plugins(self) -> None:
"""Register built-in plugins."""
try:
# Import and register built-in plugins
from .bind_mount import BindMountPlugin
from .compress_logs import CompressLogsPlugin
from .root_cache import RootCachePlugin
from .tmpfs import TmpfsPlugin
self.register(
"bind_mount",
BindMountPlugin,
{
"description": "Mount host directories into chroot",
"hooks": ["mount_root", "postumount"],
"builtin": True,
},
)
self.register(
"compress_logs",
CompressLogsPlugin,
{
"description": "Compress build logs to save space",
"hooks": ["process_logs"],
"builtin": True,
},
)
self.register(
"root_cache",
RootCachePlugin,
{
"description": "Root cache management for faster builds",
"hooks": [
"preinit",
"postinit",
"postchroot",
"postshell",
"clean",
],
"builtin": True,
},
)
self.register(
"tmpfs",
TmpfsPlugin,
{
"description": "Use tmpfs for faster I/O operations",
"hooks": ["mount_root", "postumount"],
"builtin": True,
},
)
logger.debug("Registered built-in plugins")
except ImportError as e:
logger.warning(f"Some built-in plugins could not be loaded: {e}")
except Exception as e:
logger.warning(f"Error registering built-in plugins: {e}")
def get_plugin_statistics(self) -> Dict[str, Any]:
"""
Get statistics about registered plugins.
Returns:
Dictionary with plugin statistics
"""
stats = {
"total_plugins": len(self.plugins),
"builtin_plugins": len([p for p in self.plugin_metadata.values() if p.get("builtin", False)]),
"external_plugins": len([p for p in self.plugin_metadata.values() if not p.get("builtin", False)]),
"plugins_by_hook": {},
}
# Count plugins by hook usage
for plugin_name, metadata in self.plugin_metadata.items():
hooks = metadata.get("hooks", [])
for hook in hooks:
if hook not in stats["plugins_by_hook"]:
stats["plugins_by_hook"][hook] = []
stats["plugins_by_hook"][hook].append(plugin_name)
return stats
def validate_plugin_config(self, plugin_name: str, config: Any) -> bool:
"""
Validate plugin configuration.
Args:
plugin_name: Name of the plugin
config: Configuration to validate
Returns:
True if configuration is valid, False otherwise
"""
if plugin_name not in self.plugins:
return False
# Basic validation - plugins can override this method
plugin_class = self.plugins[plugin_name]
if hasattr(plugin_class, "validate_config"):
return plugin_class.validate_config(config)
return True