The Docker-based Open edX distribution designed for peace of mind
Template rendering system using Jinja2 for generating Docker Compose configurations, Kubernetes manifests, and application settings. The environment system handles template processing, file generation, and configuration deployment across different execution contexts.
Process Jinja2 templates with configuration data and custom filters to generate deployment files.
def render_file(config: Config, *path: str) -> Union[str, bytes]:
"""
Render a template file with configuration data.
Args:
config (Config): Configuration dictionary for template variables
*path (str): Path components to the template file
Returns:
Union[str, bytes]: Rendered template content (bytes for binary files)
"""
def render_str(config: Config, text: str) -> str:
"""
Render a template string with configuration data.
Args:
config (Config): Configuration dictionary for template variables
text (str): Template string to render
Returns:
str: Rendered string content
"""
def render_unknown(config: Config, value: Any) -> Any:
"""
Render unknown value type, handling strings as templates and passing through other types.
Args:
config (Config): Configuration dictionary for template variables
value (Any): Value to potentially render
Returns:
Any: Rendered value or original value if not a string
"""Manage environment directories and file operations. File saving is handled internally by the environment rendering system.
def copy_file(root: str, filename: str) -> None:
"""
Copy a file from templates to environment directory.
Args:
root (str): Project root directory
filename (str): Filename to copy
"""
def delete_file(root: str, filename: str) -> None:
"""
Delete a file from the environment directory.
Args:
root (str): Project root directory
filename (str): Filename to delete
"""Manage the environment directory structure and lifecycle.
def create_env_dir(root: str) -> str:
"""
Create and return the environment directory path.
Args:
root (str): Project root directory
Returns:
str: Path to the environment directory
"""
def clean_env_dir(root: str) -> None:
"""
Clean the environment directory, removing all generated files.
Args:
root (str): Project root directory
"""
def get_env_path(root: str, *path: str) -> str:
"""
Get the full path to a file in the environment directory.
Args:
root (str): Project root directory
*path (str): Path components within environment directory
Returns:
str: Full path to the file
"""Configure the Jinja2 template environment with custom filters and settings.
def create_template_environment(config: Config) -> jinja2.Environment:
"""
Create a Jinja2 environment with Tutor-specific settings and filters.
Args:
config (Config): Configuration dictionary for template context
Returns:
jinja2.Environment: Configured Jinja2 environment
"""
def get_template_filters() -> Dict[str, Callable]:
"""
Get dictionary of custom Jinja2 filters available in templates.
Returns:
Dict[str, Callable]: Dictionary mapping filter names to functions
"""Apply template patches to customize generated configurations.
def apply_patches(config: Config, content: str, patches: List[str]) -> str:
"""
Apply a list of patches to template content.
Args:
config (Config): Configuration dictionary for patch rendering
content (str): Original content to patch
patches (List[str]): List of patch identifiers
Returns:
str: Content with patches applied
"""
def get_patch_content(config: Config, patch_name: str) -> str:
"""
Get rendered content for a specific patch.
Args:
config (Config): Configuration dictionary for patch rendering
patch_name (str): Name of the patch to retrieve
Returns:
str: Rendered patch content
"""Variables automatically available in all templates.
# Configuration access
config: Config # Complete configuration dictionary
get_config: Callable[[str, Any], Any] # Get config value with default
# Environment information
TUTOR_VERSION: str # Current Tutor version
TUTOR_APP: str # Application name (usually "tutor")
# Docker and deployment
DOCKER_REGISTRY: str # Docker registry for images
DOCKER_IMAGE_TAG: str # Tag for Docker images
# Networking
HTTP_PORT: int # HTTP port number
HTTPS_PORT: int # HTTPS port number
# Paths
TUTOR_ROOT: str # Project root directory
ENV_ROOT: str # Environment directory pathCustom Jinja2 filters available in templates.
# String processing filters
def common_domain(d1: str, d2: str) -> str:
"""Find common domain between two domain names."""
def reverse_host(domain: str) -> str:
"""Reverse domain name Java-style (com.example.subdomain)."""
def random_string(length: int) -> str:
"""Generate random string of specified length."""
# Configuration filters
def list_if(services: List[Tuple[str, bool]]) -> str:
"""Generate JSON list of enabled services."""
def walk_templates(obj: Any) -> Any:
"""Recursively render templates in nested data structures."""
# Docker filters
def docker_image(service: str) -> str:
"""Get Docker image name for a service."""
def docker_registry_image(image: str) -> str:
"""Add registry prefix to image name if configured."""# Template root directories (configurable via ENV_TEMPLATE_ROOTS filter)
TEMPLATES_ROOT: str # Main templates directory
PLUGIN_TEMPLATES: List[str] # Plugin template directories
# Standard template files
"docker-compose.yml" # Docker Compose configuration
"docker-compose.prod.yml" # Production Docker Compose overrides
"docker-compose.dev.yml" # Development Docker Compose overrides
"k8s/" # Kubernetes manifests directory
"local/" # Local deployment files
"dev/" # Development configuration filesFiles generated in the environment directory.
# Standard targets (configurable via ENV_TEMPLATE_TARGETS filter)
("docker-compose.yml", "docker-compose.yml") # Main compose file
("docker-compose.prod.yml", "docker-compose.prod.yml") # Production overrides
("docker-compose.dev.yml", "docker-compose.dev.yml") # Development overrides
("k8s/", "k8s/") # Kubernetes manifests
("local/", "local/") # Local deployment files
("dev/", "dev/") # Development filesfrom tutor import env, config
# Load configuration
config_data = config.load("/path/to/tutor/root")
# Render a template file
content = env.render_file(config_data, "docker-compose.yml")
# Content is rendered for use by the deployment system
print(f"Rendered content length: {len(content)} chars")from tutor import env, config
config_data = config.load("/path/to/tutor/root")
# Render template string
template_str = "My platform: {{ PLATFORM_NAME }}"
result = env.render_str(config_data, template_str)
# Process nested templates
data = {
"name": "{{ PLATFORM_NAME }}",
"host": "{{ LMS_HOST }}",
"nested": {
"port": "{{ HTTP_PORT }}"
}
}
processed = env.render_unknown(config_data, data)from tutor import env
root = "/path/to/tutor/root"
# Create environment directory
env_dir = env.create_env_dir(root)
# Get path to environment file
compose_path = env.get_env_path(root, "docker-compose.yml")
# Clean environment
env.clean_env_dir(root)from tutor import env, config, hooks
config_data = config.load("/path/to/tutor/root")
# Add custom patches via hooks
hooks.Filters.ENV_PATCHES.add_items([
("docker-compose.yml", "my-custom-patch"),
("k8s/ingress.yml", "ingress-customization"),
])
# Render template with patches applied
content = env.render_file(config_data, "docker-compose.yml")
# Patches are automatically applied during renderingfrom tutor import hooks
# Add custom Jinja2 filter
@hooks.Filters.ENV_TEMPLATE_FILTERS.add()
def add_custom_filters():
def uppercase_filter(text):
return text.upper()
def format_url(host, protocol="https"):
return f"{protocol}://{host}"
return [
("uppercase", uppercase_filter),
("format_url", format_url),
]
# Use in templates:
# {{ PLATFORM_NAME | uppercase }}
# {{ LMS_HOST | format_url("http") }}Install with Tessl CLI
npx tessl i tessl/pypi-tutor