#![allow(dead_code)]
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use lightshuttle_runtime::LifecycleEvent;
use tokio::sync::broadcast::Receiver;
use tokio::sync::broadcast::error::RecvError;
#[must_use]
pub(crate) fn docker_available() -> bool {
Command::new("docker")
.arg("info")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|status| status.success())
.unwrap_or(false)
}
#[must_use]
pub(crate) fn unique_project(prefix: &str) -> String {
static COUNTER: AtomicU32 = AtomicU32::new(0);
let sequence = COUNTER.fetch_add(1, Ordering::Relaxed);
format!("ls-it-{prefix}-{}-{sequence}", std::process::id())
}
pub(crate) async fn wait_for_healthy(
events: &mut Receiver<LifecycleEvent>,
resource: &str,
timeout: Duration,
) -> Result<(), String> {
let wait = async {
loop {
match events.recv().await {
Ok(LifecycleEvent::ResourceHealthy { name }) if name == resource => {
return Ok(());
}
Ok(LifecycleEvent::ResourceFailed { name, error }) if name == resource => {
return Err(format!("resource `{resource}` failed: {error}"));
}
Ok(_) | Err(RecvError::Lagged(_)) => {}
Err(RecvError::Closed) => {
return Err(format!(
"event channel closed before `{resource}` became healthy"
));
}
}
}
};
tokio::time::timeout(timeout, wait)
.await
.map_err(|_| format!("timed out waiting for `{resource}` to become healthy"))?
}
pub(crate) struct ProjectCleanup {
project: String,
}
impl ProjectCleanup {
#[must_use]
pub(crate) fn new(project: impl Into<String>) -> Self {
Self {
project: project.into(),
}
}
#[must_use]
pub(crate) fn project(&self) -> &str {
&self.project
}
}
impl Drop for ProjectCleanup {
fn drop(&mut self) {
let label = format!("label=lightshuttle.project={}", self.project);
if let Ok(listed) = Command::new("docker")
.args(["ps", "-aq", "--filter", &label])
.output()
{
for id in String::from_utf8_lossy(&listed.stdout).split_whitespace() {
let _ = Command::new("docker").args(["rm", "-f", id]).output();
}
}
let _ = Command::new("docker")
.args(["network", "rm", &format!("lightshuttle-{}", self.project)])
.output();
}
}