|
16 | 16 | //! Run with custom options: |
17 | 17 | //! |
18 | 18 | //! ```bash |
19 | | -//! # Keep test environment after completion |
20 | | -//! cargo run --bin e2e-config-tests -- --keep |
21 | | -//! |
22 | 19 | //! # Use custom templates directory |
23 | 20 | //! cargo run --bin e2e-config-tests -- --templates-dir ./custom/templates |
24 | 21 | //! |
|
49 | 46 |
|
50 | 47 | use anyhow::{Context, Result}; |
51 | 48 | use clap::Parser; |
52 | | -use std::net::SocketAddr; |
53 | | -use std::sync::Arc; |
54 | 49 | use std::time::Instant; |
55 | 50 | use tracing::{error, info}; |
56 | 51 |
|
57 | | -use torrust_tracker_deploy::application::commands::ConfigureCommand; |
58 | | -use torrust_tracker_deploy::config::{InstanceName, SshCredentials}; |
59 | | -use torrust_tracker_deploy::container::Services; |
60 | | -use torrust_tracker_deploy::e2e::containers::actions::{SshKeySetupAction, SshWaitAction}; |
61 | | -use torrust_tracker_deploy::e2e::containers::timeout::ContainerTimeouts; |
62 | | -use torrust_tracker_deploy::e2e::containers::{ |
63 | | - RunningProvisionedContainer, StoppedProvisionedContainer, |
| 52 | +use torrust_tracker_deploy::config::InstanceName; |
| 53 | +use torrust_tracker_deploy::e2e::environment::{TestEnvironment, TestEnvironmentType}; |
| 54 | +use torrust_tracker_deploy::e2e::tasks::{ |
| 55 | + container::{ |
| 56 | + cleanup_docker_container::cleanup_docker_container, |
| 57 | + configure_ssh_connectivity::configure_ssh_connectivity, |
| 58 | + run_provision_simulation::run_provision_simulation, |
| 59 | + setup_docker_container::setup_docker_container, |
| 60 | + }, |
| 61 | + create_test_ssh_credentials::create_test_ssh_credentials, |
| 62 | + preflight_cleanup, |
| 63 | + run_ansible_configuration::run_ansible_configuration, |
| 64 | + run_deployment_validation::run_deployment_validation, |
64 | 65 | }; |
65 | | -use torrust_tracker_deploy::e2e::environment::TestEnvironment; |
66 | | -use torrust_tracker_deploy::e2e::tasks::container::provision_docker_infrastructure::provision_docker_infrastructure; |
67 | | -use torrust_tracker_deploy::e2e::tasks::preflight_cleanup; |
68 | 66 | use torrust_tracker_deploy::logging::{self, LogFormat}; |
69 | 67 |
|
70 | 68 | #[derive(Parser)] |
71 | 69 | #[command(name = "e2e-config-tests")] |
72 | 70 | #[command(about = "E2E configuration tests for Torrust Tracker Deploy using Docker containers")] |
73 | 71 | struct CliArgs { |
74 | | - /// Keep the test environment after completion |
75 | | - #[arg(long)] |
76 | | - keep: bool, |
77 | | - |
78 | 72 | /// Templates directory path (default: ./data/templates) |
79 | 73 | #[arg(long, default_value = "./data/templates")] |
80 | 74 | templates_dir: String, |
@@ -126,7 +120,7 @@ pub async fn main() -> Result<()> { |
126 | 120 | InstanceName::new("torrust-tracker-vm".to_string()).expect("Valid hardcoded instance name"); |
127 | 121 |
|
128 | 122 | // Setup test environment with preflight cleanup |
129 | | - let test_env = setup_test_environment(cli.templates_dir, instance_name)?; |
| 123 | + let test_env = setup_test_environment(false, cli.templates_dir, instance_name)?; |
130 | 124 |
|
131 | 125 | let test_result = run_configuration_tests(&test_env).await; |
132 | 126 |
|
@@ -163,262 +157,51 @@ pub async fn main() -> Result<()> { |
163 | 157 |
|
164 | 158 | /// Setup test environment with preflight cleanup |
165 | 159 | fn setup_test_environment( |
| 160 | + keep_env: bool, |
166 | 161 | templates_dir: String, |
167 | 162 | instance_name: InstanceName, |
168 | 163 | ) -> Result<TestEnvironment> { |
169 | 164 | info!("Running preflight cleanup for Docker-based E2E tests"); |
170 | | - let test_env = |
171 | | - TestEnvironment::with_ssh_user_and_init(false, templates_dir, "torrust", instance_name) |
172 | | - .context("Failed to create test environment")?; |
| 165 | + let test_env = TestEnvironment::with_ssh_user_and_init( |
| 166 | + keep_env, |
| 167 | + templates_dir, |
| 168 | + "torrust", |
| 169 | + instance_name, |
| 170 | + TestEnvironmentType::Container, |
| 171 | + ) |
| 172 | + .context("Failed to create test environment")?; |
173 | 173 |
|
174 | 174 | preflight_cleanup::cleanup_lingering_resources_docker(&test_env) |
175 | 175 | .context("Failed to complete preflight cleanup")?; |
176 | 176 |
|
177 | 177 | Ok(test_env) |
178 | 178 | } |
179 | 179 |
|
180 | | -/// Setup Docker container and start it |
181 | | -async fn setup_docker_container() -> Result<RunningProvisionedContainer> { |
182 | | - info!("Setting up Docker container"); |
183 | | - let stopped_container = StoppedProvisionedContainer::default(); |
184 | | - let running_container = stopped_container |
185 | | - .start() |
186 | | - .await |
187 | | - .context("Failed to start provisioned instance container")?; |
188 | | - |
189 | | - Ok(running_container) |
190 | | -} |
191 | | - |
192 | | -/// Configure SSH connectivity to the running container |
193 | | -async fn configure_ssh_connectivity( |
194 | | - container: &RunningProvisionedContainer, |
195 | | - test_env: &TestEnvironment, |
196 | | -) -> Result<()> { |
197 | | - let socket_addr = container.ssh_socket_addr(); |
198 | | - let timeouts = ContainerTimeouts::default(); |
199 | | - let ssh_wait_action = SshWaitAction::new(timeouts.ssh_ready, 10); |
200 | | - ssh_wait_action |
201 | | - .execute(socket_addr) |
202 | | - .context("SSH server failed to start")?; |
203 | | - |
204 | | - // Get SSH credentials from test environment and setup keys |
205 | | - let ssh_credentials = &test_env.config.ssh_credentials; |
206 | | - let ssh_key_setup_action = SshKeySetupAction::new(); |
207 | | - ssh_key_setup_action |
208 | | - .execute(container, ssh_credentials) |
209 | | - .await |
210 | | - .context("Failed to setup SSH authentication")?; |
211 | | - |
212 | | - info!( |
213 | | - socket_addr = %socket_addr, |
214 | | - ssh_user = %ssh_credentials.ssh_username, |
215 | | - container_id = %container.container_id(), |
216 | | - "Container ready for Ansible configuration" |
217 | | - ); |
218 | | - |
219 | | - Ok(()) |
220 | | -} |
221 | | - |
222 | | -/// Run the complete configuration tests |
| 180 | +/// Run the complete configuration tests using extracted tasks |
223 | 181 | async fn run_configuration_tests(test_env: &TestEnvironment) -> Result<()> { |
224 | 182 | info!("Starting configuration tests with Docker container"); |
225 | 183 |
|
226 | | - // Step 1: Setup Docker container - start with stopped state |
| 184 | + // Step 1: Setup Docker container |
227 | 185 | let running_container = setup_docker_container().await?; |
| 186 | + let socket_addr = running_container.ssh_socket_addr(); |
228 | 187 |
|
229 | | - // Step 2: Wait for SSH server and setup connectivity (only available when running) |
230 | | - configure_ssh_connectivity(&running_container, test_env).await?; |
231 | | - |
232 | | - // Step 2.5: Run provision simulation to render Ansible templates |
233 | | - info!("Running provision simulation to prepare container configuration"); |
234 | | - run_provision_simulation(running_container.ssh_socket_addr(), test_env).await?; |
235 | | - |
236 | | - // Step 3: Run configuration tasks (Ansible playbooks) |
237 | | - info!("Running Ansible configuration tasks"); |
238 | | - run_ansible_configuration(running_container.ssh_socket_addr(), test_env)?; |
239 | | - |
240 | | - // Step 4: Validate deployment |
241 | | - info!("Validating service deployment"); |
242 | | - run_deployment_validation(running_container.ssh_socket_addr(), test_env).await?; |
243 | | - |
244 | | - // Step 5: Cleanup - transition back to stopped state |
245 | | - cleanup_container(running_container); |
246 | | - |
247 | | - info!("Configuration tests completed successfully"); |
248 | | - |
249 | | - Ok(()) |
250 | | -} |
251 | | - |
252 | | -/// Cleanup container by stopping it |
253 | | -fn cleanup_container(running_container: RunningProvisionedContainer) { |
254 | | - info!("Cleaning up container"); |
255 | | - let _stopped_container = running_container.stop(); |
256 | | - info!("Container stopped successfully"); |
257 | | -} |
258 | | - |
259 | | -/// Run provision simulation to prepare templates for container configuration |
260 | | -async fn run_provision_simulation( |
261 | | - ssh_service_socket_addr: SocketAddr, |
262 | | - test_env: &TestEnvironment, |
263 | | -) -> Result<()> { |
264 | | - info!( |
265 | | - socket_addr = %ssh_service_socket_addr, |
266 | | - "Running provision simulation for container" |
267 | | - ); |
268 | | - |
269 | | - // Create SSH credentials and use configuration from test environment |
270 | | - let ssh_credentials = |
271 | | - create_container_ssh_credentials(&test_env.config.ssh_credentials.ssh_username)?; |
272 | | - let services = Services::new(&test_env.config); |
273 | | - |
274 | | - // Run the Docker infrastructure provision simulation |
275 | | - provision_docker_infrastructure( |
276 | | - Arc::clone(&services.ansible_template_renderer), |
277 | | - ssh_credentials, |
278 | | - ssh_service_socket_addr, |
279 | | - ) |
280 | | - .await |
281 | | - .context("Failed to complete Docker infrastructure provision simulation")?; |
282 | | - |
283 | | - info!( |
284 | | - status = "complete", |
285 | | - "Provision simulation completed - Ansible templates rendered with container details" |
286 | | - ); |
287 | | - |
288 | | - Ok(()) |
289 | | -} |
290 | | - |
291 | | -/// Run Ansible configuration tasks on the container |
292 | | -fn run_ansible_configuration( |
293 | | - ssh_service_socket_addr: SocketAddr, |
294 | | - test_env: &TestEnvironment, |
295 | | -) -> Result<()> { |
296 | | - info!( |
297 | | - socket_addr = %ssh_service_socket_addr, |
298 | | - "Running Ansible configuration on container" |
299 | | - ); |
300 | | - |
301 | | - // NOTE: This demonstrates the configuration workflow structure, but currently |
302 | | - // the ConfigureCommand uses LXD-based inventory that tries to connect to |
303 | | - // 10.140.190.171 instead of 127.0.0.1:mapped_port for containers. |
304 | | - // |
305 | | - // To fully implement container-based configuration, we need to: |
306 | | - // 1. Create container-specific Ansible inventory templates |
307 | | - // 2. Modify Config/Services to support container-specific templates |
308 | | - // 3. Update template rendering to use container host/port |
309 | | - // |
310 | | - // For now, we'll catch the expected connection error and log it: |
311 | | - |
312 | | - let services = Services::new(&test_env.config); |
313 | | - let configure_command = ConfigureCommand::new(Arc::clone(&services.ansible_client)); |
314 | | - |
315 | | - match configure_command.execute().map_err(anyhow::Error::from) { |
316 | | - Ok(()) => { |
317 | | - info!( |
318 | | - status = "complete", |
319 | | - "Container configuration completed successfully" |
320 | | - ); |
321 | | - } |
322 | | - Err(e) => { |
323 | | - // Expected failure due to inventory mismatch - log and return error |
324 | | - info!( |
325 | | - status = "expected_failure", |
326 | | - error = %e, |
327 | | - note = "ConfigureCommand failed as expected - needs container-specific inventory" |
328 | | - ); |
329 | | - return Err( |
330 | | - e.context("Configuration failed (expected - needs container-specific inventory)") |
331 | | - ); |
332 | | - } |
333 | | - } |
334 | | - |
335 | | - info!( |
336 | | - status = "structural_complete", |
337 | | - "Configuration workflow structure implemented" |
338 | | - ); |
339 | | - |
340 | | - Ok(()) |
341 | | -} |
342 | | - |
343 | | -/// Run deployment validation tests on the container |
344 | | -async fn run_deployment_validation( |
345 | | - ssh_service_socket_addr: SocketAddr, |
346 | | - test_env: &TestEnvironment, |
347 | | -) -> Result<()> { |
348 | | - info!( |
349 | | - socket_addr = %ssh_service_socket_addr, |
350 | | - "Running deployment validation on container" |
351 | | - ); |
352 | | - |
353 | | - // Now we can use the proper SSH infrastructure with custom port support |
| 188 | + // Step 2: Configure SSH connectivity |
354 | 189 | let ssh_credentials = |
355 | | - create_container_ssh_credentials(&test_env.config.ssh_credentials.ssh_username) |
356 | | - .context("Failed to create container SSH credentials")?; |
| 190 | + create_test_ssh_credentials(&test_env.config.ssh_credentials.ssh_username)?; |
| 191 | + configure_ssh_connectivity(socket_addr, &ssh_credentials, Some(&running_container)).await?; |
357 | 192 |
|
358 | | - // Create SSH connection with the container's dynamic port |
359 | | - validate_container_deployment_with_port(&ssh_credentials, ssh_service_socket_addr) |
360 | | - .await |
361 | | - .context("Container deployment validation failed")?; |
| 193 | + // Step 3: Run provision simulation |
| 194 | + run_provision_simulation(socket_addr, &ssh_credentials, test_env).await?; |
362 | 195 |
|
363 | | - info!(status = "success", "All deployment validations passed"); |
| 196 | + // Step 4: Run Ansible configuration (expect failure due to inventory mismatch) |
| 197 | + run_ansible_configuration(socket_addr, test_env, false)?; |
364 | 198 |
|
365 | | - info!( |
366 | | - status = "success", |
367 | | - "Validation workflow completed successfully" |
368 | | - ); |
| 199 | + // Step 5: Run deployment validation |
| 200 | + run_deployment_validation(socket_addr, &ssh_credentials).await?; |
369 | 201 |
|
370 | | - Ok(()) |
371 | | -} |
372 | | - |
373 | | -/// Create centralized SSH credentials for test purposes |
374 | | -/// |
375 | | -/// Uses fixed test SSH keys from fixtures/ directory with provided username. |
376 | | -/// This factory eliminates code duplication across multiple functions that need |
377 | | -/// the same test SSH credentials. |
378 | | -fn create_test_ssh_credentials(ssh_username: &str) -> Result<SshCredentials> { |
379 | | - let project_root = std::env::current_dir().context("Failed to get current directory")?; |
380 | | - Ok(SshCredentials::new( |
381 | | - project_root.join("fixtures/testing_rsa"), |
382 | | - project_root.join("fixtures/testing_rsa.pub"), |
383 | | - ssh_username.to_string(), |
384 | | - )) |
385 | | -} |
386 | | - |
387 | | -/// Create SSH credentials for connecting to the container |
388 | | -fn create_container_ssh_credentials(ssh_username: &str) -> Result<SshCredentials> { |
389 | | - // Use the centralized test SSH credentials factory |
390 | | - create_test_ssh_credentials(ssh_username) |
391 | | -} |
392 | | - |
393 | | -/// Validate container deployment using SSH infrastructure with custom port |
394 | | -async fn validate_container_deployment_with_port( |
395 | | - ssh_credentials: &SshCredentials, |
396 | | - socket_addr: SocketAddr, |
397 | | -) -> Result<()> { |
398 | | - use torrust_tracker_deploy::infrastructure::remote_actions::{ |
399 | | - DockerComposeValidator, DockerValidator, RemoteAction, |
400 | | - }; |
401 | | - |
402 | | - let ip_addr = socket_addr.ip(); |
403 | | - |
404 | | - // Create SSH connection with the container's dynamic port using the new port support |
405 | | - let ssh_connection = ssh_credentials |
406 | | - .clone() |
407 | | - .with_host_and_port(ip_addr, socket_addr.port()); |
408 | | - |
409 | | - // Validate Docker installation |
410 | | - let docker_validator = DockerValidator::new(ssh_connection.clone()); |
411 | | - docker_validator |
412 | | - .execute(&ip_addr) |
413 | | - .await |
414 | | - .context("Docker validation failed")?; |
415 | | - |
416 | | - // Validate Docker Compose installation |
417 | | - let compose_validator = DockerComposeValidator::new(ssh_connection); |
418 | | - compose_validator |
419 | | - .execute(&ip_addr) |
420 | | - .await |
421 | | - .context("Docker Compose validation failed")?; |
| 202 | + // Step 6: Cleanup container |
| 203 | + cleanup_docker_container(running_container); |
422 | 204 |
|
| 205 | + info!("Configuration tests completed successfully"); |
423 | 206 | Ok(()) |
424 | 207 | } |
0 commit comments