torrust_tracker/console/ci/e2e/
tracker_container.rs1use std::time::Duration;
2
3use rand::distributions::Alphanumeric;
4use rand::Rng;
5
6use super::docker::{RunOptions, RunningContainer};
7use super::logs_parser::RunningServices;
8use crate::console::ci::e2e::docker::Docker;
9
10#[derive(Debug)]
11pub struct TrackerContainer {
12 pub image: String,
13 pub name: String,
14 pub running: Option<RunningContainer>,
15}
16
17impl Drop for TrackerContainer {
18 fn drop(&mut self) {
21 tracing::info!("Dropping tracker container: {}", self.name);
22 if Docker::container_exist(&self.name) {
23 let _unused = Docker::remove(&self.name);
24 }
25 }
26}
27
28impl TrackerContainer {
29 #[must_use]
30 pub fn new(tag: &str, container_name_prefix: &str) -> Self {
31 Self {
32 image: tag.to_owned(),
33 name: Self::generate_random_container_name(container_name_prefix),
34 running: None,
35 }
36 }
37
38 pub fn build_image(&self) {
42 tracing::info!("Building tracker container image with tag: {} ...", self.image);
43 Docker::build("./Containerfile", &self.image).expect("A tracker local docker image should be built");
44 }
45
46 pub fn run(&mut self, options: &RunOptions) {
50 tracing::info!("Running docker tracker image: {} ...", self.name);
51
52 let container = Docker::run(&self.image, &self.name, options).expect("A tracker local docker image should be running");
53
54 tracing::info!("Waiting for the container {} to be healthy ...", self.name);
55
56 let is_healthy = Docker::wait_until_is_healthy(&self.name, Duration::from_secs(10));
57
58 assert!(is_healthy, "Unhealthy tracker container: {}", &self.name);
59
60 tracing::info!("Container {} is healthy ...", &self.name);
61
62 self.running = Some(container);
63
64 self.assert_there_are_no_panics_in_logs();
65 }
66
67 #[must_use]
71 pub fn running_services(&self) -> RunningServices {
72 let logs = Docker::logs(&self.name).expect("Logs should be captured from running container");
73
74 tracing::info!("Parsing running services from logs. Logs :\n{logs}");
75
76 RunningServices::parse_from_logs(&logs)
77 }
78
79 pub fn stop(&mut self) {
83 match &self.running {
84 Some(container) => {
85 tracing::info!("Stopping docker tracker container: {} ...", self.name);
86
87 Docker::stop(container).expect("Container should be stopped");
88
89 self.assert_there_are_no_panics_in_logs();
90 }
91 None => {
92 if Docker::is_container_running(&self.name) {
93 tracing::error!("Tracker container {} was started manually", self.name);
94 } else {
95 tracing::info!("Docker tracker container is not running: {} ...", self.name);
96 }
97 }
98 }
99
100 self.running = None;
101 }
102
103 pub fn remove(&self) {
107 if let Some(_running_container) = &self.running {
108 tracing::error!("Can't remove running container: {} ...", self.name);
109 } else {
110 tracing::info!("Removing docker tracker container: {} ...", self.name);
111 Docker::remove(&self.name).expect("Container should be removed");
112 }
113 }
114
115 fn generate_random_container_name(prefix: &str) -> String {
116 let rand_string: String = rand::thread_rng()
117 .sample_iter(&Alphanumeric)
118 .take(20)
119 .map(char::from)
120 .collect();
121
122 format!("{prefix}{rand_string}")
123 }
124
125 fn assert_there_are_no_panics_in_logs(&self) {
126 let logs = Docker::logs(&self.name).expect("Logs should be captured from running container");
127
128 assert!(
129 !(logs.contains(" panicked at ") || logs.contains("RUST_BACKTRACE=1")),
130 "{}",
131 format!("Panics found is logs:\n{logs}")
132 );
133 }
134}