runners.rs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. use std::{
  2. fs::File,
  3. io::{BufRead, BufReader, Write},
  4. process::{Child, Command, Stdio},
  5. sync::{Arc, Mutex},
  6. thread,
  7. };
  8. use chrono::{DateTime, Utc};
  9. use log::{info, warn};
  10. use serde::{Deserialize, Serialize};
  11. use uuid::Uuid;
  12. use crate::DOCKER_ID;
  13. pub trait Run {
  14. fn run(&mut self) -> anyhow::Result<()>;
  15. }
  16. pub trait Wait {
  17. fn wait(&mut self) -> anyhow::Result<()>;
  18. }
  19. pub trait RunWait: Run + Wait + Log {}
  20. impl<T: Run + Wait + Log> RunWait for T {}
  21. pub fn run_wait<T: RunWait>(item: &mut T) -> anyhow::Result<RunReport> {
  22. let mut run_report = RunReport {
  23. start: Utc::now(),
  24. ..Default::default()
  25. };
  26. item.run()?;
  27. item.wait()?;
  28. run_report.log = item.log();
  29. run_report.end = Utc::now();
  30. Ok(run_report)
  31. }
  32. #[derive(Debug, Default, Serialize, Deserialize)]
  33. pub struct RunReport {
  34. pub start: DateTime<Utc>,
  35. pub end: DateTime<Utc>,
  36. pub log: String,
  37. }
  38. impl RunReport {
  39. /// Serialize the RunReport to a JSON string
  40. pub fn save_to_file(&self, file_prefix: &str) -> std::io::Result<()> {
  41. let json_data = serde_json::to_string_pretty(self).expect("Failed to serialize RunReport");
  42. let uuid = Uuid::new_v4().to_string()[..5].to_string();
  43. let file_path = format!("{}{}.log", file_prefix, uuid);
  44. let mut file = File::create(&file_path)?;
  45. file.write_all(json_data.as_bytes())?;
  46. Ok(())
  47. }
  48. }
  49. pub trait Log {
  50. fn log(&self) -> String;
  51. }
  52. #[derive(Debug, Default)]
  53. pub struct DockerRun {
  54. pub args: Vec<String>,
  55. pub container_id: Arc<Mutex<Option<String>>>,
  56. pub start: DateTime<Utc>,
  57. pub logs: Arc<Mutex<String>>,
  58. }
  59. impl DockerRun {
  60. pub fn new(args: &[&str]) -> Self {
  61. DockerRun {
  62. args: args.iter().map(|e| e.to_string()).collect(),
  63. start: Utc::now(),
  64. ..Default::default()
  65. }
  66. }
  67. }
  68. impl Run for DockerRun {
  69. fn run(&mut self) -> anyhow::Result<()> {
  70. // Setup Ctrl-C handler cant be defined two times
  71. let _ = ctrlc::try_set_handler(move || {
  72. if let Ok(container_id) = DOCKER_ID.lock() {
  73. if let Some(ref id) = *container_id {
  74. warn!("Stopping Docker container...");
  75. let _ = Command::new("docker").args(["stop", id]).status();
  76. }
  77. }
  78. std::process::exit(1);
  79. });
  80. // Spawn the main command
  81. let output = Command::new("docker")
  82. .args(&self.args)
  83. .stdout(Stdio::piped())
  84. .stderr(Stdio::inherit())
  85. .output()
  86. .expect("Failed to run Docker command");
  87. // add id to Arc
  88. let id = String::from_utf8_lossy(&output.stdout).trim().to_string();
  89. {
  90. let mut container_id_lock = DOCKER_ID.lock().unwrap();
  91. *container_id_lock = Some(id.clone());
  92. }
  93. // Spawn a thread to follow the logs
  94. let log_id = id.clone();
  95. let logs_clone = Arc::clone(&self.logs);
  96. let _loger_thread = thread::spawn(move || {
  97. let output = Command::new("docker")
  98. .args(["inspect", "--format='{{.Config.Image}}'", &log_id])
  99. .output()
  100. .expect("Failed to execute Docker inspect");
  101. let container_name = if output.status.success() {
  102. String::from_utf8(output.stdout)
  103. .expect("Failed to convert output to string")
  104. .trim() // Trim to remove any trailing newlines
  105. .to_string()
  106. } else {
  107. "?".to_string()
  108. };
  109. let mut child = Command::new("docker")
  110. .args(["logs", "--follow", &log_id])
  111. .stdout(Stdio::piped())
  112. .stderr(Stdio::inherit())
  113. .spawn()
  114. .expect("Failed to follow Docker logs");
  115. if let Some(stdout) = child.stdout.take() {
  116. let reader = BufReader::new(stdout);
  117. for line in reader.lines().map_while(Result::ok) {
  118. info!("[{container_name}] {line}");
  119. let mut logs_lock = logs_clone.lock().unwrap();
  120. logs_lock.push_str(&line);
  121. logs_lock.push('\n');
  122. }
  123. }
  124. });
  125. Ok(())
  126. }
  127. }
  128. impl Wait for DockerRun {
  129. fn wait(&mut self) -> anyhow::Result<()> {
  130. if let Ok(container_id) = DOCKER_ID.lock() {
  131. if let Some(ref id) = *container_id {
  132. let status = Command::new("docker")
  133. .args(["wait", id])
  134. .status()
  135. .expect("Failed to wait on Docker container");
  136. if !status.success() {
  137. warn!("Docker command failed with status: {}", status);
  138. }
  139. }
  140. }
  141. let mut container_id_lock = DOCKER_ID.lock().unwrap();
  142. *container_id_lock = None;
  143. Ok(())
  144. }
  145. }
  146. impl Log for DockerRun {
  147. fn log(&self) -> String {
  148. let logs_lock = self.logs.lock().unwrap();
  149. logs_lock.to_string()
  150. }
  151. }
  152. pub struct CommandRun {
  153. pub bin: String,
  154. pub args: Vec<String>,
  155. pub child: Option<Child>,
  156. pub log: String,
  157. }
  158. impl CommandRun {
  159. pub fn new(bin: &str, args: &[&str]) -> Self {
  160. CommandRun {
  161. bin: bin.to_string(),
  162. args: args.iter().map(|e| e.to_string()).collect(),
  163. child: None,
  164. log: String::default(),
  165. }
  166. }
  167. }
  168. impl Run for CommandRun {
  169. fn run(&mut self) -> anyhow::Result<()> {
  170. info!("Running command: {} {}", &self.bin, &self.args.join(" "));
  171. let mut child = Command::new(&self.bin)
  172. .args(&self.args)
  173. .stdout(Stdio::inherit())
  174. .stderr(Stdio::piped())
  175. .spawn()
  176. .expect("Failed to spawn");
  177. if let Some(stderr) = child.stderr.take() {
  178. let reader = BufReader::new(stderr);
  179. for line in reader.lines().map_while(Result::ok) {
  180. info!("[{}] {line}", self.bin);
  181. self.log.push_str(&line);
  182. self.log.push('\n');
  183. }
  184. }
  185. self.child = Some(child);
  186. Ok(())
  187. }
  188. }
  189. impl Wait for CommandRun {
  190. fn wait(&mut self) -> anyhow::Result<()> {
  191. if let Some(child) = &mut self.child {
  192. child.wait().unwrap();
  193. }
  194. Ok(())
  195. }
  196. }
  197. impl Log for CommandRun {
  198. fn log(&self) -> String {
  199. self.log.clone()
  200. }
  201. }