Architecture Documentation¶
Overview¶
The elfshoe uses a modular, plugin-based architecture that separates concerns and makes it easy to extend with new distributions.
Directory Structure¶
ipxe/
├── src/
│ └── elfshoe/ # Main package
│ ├── __init__.py # Package exports
│ ├── __main__.py # Module entry point (python -m)
│ ├── cli.py # Command-line interface
│ ├── builder.py # DistributionBuilder class
│ ├── core/ # Core functionality
│ │ ├── __init__.py # Package exports
│ │ ├── models.py # Data models (BootEntry, DistributionMenu)
│ │ ├── validator.py # URL validation
│ │ └── generator.py # Jinja2 menu generation
│ ├── distributions/ # Distribution-specific modules
│ │ ├── __init__.py # Provider registry
│ │ ├── base.py # AbstractMetadataFetcher interface
│ │ ├── fedora.py # Fedora implementation
│ │ └── [future: centos.py, debian.py, windows.py, ...]
│ └── templates/ # Jinja2 templates
│ ├── main_elfshoe.ipxe.j2
│ ├── distribution_submenus.ipxe.j2
│ └── additional_items.ipxe.j2
├── tests/ # Test suite
│ ├── conftest.py # Pytest fixtures
│ ├── test_elfshoe.py # Model tests
│ ├── test_url_validator.py # Validation tests
│ └── test_menu_generator.py # Generation tests
├── config.yaml # Main configuration
└── elfshoe.ipxe # Generated output
Key Components¶
1. Core Package (src/core/)¶
Purpose: Provides reusable components independent of specific distributions.
models.py¶
BootEntry: Dataclass representing a single boot optionDistributionMenu: Dataclass representing a distribution submenu
validator.py¶
URLValidator: Validates that boot files exist before including in menu- Uses HEAD requests for efficiency
- Optional validation for faster iteration
generator.py¶
MenuGenerator: Renders iPXE menus using Jinja2 templates- Separates presentation logic from business logic
- Template path configurable for customization
2. Distributions Package (src/distributions/)¶
Purpose: Pluggable system for distribution-specific metadata fetching.
base.py¶
AbstractMetadataFetcher: Interface that all fetchers must implement- Single method:
fetch_versions(metadata_url, **filters) -> List[str]
Provider Implementations¶
- Each distribution gets its own module (e.g.,
fedora.py) - Implements metadata fetching logic specific to that distribution
- Returns sorted list of available versions
Registry (distributions/__init__.py)¶
3. Command-Line Interface (cli.py)¶
Purpose: CLI orchestration and argument parsing.
main() function¶
- Parses command-line arguments
- Loads configuration from YAML
- Orchestrates builder and generator
- Handles verbose/quiet output
4. Distribution Builder (builder.py)¶
Purpose: Builds distribution menus from configuration.
DistributionBuilder class¶
- Builds distribution menus from configuration
- Supports two types:
- Static: Fixed version list from config
- Dynamic: Fetches versions from metadata provider
- Handles URL validation
- Manages verbose/quiet output
Data Flow¶
┌─────────────┐
│ config.yaml │
└──────┬──────┘
│
▼
┌──────────────────────┐
│ cli.py │
│ - Loads config │
│ - Creates Builder │
└──────┬───────────────┘
│
▼
┌──────────────────────────────────┐
│ DistributionBuilder │
│ - For each distribution: │
│ • Static → use config versions│
│ • Dynamic → fetch metadata │
│ - Validates URLs (optional) │
│ - Creates BootEntry objects │
└──────┬───────────────────────────┘
│
├─[dynamic]──┐
│ ▼
│ ┌────────────────────────┐
│ │ MetadataFetcher │
│ │ - Fetch from source │
│ │ - Parse format │
│ │ - Return versions │
│ └────────┬───────────────┘
│ │
├─────────────┘
│
▼
┌──────────────────────┐
│ MenuGenerator │
│ - Renders templates │
│ - Combines sections │
└──────┬───────────────┘
│
▼
┌──────────────┐
│ elfshoe.ipxe │
└──────────────┘
Plugin System¶
How Distribution Plugins Work¶
- Implement the Interface: Create a class inheriting from
AbstractMetadataFetcher - Register: Add to
METADATA_PROVIDERSdictionary - Configure: Reference in
config.yamlwithmetadata_provider: "name"
Example: Adding Windows Support¶
Step 1: Create src/distributions/windows.py
from .base import AbstractMetadataFetcher
class WindowsMetadataFetcher(AbstractMetadataFetcher):
def fetch_versions(self, metadata_url, **filters):
# Implement Windows-specific logic
return ['11', '10', '7']
Step 2: Register in src/distributions/__init__.py
from .windows import WindowsMetadataFetcher
METADATA_PROVIDERS = {
'fedora': FedoraMetadataFetcher,
'windows': WindowsMetadataFetcher, # Add this
}
Step 3: Use in config.yaml
distributions:
windows:
type: "dynamic"
metadata_provider: "windows"
metadata_url: "http://your-server/windows-versions.json"
# ... rest of config
Design Principles¶
Separation of Concerns¶
- Core: Reusable, distribution-agnostic
- Distributions: Specific to each OS/distro
- Templates: Presentation logic only
- Main: Orchestration and CLI
Open/Closed Principle¶
- Open for extension: Add new distributions without modifying existing code
- Closed for modification: Core components rarely change
Dependency Inversion¶
- Core depends on abstractions (
AbstractMetadataFetcher) - Specific implementations depend on same abstractions
- No dependency on concrete implementations
Configuration Over Code¶
- Everything configurable via YAML
- No Python changes needed for new static distributions
- Templates customizable without code changes
Testing Strategy¶
Unit Tests¶
test_elfshoe.py: Test data modelstest_url_validator.py: Test URL validation (mocked)test_menu_generator.py: Test menu generation
Integration Tests¶
- Run with
--no-validatefor fast testing - Run with validation for complete testing
- Mock HTTP requests to avoid network dependencies
Manual Testing¶
# Fast iteration (no network calls)
make fast
# Full validation
make validate
# Custom config
python3 -m elfshoe -c custom.yaml
Extension Points¶
Add New Distribution¶
See ADDING_DISTRIBUTIONS.md for detailed guide.
Custom Templates¶
- Copy
src/templates/to custom location - Modify templates as needed
- Use with
MenuGenerator(config, template_dir=custom_path)
Custom Validation¶
Extend URLValidator class or implement custom validator.
Additional Boot Types¶
Modify templates to add new boot types beyond chain/shell/exit.
Performance Considerations¶
URL Validation¶
- Most expensive operation (network I/O)
- Use
--no-validateduring development - Validation runs in serial (could be parallelized in future)
Metadata Fetching¶
- Cached by default (reuse connection)
- Timeout: 10 seconds
- Could add persistent caching in future
Template Rendering¶
- Negligible overhead
- Pre-compiled by Jinja2
- Happens once per invocation
Future Enhancements¶
Potential Improvements¶
- Parallel URL Validation: Use asyncio or threading for faster validation
- Metadata Caching: Cache API responses locally to reduce network calls
- Auto-Discovery: Automatically scan
distributions/directory for plugins instead of manual registry (currently requires manual registration in__init__.py) - Configuration Validation: Use JSON Schema to validate config.yaml structure
- More Providers: CentOS, Debian, Ubuntu, Windows, BSD metadata fetchers
- Multi-Architecture: ARM, aarch64 support in addition to x86_64
- CI/CD Integration: GitHub Actions workflow examples
- Web UI: Optional web interface for configuration management
Backward Compatibility¶
- Configuration format is stable
- Core API unlikely to change
- New providers don't affect existing ones
Migration Notes¶
From Old Architecture¶
The refactoring (February 2026) moved from a monolithic design to modular:
Before (monolithic file):
- All code in single
elfshoe.pyfile (401 lines) FedoraMetadataFetcherhardcoded- String check for 'fedoraproject.org'
After (modular):
- Core: Data models, validation, generation
- Distributions: Plugin system for metadata fetching
- CLI: User interface and orchestration
- Easier to test, extend, and maintain
Breaking Changes¶
- Import paths changed:
from elfshoe import BootEntry→from core import BootEntryfrom elfshoe import URLValidator→from core import URLValidator- Config requires
metadata_providerfield for dynamic distributions - Tests updated to use new imports
Non-Breaking Changes¶
- CLI interface unchanged
- Configuration format mostly unchanged (only
metadata_provideradded) - Template format unchanged
- Generated elfshoe.ipxe format unchanged