diff options
Diffstat (limited to 'src/network.rs')
-rw-r--r-- | src/network.rs | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..6dd4eb3 --- /dev/null +++ b/src/network.rs @@ -0,0 +1,412 @@ +//! Create and manage user-defined networks that containers can be attached to. +//! +//! API Reference: <https://docs.docker.com/engine/api/v1.41/#tag/Network> + +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: <https://docs.docker.com/engine/api/v1.41/#tag/Network> +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: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkList> + pub async fn list( + &self, + opts: &NetworkListOptions, + ) -> Result<Vec<NetworkDetails>> { + 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<S>( + &self, + id: S, + ) -> Network<'docker> + where + S: Into<String>, + { + Network::new(self.docker, id) + } + + /// Create a new Network instance + /// + /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkCreate> + pub async fn create( + &self, + opts: &NetworkCreateOptions, + ) -> Result<NetworkCreateInfo> { + 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<S>( + docker: &'docker Docker, + id: S, + ) -> Self + where + S: Into<String>, + { + 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: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkInspect> + pub async fn inspect(&self) -> Result<NetworkDetails> { + self.docker + .get_json(&format!("/networks/{}", self.id)[..]) + .await + } + + /// Delete the network instance + /// + /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkDelete> + pub async fn delete(&self) -> Result<()> { + self.docker + .delete(&format!("/networks/{}", self.id)[..]) + .await?; + Ok(()) + } + + /// Connect container to network + /// + /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkConnect> + pub async fn connect( + &self, + opts: &ContainerConnectionOptions, + ) -> Result<()> { + self.do_connection("connect", opts).await + } + + /// Disconnect container to network + /// + /// API Reference: <https://docs.docker.com/engine/api/v1.41/#operation/NetworkDisconnect> + 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<String> { + 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<String> { + serde_json::to_string(&self.params).map_err(Error::from) + } + + pub fn parse_from<'a, K, V>( + &self, + params: &'a HashMap<K, V>, + body: &mut serde_json::Map<String, Value>, + ) where + &'a HashMap<K, V>: 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<String, String>, + ) -> &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<String> { + serde_json::to_string(&self.params).map_err(Error::from) + } + + pub fn parse_from<'a, K, V>( + &self, + params: &'a HashMap<K, V>, + body: &mut BTreeMap<String, Value>, + ) where + &'a HashMap<K, V>: 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<String, Option<Vec<HashMap<String, String>>>>; + +#[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<PortDescription>, + pub networks: HashMap<String, NetworkEntry>, +} + +#[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<Vec<String>>, + pub aliases: Option<Vec<String>>, + #[serde(rename = "IPAMConfig")] + pub ipam_config: Option<EndpointIPAMConfig>, +} + +#[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<String>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct Ipam { + pub driver: String, + pub config: Vec<HashMap<String, String>>, + pub options: Option<HashMap<String, String>>, +} + +#[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<String, NetworkContainerDetails>, + pub options: Option<HashMap<String, String>>, + pub labels: Option<HashMap<String, String>>, +} + +#[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, +} |