forked from torrust/torrust-tracker
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: [torrust#634] E2E test runner: build, run and stop tracker image
- Loading branch information
1 parent
91c268a
commit 083af0e
Showing
9 changed files
with
281 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,7 @@ | |
"Swatinem", | ||
"Swiftbit", | ||
"taiki", | ||
"tempfile", | ||
"thiserror", | ||
"tlsv", | ||
"Torrentstorm", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//! Program to run E2E tests. | ||
//! | ||
//! ```text | ||
//! cargo run --bin e2e_tests_runner | ||
//! ``` | ||
use torrust_tracker::e2e::runner; | ||
|
||
fn main() { | ||
runner::run(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
use std::io; | ||
use std::process::{Child, Command, Output, Stdio}; | ||
use std::thread::sleep; | ||
use std::time::{Duration, Instant}; | ||
|
||
pub struct Docker {} | ||
|
||
impl Docker { | ||
/// Builds a Docker image from a given Dockerfile. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker build command fails. | ||
pub fn build(dockerfile: &str, tag: &str) -> io::Result<()> { | ||
let status = Command::new("docker") | ||
.args(["build", "-f", dockerfile, "-t", tag, "."]) | ||
.status()?; | ||
|
||
if status.success() { | ||
Ok(()) | ||
} else { | ||
Err(io::Error::new(io::ErrorKind::Other, "Failed to build Docker image")) | ||
} | ||
} | ||
|
||
/// Runs a Docker container from a given image. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker run command fails. | ||
pub fn run(image: &str, name: &str) -> io::Result<Output> { | ||
let output = Command::new("docker") | ||
.args(["run", "--detach", "--name", name, image]) | ||
.output()?; | ||
|
||
if output.status.success() { | ||
Ok(output) | ||
} else { | ||
Err(io::Error::new(io::ErrorKind::Other, "Failed to run Docker container")) | ||
} | ||
} | ||
|
||
/// Runs a Docker container from a given image in the background. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker run command fails to start. | ||
pub fn run_spawned(image: &str, name: &str) -> io::Result<Child> { | ||
let child = Command::new("docker") | ||
.args(["run", "--name", name, image]) | ||
.stdin(Stdio::null()) // Ignore stdin | ||
.stdout(Stdio::null()) // Ignore stdout | ||
.stderr(Stdio::null()) // Ignore stderr | ||
.spawn()?; | ||
|
||
Ok(child) | ||
} | ||
|
||
/// Stops a Docker container. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker stop command fails. | ||
pub fn stop(name: &str) -> io::Result<()> { | ||
let status = Command::new("docker").args(["stop", name]).status()?; | ||
|
||
if status.success() { | ||
Ok(()) | ||
} else { | ||
Err(io::Error::new(io::ErrorKind::Other, "Failed to stop Docker container")) | ||
} | ||
} | ||
|
||
/// Removes a Docker container. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker rm command fails. | ||
pub fn remove(name: &str) -> io::Result<()> { | ||
let status = Command::new("docker").args(["rm", "-f", name]).status()?; | ||
|
||
if status.success() { | ||
Ok(()) | ||
} else { | ||
Err(io::Error::new(io::ErrorKind::Other, "Failed to remove Docker container")) | ||
} | ||
} | ||
|
||
/// Fetches logs from a Docker container. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will fail if the docker logs command fails. | ||
pub fn logs(container_name: &str) -> io::Result<String> { | ||
let output = Command::new("docker").args(["logs", container_name]).output()?; | ||
|
||
if output.status.success() { | ||
Ok(String::from_utf8_lossy(&output.stdout).to_string()) | ||
} else { | ||
Err(io::Error::new( | ||
io::ErrorKind::Other, | ||
"Failed to fetch logs from Docker container", | ||
)) | ||
} | ||
} | ||
|
||
/// Checks if a Docker container is healthy. | ||
#[must_use] | ||
pub fn wait_until_is_healthy(name: &str, timeout: Duration) -> bool { | ||
let start = Instant::now(); | ||
|
||
while start.elapsed() < timeout { | ||
let Ok(output) = Command::new("docker") | ||
.args(["ps", "-f", &format!("name={name}"), "--format", "{{.Status}}"]) | ||
.output() | ||
else { | ||
return false; | ||
}; | ||
|
||
let output_str = String::from_utf8_lossy(&output.stdout); | ||
|
||
if output_str.contains("(healthy)") { | ||
return true; | ||
} | ||
|
||
sleep(Duration::from_secs(1)); | ||
} | ||
|
||
false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod docker; | ||
pub mod runner; | ||
pub mod temp_dir; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use std::env; | ||
use std::time::Duration; | ||
|
||
use rand::distributions::Alphanumeric; | ||
use rand::Rng; | ||
|
||
use crate::e2e::docker::Docker; | ||
use crate::e2e::temp_dir::Handler; | ||
|
||
/// # Panics | ||
/// | ||
/// Will panic if: | ||
/// | ||
/// - It can't build the docker image. | ||
/// - It can't create a temp dir. | ||
/// - It can't change to the new temp dir. | ||
/// - It can't revert the dit to the previous one. | ||
pub fn run() { | ||
/* todo: | ||
- [x] Build the docker image. | ||
- [x] Run the docker image. | ||
- [x] Wait until the container is healthy. | ||
- [ ] Parse logs to get running services. | ||
- [ ] Build config file for the tracker_checker. | ||
- [ ] Run the tracker_checker. | ||
- [x] Stop the container. | ||
*/ | ||
|
||
//Docker::build("./Containerfile", "local").expect("A tracker local docker image should be built"); | ||
|
||
println!( | ||
"Current dir: {:?}", | ||
env::current_dir().expect("It should return the current dir") | ||
); | ||
|
||
println!("Create temp dir ..."); | ||
let temp_dir_handler = Handler::new().expect("A temp dir should be created"); | ||
println!("Temp dir created: {:?}", temp_dir_handler.temp_dir); | ||
|
||
println!("Change dir to: {:?}", temp_dir_handler.temp_dir); | ||
temp_dir_handler | ||
.change_to_temp_dir() | ||
.expect("The app should change dir to the temp dir"); | ||
|
||
let container_name = generate_random_container_name("tracker_"); | ||
|
||
println!("Running docker tracker image: {container_name} ..."); | ||
Docker::run("local", &container_name).expect("A tracker local docker image should be running"); | ||
|
||
println!("Waiting for the container {container_name} to be healthy ..."); | ||
let is_healthy = Docker::wait_until_is_healthy(&container_name, Duration::from_secs(10)); | ||
|
||
if !is_healthy { | ||
println!("Unhealthy container: {container_name}"); | ||
println!("Stopping container: {container_name} ..."); | ||
Docker::stop(&container_name).expect("A tracker local docker image should be stopped"); | ||
panic!("Unhealthy container: {container_name}"); | ||
} | ||
|
||
println!("Container {container_name} is healthy ..."); | ||
|
||
println!("Stopping docker tracker image: {container_name} ..."); | ||
Docker::stop(&container_name).expect("A tracker local docker image should be stopped"); | ||
|
||
println!("Revert current dir to: {:?}", temp_dir_handler.original_dir); | ||
temp_dir_handler | ||
.revert_to_original_dir() | ||
.expect("The app should revert dir from temp dir to the original one"); | ||
} | ||
|
||
fn generate_random_container_name(prefix: &str) -> String { | ||
let rand_string: String = rand::thread_rng() | ||
.sample_iter(&Alphanumeric) | ||
.take(20) | ||
.map(char::from) | ||
.collect(); | ||
|
||
format!("{prefix}{rand_string}") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use std::path::PathBuf; | ||
use std::{env, io}; | ||
|
||
use tempfile::TempDir; | ||
|
||
pub struct Handler { | ||
pub temp_dir: TempDir, | ||
pub original_dir: PathBuf, | ||
} | ||
|
||
impl Handler { | ||
/// Creates a new temporary directory and remembers the current working directory. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will error if: | ||
/// | ||
/// - It can't create the temp dir. | ||
/// - It can't get the current dir. | ||
pub fn new() -> io::Result<Self> { | ||
let temp_dir = TempDir::new()?; | ||
let original_dir = env::current_dir()?; | ||
|
||
Ok(Handler { temp_dir, original_dir }) | ||
} | ||
|
||
/// Changes the current working directory to the temporary directory. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will error if it can't change the current di to the temp dir. | ||
pub fn change_to_temp_dir(&self) -> io::Result<()> { | ||
env::set_current_dir(self.temp_dir.path()) | ||
} | ||
|
||
/// Changes the current working directory back to the original directory. | ||
/// | ||
/// # Errors | ||
/// | ||
/// Will error if it can't revert the current dir to the original one. | ||
pub fn revert_to_original_dir(&self) -> io::Result<()> { | ||
env::set_current_dir(&self.original_dir) | ||
} | ||
} | ||
|
||
impl Drop for Handler { | ||
/// Ensures that the temporary directory is deleted when the struct goes out of scope. | ||
fn drop(&mut self) { | ||
// The temporary directory is automatically deleted when `TempDir` is dropped. | ||
// We can add additional cleanup here if necessary. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters