use foundation_netio::shared::client::body_reader::collect_bytes_from_send_safe;
use foundation_netio::shared::http::SimpleHeader;
use foundation_netio::PreparedRequestBuilder;
use crate::error::DockerError;
use crate::generated::shared::ApiError;
use crate::generated::update::{container_update_request, ContainerUpdateArgs, ContainerUpdateResponse};
use crate::generated::changes::{container_changes_request, ContainerChangesArgs};
use crate::generated::json::{container_list_request, ContainerListArgs};
use crate::generated::kill::{container_kill_request, ContainerKillArgs};
use crate::generated::prune::{container_prune_request, ContainerPruneArgs};
use crate::generated::rename::{container_rename_request, ContainerRenameArgs};
use crate::generated::resize::{container_resize_request, ContainerResizeArgs};
use crate::generated::restart::{container_restart_request, ContainerRestartArgs};
use crate::generated::stats::{container_stats_request, ContainerStatsArgs, ContainerStatsResponse};
use crate::generated::top::{container_top_request, ContainerTopArgs, ContainerTopResponse};
use super::DockerClient;
use super::NoMod;
impl DockerClient {
pub async fn restart_container(
&self,
id: &str,
signal: Option<&str>,
t: Option<u32>,
) -> Result<(), DockerError> {
let args = ContainerRestartArgs {
id: id.to_string(),
signal: signal.map(str::to_string),
t: t.map(|v| v.to_string()),
};
container_restart_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(())
}
pub async fn kill_container(
&self,
id: &str,
signal: Option<&str>,
) -> Result<(), DockerError> {
let args = ContainerKillArgs {
id: id.to_string(),
signal: signal.map(str::to_string),
};
container_kill_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(())
}
pub async fn rename_container(&self, id: &str, name: &str) -> Result<(), DockerError> {
let args = ContainerRenameArgs {
id: id.to_string(),
name: Some(name.to_string()),
};
container_rename_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(())
}
pub async fn resize_container(
&self,
id: &str,
h: Option<u32>,
w: Option<u32>,
) -> Result<(), DockerError> {
let args = ContainerResizeArgs {
id: id.to_string(),
h: h.map(|v| v.to_string()),
w: w.map(|v| v.to_string()),
};
container_resize_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(())
}
pub async fn container_top(
&self,
id: &str,
ps_args: Option<&str>,
) -> Result<ContainerTopResponse, DockerError> {
let args = ContainerTopArgs {
id: id.to_string(),
ps_args: ps_args.map(str::to_string),
};
let response = container_top_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(response.body)
}
pub async fn container_changes(
&self,
id: &str,
) -> Result<serde_json::Value, DockerError> {
let args = ContainerChangesArgs {
id: id.to_string(),
};
let response = container_changes_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(response.body)
}
pub async fn container_logs(
&self,
id: &str,
follow: bool,
stdout: bool,
stderr: bool,
since: Option<&str>,
until: Option<&str>,
timestamps: bool,
tail: Option<&str>,
) -> Result<Vec<u8>, DockerError> {
let endpoint_url = format!("{}/containers/{id}/logs", self.base_url());
let mut builder = PreparedRequestBuilder::get(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
builder = builder.query("follow", Some(follow.to_string()).as_deref());
builder = builder.query("stdout", Some(stdout.to_string()).as_deref());
builder = builder.query("stderr", Some(stderr.to_string()).as_deref());
builder = builder.query("since", since);
builder = builder.query("until", until);
builder = builder.query("timestamps", Some(timestamps.to_string()).as_deref());
builder = builder.query("tail", tail);
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
Ok(collect_bytes_from_send_safe(response.take_body()))
}
pub async fn container_stats(
&self,
id: &str,
stream: bool,
one_shot: bool,
) -> Result<ContainerStatsResponse, DockerError> {
let args = ContainerStatsArgs {
id: id.to_string(),
stream: Some(stream.to_string()),
one_shot: Some(one_shot.to_string()),
};
let response = container_stats_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(response.body)
}
pub async fn container_export(&self, id: &str) -> Result<Vec<u8>, DockerError> {
let endpoint_url = format!("{}/containers/{id}/export", self.base_url());
let builder = PreparedRequestBuilder::get(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
Ok(collect_bytes_from_send_safe(response.take_body()))
}
pub async fn container_archive(&self, id: &str, path: &str) -> Result<Vec<u8>, DockerError> {
let endpoint_url = format!("{}/containers/{id}/archive", self.base_url());
let mut builder = PreparedRequestBuilder::get(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
builder = builder.query("path", Some(path));
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
Ok(collect_bytes_from_send_safe(response.take_body()))
}
pub async fn container_attach(
&self,
id: &str,
detach_keys: Option<&str>,
logs: bool,
stream: bool,
stdin: bool,
stdout: bool,
stderr: bool,
) -> Result<Vec<u8>, DockerError> {
let endpoint_url = format!("{}/containers/{id}/attach", self.base_url());
let mut builder = PreparedRequestBuilder::post(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
builder = builder.query("detachKeys", detach_keys);
builder = builder.query("logs", Some(logs.to_string()).as_deref());
builder = builder.query("stream", Some(stream.to_string()).as_deref());
builder = builder.query("stdin", Some(stdin.to_string()).as_deref());
builder = builder.query("stdout", Some(stdout.to_string()).as_deref());
builder = builder.query("stderr", Some(stderr.to_string()).as_deref());
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
Ok(collect_bytes_from_send_safe(response.take_body()))
}
pub async fn container_list(
&self,
all: bool,
limit: Option<u32>,
size: bool,
filters: Option<&str>,
) -> Result<serde_json::Value, DockerError> {
let args = ContainerListArgs {
all: Some(all.to_string()),
limit: limit.map(|v| v.to_string()),
size: Some(size.to_string()),
filters: filters.map(str::to_string),
};
let response = container_list_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(response.body)
}
pub async fn container_prune(
&self,
filters: Option<&str>,
) -> Result<(), DockerError> {
let args = ContainerPruneArgs {
filters: filters.map(str::to_string),
};
container_prune_request(self.http(), &args, &self.base_url(), None::<NoMod>).await?;
Ok(())
}
pub async fn update_container(
&self,
id: &str,
config: serde_json::Value,
) -> Result<ContainerUpdateResponse, DockerError> {
let args = ContainerUpdateArgs { id: id.to_string() };
let response = container_update_request(
self.http(),
&args,
&self.base_url(),
Some(move |b: &mut PreparedRequestBuilder| {
let _ = b.set_body_json(&config);
}),
)
.await?;
Ok(response.body)
}
pub async fn upload_to_container(
&self,
id: &str,
path: &str,
tar: Vec<u8>,
no_overwrite_dir_non_dir: Option<bool>,
copy_uidgid: Option<bool>,
) -> Result<(), DockerError> {
let endpoint_url = format!("{}/containers/{id}/archive", self.base_url());
let mut builder = PreparedRequestBuilder::put(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
builder = builder.query("path", Some(path));
builder = builder.query(
"noOverwriteDirNonDir",
no_overwrite_dir_non_dir.map(|v| v.to_string()).as_deref(),
);
builder = builder.query("copyUIDGID", copy_uidgid.map(|v| v.to_string()).as_deref());
builder = builder.header(SimpleHeader::custom("Content-Type"), "application/x-tar");
builder = builder.body_bytes(tar);
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
Ok(())
}
pub async fn get_container_archive_info(
&self,
id: &str,
path: &str,
) -> Result<String, DockerError> {
let endpoint_url = format!("{}/containers/{id}/archive", self.base_url());
let mut builder = PreparedRequestBuilder::head(&endpoint_url)
.map_err(|e| ApiError::RequestBuildFailed(e.to_string()))?;
builder = builder.query("path", Some(path));
let response = self.http().send_async(builder.build()).await
.map_err(|e| ApiError::RequestSendFailed(e.to_string()))?;
let status: usize = response.get_status().into();
let headers = response.get_headers_ref().clone();
if status < 200 || status >= 300 {
return Err(ApiError::HttpStatus { code: status as u16, headers, body: None }.into());
}
let stat = headers
.iter()
.find(|(k, _)| k.to_string().eq_ignore_ascii_case("x-docker-container-path-stat"))
.and_then(|(_, v)| v.first())
.cloned();
stat.ok_or_else(|| {
ApiError::RequestSendFailed("missing X-Docker-Container-Path-Stat header".to_string())
.into()
})
}
}