//! Create and manage user-defined networks that containers can be attached to. //! //! API Reference: use std::{ collections::{BTreeMap, HashMap}, hash::Hash, }; use hyper::Body; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use url::form_urlencoded; use crate::{ docker::Docker, errors::{Error, Result}, }; /// Interface for docker network /// /// API Reference: pub struct Networks<'docker> { docker: &'docker Docker, } impl<'docker> Networks<'docker> { /// Exports an interface for interacting with docker Networks pub fn new(docker: &'docker Docker) -> Self { Networks { docker } } /// List the docker networks on the current docker host /// /// API Reference: pub async fn list( &self, opts: &NetworkListOptions, ) -> Result> { let mut path = vec!["/networks".to_owned()]; if let Some(query) = opts.serialize() { path.push(query); } self.docker.get_json(&path.join("?")).await } /// Returns a reference to a set of operations available to a specific network instance pub fn get( &self, id: S, ) -> Network<'docker> where S: Into, { Network::new(self.docker, id) } /// Create a new Network instance /// /// API Reference: pub async fn create( &self, opts: &NetworkCreateOptions, ) -> Result { let body: Body = opts.serialize()?.into(); let path = vec!["/networks/create".to_owned()]; self.docker .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON))) .await } } /// Interface for accessing and manipulating a docker network pub struct Network<'docker> { docker: &'docker Docker, id: String, } impl<'docker> Network<'docker> { /// Exports an interface exposing operations against a network instance pub fn new( docker: &'docker Docker, id: S, ) -> Self where S: Into, { Network { docker, id: id.into(), } } /// a getter for the Network id pub fn id(&self) -> &str { &self.id } /// Inspects the current docker network instance's details /// /// API Reference: pub async fn inspect(&self) -> Result { self.docker .get_json(&format!("/networks/{}", self.id)[..]) .await } /// Delete the network instance /// /// API Reference: pub async fn delete(&self) -> Result<()> { self.docker .delete(&format!("/networks/{}", self.id)[..]) .await?; Ok(()) } /// Connect container to network /// /// API Reference: pub async fn connect( &self, opts: &ContainerConnectionOptions, ) -> Result<()> { self.do_connection("connect", opts).await } /// Disconnect container to network /// /// API Reference: pub async fn disconnect( &self, opts: &ContainerConnectionOptions, ) -> Result<()> { self.do_connection("disconnect", opts).await } async fn do_connection( &self, segment: &str, opts: &ContainerConnectionOptions, ) -> Result<()> { let body: Body = opts.serialize()?.into(); self.docker .post( &format!("/networks/{}/{}", self.id, segment)[..], Some((body, mime::APPLICATION_JSON)), ) .await?; Ok(()) } } /// Options for filtering networks list results #[derive(Default, Debug)] pub struct NetworkListOptions { params: HashMap<&'static str, String>, } impl NetworkListOptions { /// serialize options as a string. returns None if no options are defined pub fn serialize(&self) -> Option { if self.params.is_empty() { None } else { Some( form_urlencoded::Serializer::new(String::new()) .extend_pairs(&self.params) .finish(), ) } } } /// Interface for creating new docker network #[derive(Serialize, Debug)] pub struct NetworkCreateOptions { params: HashMap<&'static str, Value>, } impl NetworkCreateOptions { /// return a new instance of a builder for options pub fn builder(name: &str) -> NetworkCreateOptionsBuilder { NetworkCreateOptionsBuilder::new(name) } /// serialize options as a string. returns None if no options are defined pub fn serialize(&self) -> Result { serde_json::to_string(&self.params).map_err(Error::from) } pub fn parse_from<'a, K, V>( &self, params: &'a HashMap, body: &mut serde_json::Map, ) where &'a HashMap: IntoIterator, K: ToString + Eq + Hash, V: Serialize, { for (k, v) in params.iter() { let key = k.to_string(); let value = serde_json::to_value(v).unwrap(); body.insert(key, value); } } } #[derive(Default)] pub struct NetworkCreateOptionsBuilder { params: HashMap<&'static str, Value>, } impl NetworkCreateOptionsBuilder { pub(crate) fn new(name: &str) -> Self { let mut params = HashMap::new(); params.insert("Name", json!(name)); NetworkCreateOptionsBuilder { params } } pub fn driver( &mut self, name: &str, ) -> &mut Self { if !name.is_empty() { self.params.insert("Driver", json!(name)); } self } pub fn label( &mut self, labels: HashMap, ) -> &mut Self { self.params.insert("Labels", json!(labels)); self } pub fn build(&self) -> NetworkCreateOptions { NetworkCreateOptions { params: self.params.clone(), } } } /// Interface for connect container to network #[derive(Serialize, Debug)] pub struct ContainerConnectionOptions { params: HashMap<&'static str, Value>, } impl ContainerConnectionOptions { /// serialize options as a string. returns None if no options are defined pub fn serialize(&self) -> Result { serde_json::to_string(&self.params).map_err(Error::from) } pub fn parse_from<'a, K, V>( &self, params: &'a HashMap, body: &mut BTreeMap, ) where &'a HashMap: IntoIterator, K: ToString + Eq + Hash, V: Serialize, { for (k, v) in params.iter() { let key = k.to_string(); let value = serde_json::to_value(v).unwrap(); body.insert(key, value); } } /// return a new instance of a builder for options pub fn builder(container_id: &str) -> ContainerConnectionOptionsBuilder { ContainerConnectionOptionsBuilder::new(container_id) } } #[derive(Default)] pub struct ContainerConnectionOptionsBuilder { params: HashMap<&'static str, Value>, } impl ContainerConnectionOptionsBuilder { pub(crate) fn new(container_id: &str) -> Self { let mut params = HashMap::new(); params.insert("Container", json!(container_id)); ContainerConnectionOptionsBuilder { params } } pub fn aliases( &mut self, aliases: Vec<&str>, ) -> &mut Self { self.params .insert("EndpointConfig", json!({ "Aliases": json!(aliases) })); self } pub fn force(&mut self) -> &mut Self { self.params.insert("Force", json!(true)); self } pub fn build(&self) -> ContainerConnectionOptions { ContainerConnectionOptions { params: self.params.clone(), } } } type PortDescription = HashMap>>>; #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct NetworkSettings { pub bridge: String, pub gateway: String, #[serde(rename = "IPAddress")] pub ip_address: String, #[serde(rename = "IPPrefixLen")] pub ip_prefix_len: u64, pub mac_address: String, pub ports: Option, pub networks: HashMap, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct NetworkEntry { #[serde(rename = "NetworkID")] pub network_id: String, #[serde(rename = "EndpointID")] pub endpoint_id: String, pub gateway: String, #[serde(rename = "IPAddress")] pub ip_address: String, #[serde(rename = "IPPrefixLen")] pub ip_prefix_len: u64, #[serde(rename = "IPv6Gateway")] pub ipv6_gateway: String, #[serde(rename = "GlobalIPv6Address")] pub global_ipv6_address: String, #[serde(rename = "GlobalIPv6PrefixLen")] pub global_ipv6_prefix_len: u64, pub mac_address: String, pub links: Option>, pub aliases: Option>, #[serde(rename = "IPAMConfig")] pub ipam_config: Option, } #[derive(Clone, Debug, Serialize, Deserialize)] #[allow(clippy::upper_case_acronyms)] pub struct EndpointIPAMConfig { #[serde(rename = "IPv4Address")] pub ipv4_address: String, #[serde(rename = "IPv6Address")] pub ipv6_address: String, #[serde(rename = "LinkLocalIPs")] pub link_local_ips: Vec, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct Ipam { pub driver: String, pub config: Vec>, pub options: Option>, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct NetworkDetails { pub name: String, pub id: String, pub scope: String, pub driver: String, #[serde(rename = "EnableIPv6")] pub enable_ipv6: bool, #[serde(rename = "IPAM")] pub ipam: Ipam, pub internal: bool, pub attachable: bool, pub containers: HashMap, pub options: Option>, pub labels: Option>, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct NetworkContainerDetails { #[serde(rename = "EndpointID")] pub endpoint_id: String, pub mac_address: String, #[serde(rename = "IPv4Address")] pub ipv4_address: String, #[serde(rename = "IPv6Address")] pub ipv6_address: String, } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] pub struct NetworkCreateInfo { pub id: String, pub warning: String, }