use foundation_deployment_docker::DockerClient;
use tracing::warn;
use crate::docker::error::{docker_err, DockerError, DockerResult};
#[derive(Clone)]
pub struct NetworkHandle {
client: DockerClient,
network_id: String,
name: String,
remove_on_drop: bool,
}
impl NetworkHandle {
pub async fn create_or_find(
client: &DockerClient,
name: &str,
subnet: Option<&str>,
) -> DockerResult<Self> {
if let Some(existing) = Self::find(client, name).await? {
return Ok(existing);
}
let mut config = serde_json::json!({
"Name": name,
"Driver": "bridge",
});
if let Some(subnet) = subnet {
config["IPAM"] = serde_json::json!({
"Driver": "default",
"Config": [{ "Subnet": subnet }],
});
}
let response = client
.network_create(config)
.await
.map_err(|e| docker_err(DockerError::Network(format!("{e}"))))?;
Ok(Self {
client: client.clone(),
network_id: response.id,
name: name.to_string(),
remove_on_drop: false,
})
}
pub async fn find(client: &DockerClient, name: &str) -> DockerResult<Option<Self>> {
let networks = client
.network_list(None)
.await
.map_err(|e| docker_err(DockerError::Network(format!("{e}"))))?;
for net in networks.as_array().unwrap_or(&vec![]) {
if net.get("Name").and_then(|n| n.as_str()) == Some(name) {
let id = net
.get("Id")
.and_then(|i| i.as_str())
.unwrap_or(name)
.to_string();
return Ok(Some(Self {
client: client.clone(),
network_id: id,
name: name.to_string(),
remove_on_drop: false,
}));
}
}
Ok(None)
}
#[must_use]
pub fn id(&self) -> &str {
&self.network_id
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn remove_on_drop(mut self) -> Self {
self.remove_on_drop = true;
self
}
pub async fn connect(&self, container_id: &str, aliases: &[&str]) -> DockerResult<()> {
self.client
.network_connect(&self.network_id, container_id, aliases)
.await
.map_err(|e| docker_err(DockerError::Network(format!("{e}"))))?;
Ok(())
}
pub async fn remove(&self) -> DockerResult<()> {
self.client
.network_delete(&self.name)
.await
.map_err(|e| docker_err(DockerError::Network(format!("{e}"))))?;
Ok(())
}
}
impl std::fmt::Debug for NetworkHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NetworkHandle")
.field("name", &self.name)
.field("network_id", &self.network_id)
.field("remove_on_drop", &self.remove_on_drop)
.finish()
}
}
impl Drop for NetworkHandle {
fn drop(&mut self) {
if !self.remove_on_drop {
return;
}
let client = self.client.clone();
let name = self.name.clone();
let _ = crate::block_on(async move {
if let Err(e) = client.network_delete(&name).await {
warn!(network = %name, "drop: failed to remove network: {e}");
}
});
}
}