use crate::error::{HylixError, HylixResult};
use crate::logging::{log_error, log_success, log_warning, ProgressExecutor};
use crate::{constants, docker::ContainerSpec};
use super::context::DevnetContext;
#[derive(Debug, Clone, PartialEq)]
pub enum ContainerStatus {
Running,
Stopped,
NotExisting,
}
pub async fn get_docker_container_status(container_name: &str) -> HylixResult<ContainerStatus> {
use tokio::process::Command;
let output = Command::new("docker")
.args(["ps", "-a", "-q", "-f", &format!("name=^{container_name}$")])
.output()
.await
.map_err(|e| HylixError::process(format!("Failed to check Docker container: {e}")))?;
if output.stdout.is_empty() {
return Ok(ContainerStatus::NotExisting);
}
let running_output = Command::new("docker")
.args(["ps", "-q", "-f", &format!("name={container_name}")])
.output()
.await
.map_err(|e| HylixError::process(format!("Failed to check Docker container: {e}")))?;
if running_output.stdout.is_empty() {
Ok(ContainerStatus::Stopped)
} else {
Ok(ContainerStatus::Running)
}
}
pub async fn check_docker_container(
_context: &DevnetContext,
container_name: &str,
) -> HylixResult<ContainerStatus> {
let status = get_docker_container_status(container_name).await?;
match status {
ContainerStatus::Running => {
log_success(&format!("Container {container_name} is running"));
}
ContainerStatus::Stopped => {
log_warning(&format!("Container {container_name} is stopped"));
}
ContainerStatus::NotExisting => {
log_error(&format!("Container {container_name} does not exist"));
}
}
Ok(status)
}
pub async fn start_containers(context: &DevnetContext) -> HylixResult<()> {
const CONTAINERS: [&str; 7] = constants::containers::ALL;
let executor = ProgressExecutor::new();
for container in CONTAINERS {
if get_docker_container_status(container).await? == ContainerStatus::Stopped {
start_container(&executor, context, container).await?;
}
}
executor.clear()?;
Ok(())
}
pub async fn start_container(
executor: &ProgressExecutor,
_context: &DevnetContext,
container_name: &str,
) -> HylixResult<()> {
let success = executor
.execute_command(
format!("Starting container {container_name}"),
"docker",
&["start", container_name],
None,
)
.await?;
if success {
log_success(&format!("Started container {container_name}"));
} else {
return Err(HylixError::process(format!(
"Failed to start {container_name}"
)));
}
Ok(())
}
pub async fn stop_and_remove_container(
pb: &indicatif::ProgressBar,
container_name: &str,
display_name: &str,
) -> HylixResult<()> {
use tokio::process::Command;
if get_docker_container_status(container_name).await? == ContainerStatus::NotExisting {
pb.set_message(format!("{display_name} does not exist, skipping..."));
return Ok(());
}
pb.set_message(format!("Stopping {display_name}..."));
let output = Command::new("docker")
.args(["stop", container_name])
.output()
.await
.map_err(|e| HylixError::process(format!("Failed to stop Docker container: {e}")))?;
if !output.status.success() {
log_warning(&format!("{}", String::from_utf8_lossy(&output.stderr)));
} else {
pb.set_message(format!("{display_name} stopped successfully"));
}
pb.set_message(format!("Removing {display_name}..."));
let output = Command::new("docker")
.args(["rm", container_name])
.output()
.await
.map_err(|e| HylixError::process(format!("Failed to remove Docker container: {e}")))?;
if !output.status.success() {
log_warning(&format!("{}", String::from_utf8_lossy(&output.stderr)));
} else {
pb.set_message(format!("{display_name} removed successfully"));
}
Ok(())
}
pub async fn start_managed_container(
executor: &ProgressExecutor,
spec: ContainerSpec,
pull: bool,
) -> HylixResult<()> {
crate::docker::ContainerManager::start_container(executor, spec, pull).await
}