archy/core/startos/src/util/docker.rs

240 lines
6.2 KiB
Rust
Raw Normal View History

2026-01-24 22:59:20 +00:00
use std::net::Ipv4Addr;
use std::time::Duration;
use models::{Error, ErrorKind, PackageId, ResultExt, Version};
use nix::sys::signal::Signal;
use tokio::process::Command;
use crate::util::Invoke;
#[cfg(feature = "docker")]
pub const CONTAINER_TOOL: &str = "docker";
#[cfg(not(feature = "docker"))]
pub const CONTAINER_TOOL: &str = "podman";
#[cfg(feature = "docker")]
pub const CONTAINER_DATADIR: &str = "/var/lib/docker";
#[cfg(not(feature = "docker"))]
pub const CONTAINER_DATADIR: &str = "/var/lib/containers";
pub struct DockerImageSha(String);
// docker images start9/${package}/*:${version} -q --no-trunc
pub async fn images_for(
package: &PackageId,
version: &Version,
) -> Result<Vec<DockerImageSha>, Error> {
Ok(String::from_utf8(
Command::new(CONTAINER_TOOL)
.arg("images")
.arg(format!("start9/{package}/*:{version}"))
.arg("--no-trunc")
.arg("-q")
.invoke(ErrorKind::Docker)
.await?,
)?
.lines()
.map(|l| DockerImageSha(l.trim().to_owned()))
.collect())
}
// docker rmi -f ${sha}
pub async fn remove_image(sha: &DockerImageSha) -> Result<(), Error> {
match Command::new(CONTAINER_TOOL)
.arg("rmi")
.arg("-f")
.arg(&sha.0)
.invoke(ErrorKind::Docker)
.await
.map(|_| ())
{
Err(e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such image") =>
{
Ok(())
}
a => a,
}?;
Ok(())
}
// docker image prune -f
pub async fn prune_images() -> Result<(), Error> {
Command::new(CONTAINER_TOOL)
.arg("image")
.arg("prune")
.arg("-f")
.invoke(ErrorKind::Docker)
.await?;
Ok(())
}
// docker container inspect ${name} --format '{{.NetworkSettings.Networks.start9.IPAddress}}'
pub async fn get_container_ip(name: &str) -> Result<Option<Ipv4Addr>, Error> {
match Command::new(CONTAINER_TOOL)
.arg("container")
.arg("inspect")
.arg(name)
.arg("--format")
.arg("{{.NetworkSettings.Networks.start9.IPAddress}}")
.invoke(ErrorKind::Docker)
.await
{
Err(e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
Ok(None)
}
Err(e) => Err(e),
Ok(a) => {
let out = std::str::from_utf8(&a)?.trim();
if out.is_empty() {
Ok(None)
} else {
Ok(Some({
out.parse()
.with_ctx(|_| (ErrorKind::ParseNetAddress, out.to_string()))?
}))
}
}
}
}
// docker stop -t ${timeout} -s ${signal} ${name}
pub async fn stop_container(
name: &str,
timeout: Option<Duration>,
signal: Option<Signal>,
) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("stop");
if let Some(dur) = timeout {
cmd.arg("-t").arg(dur.as_secs().to_string());
}
if let Some(sig) = signal {
cmd.arg("-s").arg(sig.to_string());
}
cmd.arg(name);
match cmd.invoke(ErrorKind::Docker).await {
Ok(_) => Ok(()),
Err(mut e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
e.kind = ErrorKind::NotFound;
Err(e)
}
Err(e) => Err(e),
}
}
// docker kill -s ${signal} ${name}
pub async fn kill_container(name: &str, signal: Option<Signal>) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("kill");
if let Some(sig) = signal {
cmd.arg("-s").arg(sig.to_string());
}
cmd.arg(name);
match cmd.invoke(ErrorKind::Docker).await {
Ok(_) => Ok(()),
Err(mut e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
e.kind = ErrorKind::NotFound;
Err(e)
}
Err(e) => Err(e),
}
}
// docker pause ${name}
pub async fn pause_container(name: &str) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("pause");
cmd.arg(name);
match cmd.invoke(ErrorKind::Docker).await {
Ok(_) => Ok(()),
Err(mut e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
e.kind = ErrorKind::NotFound;
Err(e)
}
Err(e) => Err(e),
}
}
// docker unpause ${name}
pub async fn unpause_container(name: &str) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("unpause");
cmd.arg(name);
match cmd.invoke(ErrorKind::Docker).await {
Ok(_) => Ok(()),
Err(mut e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
e.kind = ErrorKind::NotFound;
Err(e)
}
Err(e) => Err(e),
}
}
// docker rm -f ${name}
pub async fn remove_container(name: &str, force: bool) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("rm");
if force {
cmd.arg("-f");
}
cmd.arg(name);
match cmd.invoke(ErrorKind::Docker).await {
Ok(_) => Ok(()),
Err(e)
if e.source
.to_string()
.to_ascii_lowercase()
.contains("no such container") =>
{
Ok(())
}
Err(e) => Err(e),
}
}
// docker network create -d bridge --subnet ${subnet} --opt com.podman.network.bridge.name=${bridge_name}
pub async fn create_bridge_network(
name: &str,
subnet: &str,
bridge_name: &str,
) -> Result<(), Error> {
let mut cmd = Command::new(CONTAINER_TOOL);
cmd.arg("network").arg("create");
cmd.arg("-d").arg("bridge");
cmd.arg("--subnet").arg(subnet);
cmd.arg("--opt")
.arg(format!("com.docker.network.bridge.name={bridge_name}"));
cmd.arg(name);
cmd.invoke(ErrorKind::Docker).await?;
Ok(())
}