Skip to content

Commit fb69b25

Browse files
committed
refactor: create foundation for instance name parameterization
- Create OpenTofu variables template with instance_name variable - Update main.tf to use var.instance_name instead of hardcoded 'torrust-vm' - Refactor OpenTofu client to accept extra_args parameter for reusability - Update infrastructure steps to pass -var-file=variables.tfvars explicitly - Fix variable naming: use instance_name instead of container_name for VMs - Maintain generic client design while supporting variables file functionality All tests pass and e2e validation successful with new parameterized approach.
1 parent e38af14 commit fb69b25

File tree

7 files changed

+120
-26
lines changed

7 files changed

+120
-26
lines changed

docs/refactors/instance-name-parameterization.md

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,90 @@
22

33
## Overview
44

5-
This refactor aims to eliminate the hardcoded "torrust-vm" instance name throughout the codebase and make it configurable from the top-level main function in `src/bin/e2e_tests.rs`.
5+
This refactor aims t## 🔄 Design Updates
66

7-
## Motivation
7+
### Variable Naming Convention
8+
9+
- **Variable name**: `instance_name` (not `container_name`) - since we're provisioning VM instances
10+
- **Scope**: Focus on instance name parameterization only
11+
- **Static variables**: Keep `image` and other variables static for now (not Tera templated)
12+
13+
## 🎯 Implementation Plan
14+
15+
### Phase 1: Foundation - OpenTofu Variables Infrastructure
16+
17+
**Goal**: Establish OpenTofu variables file pattern and integration
18+
19+
#### Step 1a: Create OpenTofu variables template ✅
20+
21+
- Create `templates/tofu/lxd/variables.tfvars` template file to define `instance_name` variable
22+
- Update `TofuTemplateRenderer` to include this file in static template copying
23+
- Keep `image` variable static (not templated)
24+
- **Status**: Static variables file created with hardcoded "torrust-vm" value
25+
- **Validation**: Unit tests + linters + e2e tests must pass
26+
27+
#### Step 1b: Update OpenTofu client for variables file
28+
29+
- Modify OpenTofu client to pass `-var-file` parameter to `tofu` commands
30+
- Update unit tests for `TofuClient`
31+
- **Validation**: All OpenTofu commands work with variables file
32+
33+
### Phase 2: Template Parameterization
34+
35+
**Goal**: Convert static variables to dynamic Tera templates
36+
37+
#### Step 2a: Convert variables.tfvars to Tera template
38+
39+
- Transform static `variables.tfvars` into `variables.tfvars.tera` template with `{{instance_name}}` placeholder
40+
- Update `TofuTemplateRenderer` to render it with context
41+
- **Validation**: Rendered file contains correct instance name value
42+
43+
#### Step 2b: Update main.tf to use variables
44+
45+
- Modify `templates/tofu/lxd/main.tf.tera` to use `var.instance_name` instead of hardcoded "torrust-vm"
46+
- Ensure proper OpenTofu variable reference syntax
47+
- **Validation**: Infrastructure deploys with custom instance names
48+
49+
### Phase 3: Context Integration
50+
51+
#### Step 3: Add instance_name to TofuContext
52+
53+
- Add `instance_name` field to `TofuContext` struct in `src/tofu/template/context.rs`
54+
- Provide default value "torrust-vm" for backward compatibility
55+
- **Validation**: Context serialization and template rendering work
56+
57+
### Phase 4: E2E Integration
58+
59+
#### Step 4: Update E2E tests infrastructure context
60+
61+
- Modify `src/e2e/tasks/provision_infrastructure.rs` to pass custom `instance_name` in `TofuContext`
62+
- Start with default value for validation
63+
- **Validation**: E2E tests pass with parameterized context
64+
65+
#### Step 5: Add parameterization to e2e_tests.rs main
66+
67+
- Modify `src/bin/e2e_tests.rs` main function to accept instance name parameter
68+
- Pass it through to E2E tasks
69+
- Implement CLI argument parsing
70+
- **Validation**: CLI accepts custom instance names
71+
72+
### Phase 5: Complete Migration
73+
74+
#### Step 6: Update remaining hardcoded references
75+
76+
- Replace remaining 54 hardcoded "torrust-vm" occurrences across codebase with parameterized values
77+
- Update tests and documentation
78+
- **Validation**: All hardcoded references eliminated
79+
80+
#### Final validation and documentation
81+
82+
- Run comprehensive tests
83+
- Update documentation to reflect parameterization capabilities
84+
- **Validation**: All hardcoded references are eliminated
85+
86+
## 🔍 Analysis of Current "torrust-vm" Usage
87+
88+
This refactor addresses a comprehensive parameterization of the h
889

990
Currently, the instance name "torrust-vm" is hardcoded in multiple places across the codebase, including:
1091

src/command_wrappers/opentofu/client.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ impl OpenTofuClient {
124124

125125
/// Plan infrastructure changes
126126
///
127+
/// # Arguments
128+
///
129+
/// * `extra_args` - Additional arguments to pass to the tofu plan command (e.g., "-var-file=variables.tfvars")
130+
///
127131
/// # Returns
128132
///
129133
/// * `Ok(String)` - The stdout output if the command succeeds
@@ -134,21 +138,25 @@ impl OpenTofuClient {
134138
/// This function will return an error if:
135139
/// * The `OpenTofu` plan fails
136140
/// * The configuration is not initialized
137-
pub fn plan(&self) -> Result<String, CommandError> {
141+
pub fn plan(&self, extra_args: &[&str]) -> Result<String, CommandError> {
138142
info!(
139143
"Planning infrastructure changes in directory: {}",
140144
self.working_dir.display()
141145
);
142146

147+
let mut args = vec!["plan"];
148+
args.extend_from_slice(extra_args);
149+
143150
self.command_executor
144-
.run_command("tofu", &["plan"], Some(&self.working_dir))
151+
.run_command("tofu", &args, Some(&self.working_dir))
145152
}
146153

147154
/// Apply infrastructure changes
148155
///
149156
/// # Arguments
150157
///
151158
/// * `auto_approve` - Whether to automatically approve the changes without interactive confirmation
159+
/// * `extra_args` - Additional arguments to pass to the tofu apply command (e.g., "-var-file=variables.tfvars")
152160
///
153161
/// # Returns
154162
///
@@ -160,13 +168,14 @@ impl OpenTofuClient {
160168
/// This function will return an error if:
161169
/// * The `OpenTofu` apply fails
162170
/// * The configuration is not initialized
163-
pub fn apply(&self, auto_approve: bool) -> Result<String, CommandError> {
171+
pub fn apply(&self, auto_approve: bool, extra_args: &[&str]) -> Result<String, CommandError> {
164172
info!(
165173
"Applying infrastructure changes in directory: {}",
166174
self.working_dir.display()
167175
);
168176

169177
let mut args = vec!["apply"];
178+
args.extend_from_slice(extra_args);
170179
if auto_approve {
171180
args.push("-auto-approve");
172181
}
@@ -180,6 +189,7 @@ impl OpenTofuClient {
180189
/// # Arguments
181190
///
182191
/// * `auto_approve` - Whether to automatically approve the destruction without interactive confirmation
192+
/// * `extra_args` - Additional arguments to pass to the tofu destroy command (e.g., "-var-file=variables.tfvars")
183193
///
184194
/// # Returns
185195
///
@@ -191,13 +201,14 @@ impl OpenTofuClient {
191201
/// This function will return an error if:
192202
/// * The `OpenTofu` destroy fails
193203
/// * The configuration is not initialized
194-
pub fn destroy(&self, auto_approve: bool) -> Result<String, CommandError> {
204+
pub fn destroy(&self, auto_approve: bool, extra_args: &[&str]) -> Result<String, CommandError> {
195205
info!(
196206
"Destroying infrastructure in directory: {}",
197207
self.working_dir.display()
198208
);
199209

200210
let mut args = vec!["destroy"];
211+
args.extend_from_slice(extra_args);
201212
if auto_approve {
202213
args.push("-auto-approve");
203214
}
@@ -336,7 +347,7 @@ resource "null_resource" "test" {
336347
drop(client.init());
337348

338349
// This would fail if OpenTofu is not installed, so we ignore it by default
339-
match client.plan() {
350+
match client.plan(&[]) {
340351
Ok(_output) => {
341352
// Plan succeeded
342353
}

src/e2e/tasks/cleanup_infrastructure.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ pub fn cleanup_infrastructure(env: &TestEnvironment) {
5353

5454
info!(operation = "cleanup", "Cleaning up test environment");
5555

56-
// Destroy infrastructure using OpenTofuClient
56+
// Destroy infrastructure using OpenTofuClient with variables file
5757
let result = env
5858
.services
5959
.opentofu_client
60-
.destroy(true) // auto_approve = true
60+
.destroy(true, &["-var-file=variables.tfvars"]) // auto_approve = true
6161
.map_err(anyhow::Error::from);
6262

6363
match result {

src/steps/infrastructure/apply.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ impl ApplyInfrastructureStep {
7373
"Applying OpenTofu infrastructure"
7474
);
7575

76-
// Execute tofu apply command
77-
let output = self.opentofu_client.apply(self.auto_approve)?;
76+
// Execute tofu apply command with variables file
77+
let output = self
78+
.opentofu_client
79+
.apply(self.auto_approve, &["-var-file=variables.tfvars"])?;
7880

7981
info!(
8082
step = "apply_infrastructure",

src/steps/infrastructure/plan.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ impl PlanInfrastructureStep {
5858
"Planning OpenTofu infrastructure"
5959
);
6060

61-
// Execute tofu plan command
62-
let output = self.opentofu_client.plan()?;
61+
// Execute tofu plan command with variables file
62+
let output = self.opentofu_client.plan(&["-var-file=variables.tfvars"])?;
6363

6464
info!(
6565
step = "plan_infrastructure",

src/tofu/template/renderer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl TofuTemplateRenderer {
154154
let build_tofu_dir = self.create_build_directory().await?;
155155

156156
// List of static templates to copy directly
157-
let static_template_files = vec!["main.tf"];
157+
let static_template_files = vec!["main.tf", "variables.tfvars"];
158158

159159
// Copy static template files
160160
self.copy_templates(&static_template_files, &build_tofu_dir)

templates/tofu/lxd/main.tf

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ provider "lxd" {
1414
}
1515

1616
# Variables
17-
variable "container_name" {
18-
description = "Name of the LXD container"
17+
variable "instance_name" {
18+
description = "Name of the LXD instance"
1919
type = string
2020
default = "torrust-vm"
2121
}
@@ -58,7 +58,7 @@ resource "lxd_profile" "torrust_profile" {
5858

5959
# Create the LXD virtual machine
6060
resource "lxd_instance" "torrust_vm" {
61-
name = var.container_name
61+
name = var.instance_name
6262
image = var.image
6363
type = "virtual-machine"
6464
profiles = [lxd_profile.torrust_profile.name]
@@ -90,20 +90,20 @@ output "instance_info" {
9090
output "connection_commands" {
9191
description = "Commands to connect to the container"
9292
value = [
93-
"lxc exec ${var.container_name} -- /bin/bash",
94-
"lxc exec ${var.container_name} -- whoami",
95-
"lxc exec ${var.container_name} -- systemctl status",
96-
"lxc list ${var.container_name}"
93+
"lxc exec ${var.instance_name} -- /bin/bash",
94+
"lxc exec ${var.instance_name} -- whoami",
95+
"lxc exec ${var.instance_name} -- systemctl status",
96+
"lxc list ${var.instance_name}"
9797
]
9898
}
9999

100100
output "test_commands" {
101101
description = "Commands to test the container functionality"
102102
value = [
103-
"lxc exec ${var.container_name} -- cat /etc/os-release",
104-
"lxc exec ${var.container_name} -- df -h",
105-
"lxc exec ${var.container_name} -- free -h",
106-
"lxc exec ${var.container_name} -- systemctl list-units --type=service --state=running",
107-
"lxc exec ${var.container_name} -- cloud-init status"
103+
"lxc exec ${var.instance_name} -- cat /etc/os-release",
104+
"lxc exec ${var.instance_name} -- df -h",
105+
"lxc exec ${var.instance_name} -- free -h",
106+
"lxc exec ${var.instance_name} -- systemctl list-units --type=service --state=running",
107+
"lxc exec ${var.instance_name} -- cloud-init status"
108108
]
109109
}

0 commit comments

Comments
 (0)