0
# Environment Management
1
2
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.
3
4
## Capabilities
5
6
### Template Rendering
7
8
Process Jinja2 templates with configuration data and custom filters to generate deployment files.
9
10
```python { .api }
11
def render_file(config: Config, *path: str) -> Union[str, bytes]:
12
"""
13
Render a template file with configuration data.
14
15
Args:
16
config (Config): Configuration dictionary for template variables
17
*path (str): Path components to the template file
18
19
Returns:
20
Union[str, bytes]: Rendered template content (bytes for binary files)
21
"""
22
23
def render_str(config: Config, text: str) -> str:
24
"""
25
Render a template string with configuration data.
26
27
Args:
28
config (Config): Configuration dictionary for template variables
29
text (str): Template string to render
30
31
Returns:
32
str: Rendered string content
33
"""
34
35
def render_unknown(config: Config, value: Any) -> Any:
36
"""
37
Render unknown value type, handling strings as templates and passing through other types.
38
39
Args:
40
config (Config): Configuration dictionary for template variables
41
value (Any): Value to potentially render
42
43
Returns:
44
Any: Rendered value or original value if not a string
45
"""
46
```
47
48
### File Operations
49
50
Manage environment directories and file operations. File saving is handled internally by the environment rendering system.
51
52
```python { .api }
53
def copy_file(root: str, filename: str) -> None:
54
"""
55
Copy a file from templates to environment directory.
56
57
Args:
58
root (str): Project root directory
59
filename (str): Filename to copy
60
"""
61
62
def delete_file(root: str, filename: str) -> None:
63
"""
64
Delete a file from the environment directory.
65
66
Args:
67
root (str): Project root directory
68
filename (str): Filename to delete
69
"""
70
```
71
72
### Environment Directory Management
73
74
Manage the environment directory structure and lifecycle.
75
76
```python { .api }
77
def create_env_dir(root: str) -> str:
78
"""
79
Create and return the environment directory path.
80
81
Args:
82
root (str): Project root directory
83
84
Returns:
85
str: Path to the environment directory
86
"""
87
88
def clean_env_dir(root: str) -> None:
89
"""
90
Clean the environment directory, removing all generated files.
91
92
Args:
93
root (str): Project root directory
94
"""
95
96
def get_env_path(root: str, *path: str) -> str:
97
"""
98
Get the full path to a file in the environment directory.
99
100
Args:
101
root (str): Project root directory
102
*path (str): Path components within environment directory
103
104
Returns:
105
str: Full path to the file
106
"""
107
```
108
109
### Template System Configuration
110
111
Configure the Jinja2 template environment with custom filters and settings.
112
113
```python { .api }
114
def create_template_environment(config: Config) -> jinja2.Environment:
115
"""
116
Create a Jinja2 environment with Tutor-specific settings and filters.
117
118
Args:
119
config (Config): Configuration dictionary for template context
120
121
Returns:
122
jinja2.Environment: Configured Jinja2 environment
123
"""
124
125
def get_template_filters() -> Dict[str, Callable]:
126
"""
127
Get dictionary of custom Jinja2 filters available in templates.
128
129
Returns:
130
Dict[str, Callable]: Dictionary mapping filter names to functions
131
"""
132
```
133
134
### Patch System
135
136
Apply template patches to customize generated configurations.
137
138
```python { .api }
139
def apply_patches(config: Config, content: str, patches: List[str]) -> str:
140
"""
141
Apply a list of patches to template content.
142
143
Args:
144
config (Config): Configuration dictionary for patch rendering
145
content (str): Original content to patch
146
patches (List[str]): List of patch identifiers
147
148
Returns:
149
str: Content with patches applied
150
"""
151
152
def get_patch_content(config: Config, patch_name: str) -> str:
153
"""
154
Get rendered content for a specific patch.
155
156
Args:
157
config (Config): Configuration dictionary for patch rendering
158
patch_name (str): Name of the patch to retrieve
159
160
Returns:
161
str: Rendered patch content
162
"""
163
```
164
165
## Template Variables
166
167
### Built-in Template Variables
168
169
Variables automatically available in all templates.
170
171
```python { .api }
172
# Configuration access
173
config: Config # Complete configuration dictionary
174
get_config: Callable[[str, Any], Any] # Get config value with default
175
176
# Environment information
177
TUTOR_VERSION: str # Current Tutor version
178
TUTOR_APP: str # Application name (usually "tutor")
179
180
# Docker and deployment
181
DOCKER_REGISTRY: str # Docker registry for images
182
DOCKER_IMAGE_TAG: str # Tag for Docker images
183
184
# Networking
185
HTTP_PORT: int # HTTP port number
186
HTTPS_PORT: int # HTTPS port number
187
188
# Paths
189
TUTOR_ROOT: str # Project root directory
190
ENV_ROOT: str # Environment directory path
191
```
192
193
### Custom Template Filters
194
195
Custom Jinja2 filters available in templates.
196
197
```python { .api }
198
# String processing filters
199
def common_domain(d1: str, d2: str) -> str:
200
"""Find common domain between two domain names."""
201
202
def reverse_host(domain: str) -> str:
203
"""Reverse domain name Java-style (com.example.subdomain)."""
204
205
def random_string(length: int) -> str:
206
"""Generate random string of specified length."""
207
208
# Configuration filters
209
def list_if(services: List[Tuple[str, bool]]) -> str:
210
"""Generate JSON list of enabled services."""
211
212
def walk_templates(obj: Any) -> Any:
213
"""Recursively render templates in nested data structures."""
214
215
# Docker filters
216
def docker_image(service: str) -> str:
217
"""Get Docker image name for a service."""
218
219
def docker_registry_image(image: str) -> str:
220
"""Add registry prefix to image name if configured."""
221
```
222
223
## Template Organization
224
225
### Template Directory Structure
226
227
```python { .api }
228
# Template root directories (configurable via ENV_TEMPLATE_ROOTS filter)
229
TEMPLATES_ROOT: str # Main templates directory
230
PLUGIN_TEMPLATES: List[str] # Plugin template directories
231
232
# Standard template files
233
"docker-compose.yml" # Docker Compose configuration
234
"docker-compose.prod.yml" # Production Docker Compose overrides
235
"docker-compose.dev.yml" # Development Docker Compose overrides
236
"k8s/" # Kubernetes manifests directory
237
"local/" # Local deployment files
238
"dev/" # Development configuration files
239
```
240
241
### Template Targets
242
243
Files generated in the environment directory.
244
245
```python { .api }
246
# Standard targets (configurable via ENV_TEMPLATE_TARGETS filter)
247
("docker-compose.yml", "docker-compose.yml") # Main compose file
248
("docker-compose.prod.yml", "docker-compose.prod.yml") # Production overrides
249
("docker-compose.dev.yml", "docker-compose.dev.yml") # Development overrides
250
("k8s/", "k8s/") # Kubernetes manifests
251
("local/", "local/") # Local deployment files
252
("dev/", "dev/") # Development files
253
```
254
255
## Usage Examples
256
257
### Basic Template Rendering
258
259
```python
260
from tutor import env, config
261
262
# Load configuration
263
config_data = config.load("/path/to/tutor/root")
264
265
# Render a template file
266
content = env.render_file(config_data, "docker-compose.yml")
267
268
# Content is rendered for use by the deployment system
269
print(f"Rendered content length: {len(content)} chars")
270
```
271
272
### Custom Template Processing
273
274
```python
275
from tutor import env, config
276
277
config_data = config.load("/path/to/tutor/root")
278
279
# Render template string
280
template_str = "My platform: {{ PLATFORM_NAME }}"
281
result = env.render_str(config_data, template_str)
282
283
# Process nested templates
284
data = {
285
"name": "{{ PLATFORM_NAME }}",
286
"host": "{{ LMS_HOST }}",
287
"nested": {
288
"port": "{{ HTTP_PORT }}"
289
}
290
}
291
processed = env.render_unknown(config_data, data)
292
```
293
294
### Environment Directory Management
295
296
```python
297
from tutor import env
298
299
root = "/path/to/tutor/root"
300
301
# Create environment directory
302
env_dir = env.create_env_dir(root)
303
304
# Get path to environment file
305
compose_path = env.get_env_path(root, "docker-compose.yml")
306
307
# Clean environment
308
env.clean_env_dir(root)
309
```
310
311
### Working with Patches
312
313
```python
314
from tutor import env, config, hooks
315
316
config_data = config.load("/path/to/tutor/root")
317
318
# Add custom patches via hooks
319
hooks.Filters.ENV_PATCHES.add_items([
320
("docker-compose.yml", "my-custom-patch"),
321
("k8s/ingress.yml", "ingress-customization"),
322
])
323
324
# Render template with patches applied
325
content = env.render_file(config_data, "docker-compose.yml")
326
# Patches are automatically applied during rendering
327
```
328
329
### Custom Template Filters
330
331
```python
332
from tutor import hooks
333
334
# Add custom Jinja2 filter
335
@hooks.Filters.ENV_TEMPLATE_FILTERS.add()
336
def add_custom_filters():
337
def uppercase_filter(text):
338
return text.upper()
339
340
def format_url(host, protocol="https"):
341
return f"{protocol}://{host}"
342
343
return [
344
("uppercase", uppercase_filter),
345
("format_url", format_url),
346
]
347
348
# Use in templates:
349
# {{ PLATFORM_NAME | uppercase }}
350
# {{ LMS_HOST | format_url("http") }}
351
```