Skip to content

Commit 617b90f

Browse files
committed
feat: add embedded template functionality with on-demand creation
- Add rust-embed dependency for embedding templates at compile time - Implement TemplateManager with embedded template support - Add TemplateManagerError enum with comprehensive error handling - Support on-demand template creation from embedded resources - Add template directory cleaning functionality for development - Update e2e_tests to use TemplateManager with template cleaning - Add data/ directory to .gitignore for runtime template storage - Replace println! with tracing::debug! for proper library logging - Remove test-concrete-errors directory - Add comprehensive unit tests with 116 total tests passing
1 parent 055f75e commit 617b90f

File tree

5 files changed

+524
-13
lines changed

5 files changed

+524
-13
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ Cargo.lock
5353
# Template build directory (runtime-generated configs)
5454
build/
5555

56+
# Template data directory (runtime-generated templates)
57+
data/
58+
5659
# Meson build directory
5760
builddir/
5861

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ tokio = { version = "1.0", features = [ "full" ] }
2929
anyhow = "1.0"
3030
clap = { version = "4.0", features = [ "derive" ] }
3131
derive_more = "0.99"
32+
rust-embed = "8.0"
3233
serde = { version = "1.0", features = [ "derive" ] }
3334
serde_json = "1.0"
3435
tempfile = "3.0"
3536
tera = "1.0"
3637
thiserror = "1.0"
3738
torrust-linting = { path = "packages/linting" }
39+
tracing = "0.1"

src/bin/e2e_tests.rs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use torrust_tracker_deploy::template::file::File;
1313
use torrust_tracker_deploy::template::wrappers::ansible::inventory::{
1414
AnsibleHost, InventoryContext, InventoryTemplate, SshPrivateKeyFile,
1515
};
16+
use torrust_tracker_deploy::template::TemplateManager;
1617

1718
#[derive(Parser)]
1819
#[command(name = "e2e-tests")]
@@ -25,26 +26,43 @@ struct Cli {
2526
/// Verbose output
2627
#[arg(short, long)]
2728
verbose: bool,
29+
30+
/// Templates directory path (default: ./data/templates)
31+
#[arg(long, default_value = "./data/templates")]
32+
templates_dir: String,
2833
}
2934

3035
struct TestEnvironment {
36+
#[allow(dead_code)] // Still used for SSH key fixtures and cleanup
3137
project_root: PathBuf,
3238
#[allow(dead_code)] // Will be used in template rendering
3339
build_dir: PathBuf,
3440
keep_env: bool,
3541
verbose: bool,
3642
ssh_key_path: PathBuf,
43+
template_manager: TemplateManager,
3744
#[allow(dead_code)] // Kept to maintain temp directory lifetime
3845
temp_dir: Option<tempfile::TempDir>,
3946
#[allow(dead_code)] // Used for cleanup but not directly accessed
4047
original_inventory: Option<String>,
4148
}
4249

4350
impl TestEnvironment {
44-
fn new(keep_env: bool, verbose: bool) -> Result<Self> {
51+
fn new(keep_env: bool, verbose: bool, templates_dir: String) -> Result<Self> {
4552
// Get project root (current directory when running from root)
4653
let project_root = std::env::current_dir()?;
4754

55+
// Create template manager
56+
let template_manager = TemplateManager::new(templates_dir);
57+
58+
// Clean templates directory to ensure we use fresh templates from embedded resources
59+
if verbose {
60+
println!("🧹 Cleaning templates directory to ensure fresh embedded templates...");
61+
}
62+
template_manager.clean_templates_dir()?;
63+
64+
template_manager.ensure_templates_dir()?;
65+
4866
// Create temporary directory for SSH keys
4967
let temp_dir = TempDir::new().context("Failed to create temporary directory")?;
5068

@@ -70,6 +88,10 @@ impl TestEnvironment {
7088
temp_ssh_key.display()
7189
);
7290
println!("📁 Temporary directory: {}", temp_dir.path().display());
91+
println!(
92+
"📄 Templates directory: {}",
93+
template_manager.templates_dir().display()
94+
);
7395
}
7496

7597
Ok(Self {
@@ -78,6 +100,7 @@ impl TestEnvironment {
78100
keep_env,
79101
verbose,
80102
ssh_key_path: temp_ssh_key,
103+
template_manager,
81104
temp_dir: Some(temp_dir),
82105
original_inventory: None,
83106
})
@@ -94,17 +117,19 @@ impl TestEnvironment {
94117
.context("Failed to create build/tofu/lxd directory")?;
95118

96119
// Copy static tofu templates (no variables for now)
97-
let templates_tofu_dir = self.project_root.join("templates/tofu/lxd");
98-
99-
// Copy main.tf
100-
let source_main_tf = templates_tofu_dir.join("main.tf");
120+
// Get template paths, creating them from embedded resources if needed
121+
let source_main_tf = self
122+
.template_manager
123+
.get_template_path("tofu/lxd/main.tf")?;
101124
let dest_main_tf = build_tofu_dir.join("main.tf");
102125
tokio::fs::copy(&source_main_tf, &dest_main_tf)
103126
.await
104127
.context("Failed to copy main.tf to build directory")?;
105128

106129
// Copy cloud-init.yml
107-
let source_cloud_init = templates_tofu_dir.join("cloud-init.yml");
130+
let source_cloud_init = self
131+
.template_manager
132+
.get_template_path("tofu/lxd/cloud-init.yml")?;
108133
let dest_cloud_init = build_tofu_dir.join("cloud-init.yml");
109134
tokio::fs::copy(&source_cloud_init, &dest_cloud_init)
110135
.await
@@ -133,8 +158,8 @@ impl TestEnvironment {
133158

134159
// Render inventory.yml.tera with runtime variables
135160
let inventory_template_path = self
136-
.project_root
137-
.join("templates/ansible/inventory.yml.tera");
161+
.template_manager
162+
.get_template_path("ansible/inventory.yml.tera")?;
138163
let inventory_output_path = build_ansible_dir.join("inventory.yml");
139164

140165
let inventory_template_content = std::fs::read_to_string(&inventory_template_path)
@@ -164,10 +189,10 @@ impl TestEnvironment {
164189
.context("Failed to render inventory template")?;
165190

166191
// Copy static ansible files
167-
let templates_ansible_dir = self.project_root.join("templates/ansible");
168-
169192
// Copy ansible.cfg
170-
let source_cfg = templates_ansible_dir.join("ansible.cfg");
193+
let source_cfg = self
194+
.template_manager
195+
.get_template_path("ansible/ansible.cfg")?;
171196
let dest_cfg = build_ansible_dir.join("ansible.cfg");
172197
tokio::fs::copy(&source_cfg, &dest_cfg)
173198
.await
@@ -180,7 +205,9 @@ impl TestEnvironment {
180205
"install-docker-compose.yml",
181206
"wait-cloud-init.yml",
182207
] {
183-
let source_playbook = templates_ansible_dir.join(playbook);
208+
let source_playbook = self
209+
.template_manager
210+
.get_template_path(&format!("ansible/{playbook}"))?;
184211
let dest_playbook = build_ansible_dir.join(playbook);
185212
tokio::fs::copy(&source_playbook, &dest_playbook)
186213
.await
@@ -682,7 +709,7 @@ async fn main() -> Result<()> {
682709
println!("🚀 Torrust Tracker Deploy E2E Tests");
683710
println!("===========================================");
684711

685-
let env = TestEnvironment::new(cli.keep, cli.verbose)?;
712+
let env = TestEnvironment::new(cli.keep, cli.verbose, cli.templates_dir)?;
686713

687714
let test_start = Instant::now();
688715

0 commit comments

Comments
 (0)