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},
};
pub struct Networks<'docker> {
docker: &'docker Docker,
}
impl<'docker> Networks<'docker> {
pub fn new(docker: &'docker Docker) -> Self {
Networks { docker }
}
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
}
pub fn get<S>(
&self,
id: S,
) -> Network<'docker>
where
S: Into<String>,
{
Network::new(self.docker, id)
}
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
}
}
pub struct Network<'docker> {
docker: &'docker Docker,
id: String,
}
impl<'docker> Network<'docker> {
pub fn new<S>(
docker: &'docker Docker,
id: S,
) -> Self
where
S: Into<String>,
{
Network {
docker,
id: id.into(),
}
}
pub fn id(&self) -> &str {
&self.id
}
pub async fn inspect(&self) -> Result<NetworkDetails> {
self.docker
.get_json(&format!("/networks/{}", self.id)[..])
.await
}
pub async fn delete(&self) -> Result<()> {
self.docker
.delete(&format!("/networks/{}", self.id)[..])
.await?;
Ok(())
}
pub async fn connect(
&self,
opts: &ContainerConnectionOptions,
) -> Result<()> {
self.do_connection("connect", opts).await
}
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(())
}
}
#[derive(Default, Debug)]
pub struct NetworkListOptions {
params: HashMap<&'static str, String>,
}
impl NetworkListOptions {
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(),
)
}
}
}
#[derive(Serialize, Debug)]
pub struct NetworkCreateOptions {
params: HashMap<&'static str, Value>,
}
impl NetworkCreateOptions {
pub fn builder(name: &str) -> NetworkCreateOptionsBuilder {
NetworkCreateOptionsBuilder::new(name)
}
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(),
}
}
}
#[derive(Serialize, Debug)]
pub struct ContainerConnectionOptions {
params: HashMap<&'static str, Value>,
}
impl ContainerConnectionOptions {
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);
}
}
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,
}