diff options
author | Alexander Kirillov <saratovsource@gmail.com> | 2017-04-03 22:52:18 +0400 |
---|---|---|
committer | Alexander Kirillov <saratovsource@gmail.com> | 2017-04-03 22:52:18 +0400 |
commit | ad1153421117b7c92785623b43c44bedda15f369 (patch) | |
tree | 857611c0984c1491254b64c2cb0dbfeee82d4772 | |
parent | 6653b0c4bb79dfa6b11aa786dcc6a2f7ed767452 (diff) |
Add support for docker networks
commit 55008eea85dace74acc625914e11d87d15a46b1e
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 22:47:34 2017 +0400
Some DRY
commit c9173593eb9827b30071cb6e42d439ec3d4c3bb1
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 22:42:35 2017 +0400
Connect container to network
commit 8d68406ef4c69c98e43b7b28923a78e1e9672955
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 22:09:58 2017 +0400
Create docker network
commit 868e2076988c0b16f6d5a200cf12e77f5bffaeab
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 18:49:12 2017 +0400
Delete network
commit 58a08e77e5984847589eeb35bc097c8949752619
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 18:42:28 2017 +0400
Add inspect docker network
commit 9fa4143013aa43aaf73645b19565d6e606489952
Author: Alexander Kirillov <saratovsource@gmail.com>
Date: Mon Apr 3 18:03:02 2017 +0400
Add list networks
-rw-r--r-- | examples/networkconnect.rs | 16 | ||||
-rw-r--r-- | examples/networkcreate.rs | 17 | ||||
-rw-r--r-- | examples/networkdelete.rs | 15 | ||||
-rw-r--r-- | examples/networkdisconnect.rs | 16 | ||||
-rw-r--r-- | examples/networkinspect.rs | 15 | ||||
-rw-r--r-- | examples/networks.rs | 13 | ||||
-rw-r--r-- | src/builder.rs | 161 | ||||
-rw-r--r-- | src/lib.rs | 100 | ||||
-rw-r--r-- | src/rep.rs | 40 |
9 files changed, 391 insertions, 2 deletions
diff --git a/examples/networkconnect.rs b/examples/networkconnect.rs new file mode 100644 index 0000000..165510b --- /dev/null +++ b/examples/networkconnect.rs @@ -0,0 +1,16 @@ +extern crate shiplift; + +use shiplift::{ContainerConnectionOptions, Docker}; +use std::env; + +fn main() { + let docker = Docker::new(); + let networks = docker.networks(); + let container_id = env::args().nth(1).unwrap(); + let network_id = env::args().nth(2).unwrap(); + let info = networks + .get(&network_id) + .connect(&ContainerConnectionOptions::new(&container_id)) + .unwrap(); + println!("{:?}", info); +} diff --git a/examples/networkcreate.rs b/examples/networkcreate.rs new file mode 100644 index 0000000..b78ddd0 --- /dev/null +++ b/examples/networkcreate.rs @@ -0,0 +1,17 @@ +extern crate shiplift; + +use shiplift::{NetworkCreateOptions, Docker}; +use std::env; + +fn main() { + let docker = Docker::new(); + let networks = docker.networks(); + if let Some(network_name) = env::args().nth(1) { + let info = networks.create( + &NetworkCreateOptions::builder(network_name.as_ref()) + .driver("bridge") + .build() + ).unwrap(); + println!("{:?}", info); + } +} diff --git a/examples/networkdelete.rs b/examples/networkdelete.rs new file mode 100644 index 0000000..e67b798 --- /dev/null +++ b/examples/networkdelete.rs @@ -0,0 +1,15 @@ +extern crate shiplift; + +use shiplift::Docker; +use std::env; + +fn main() { + let docker = Docker::new(); + if let Some(id) = env::args().nth(1) { + let status = docker.networks() + .get(&id) + .delete() + .unwrap(); + println!("{:?}", status); + } +} diff --git a/examples/networkdisconnect.rs b/examples/networkdisconnect.rs new file mode 100644 index 0000000..b3e0721 --- /dev/null +++ b/examples/networkdisconnect.rs @@ -0,0 +1,16 @@ +extern crate shiplift; + +use shiplift::{ContainerConnectionOptions, Docker}; +use std::env; + +fn main() { + let docker = Docker::new(); + let networks = docker.networks(); + let container_id = env::args().nth(1).unwrap(); + let network_id = env::args().nth(2).unwrap(); + let info = networks + .get(&network_id) + .disconnect(&ContainerConnectionOptions::new(&container_id)) + .unwrap(); + println!("{:?}", info); +} diff --git a/examples/networkinspect.rs b/examples/networkinspect.rs new file mode 100644 index 0000000..f290f93 --- /dev/null +++ b/examples/networkinspect.rs @@ -0,0 +1,15 @@ +extern crate shiplift; + +use shiplift::Docker; +use std::env; + +fn main() { + let docker = Docker::new(); + if let Some(id) = env::args().nth(1) { + let network = docker.networks() + .get(&id) + .inspect() + .unwrap(); + println!("{:?}", network); + } +} diff --git a/examples/networks.rs b/examples/networks.rs new file mode 100644 index 0000000..18767d1 --- /dev/null +++ b/examples/networks.rs @@ -0,0 +1,13 @@ +extern crate shiplift; +extern crate env_logger; + +use shiplift::Docker; + +fn main() { + env_logger::init().unwrap(); + let docker = Docker::new(); + for c in docker.networks(). + list(&Default::default()).unwrap() { + println!("network -> {:?}", c) + } +} diff --git a/src/builder.rs b/src/builder.rs index c13dca6..244f93d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -767,3 +767,164 @@ impl RmContainerOptionsBuilder { RmContainerOptions { params: self.params.clone() } } } + +/// Options for filtering networks list results +#[derive(Default)] +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::serialize(&self.params)) + } + } +} + +/// Interface for creating new docker network +pub struct NetworkCreateOptions { + pub name: Option<String>, + params: HashMap<&'static str, String>, + params_hash: HashMap<String, Vec<HashMap<String, String>>> +} + +impl ToJson for NetworkCreateOptions { + fn to_json(&self) -> Json { + let mut body: BTreeMap<String, Json> = BTreeMap::new(); + + self.parse_from(&self.params, &mut body); + self.parse_from(&self.params_hash, &mut body); + + body.to_json() + } +} + +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> { + Ok(try!(json::encode(&self.to_json()))) + } + + pub fn parse_from<'a, K, V>(&self, + params: &'a HashMap<K, V>, + body: &mut BTreeMap<String, Json>) + where &'a HashMap<K, V>: IntoIterator, + K: ToString + Eq + Hash, + V: ToJson + { + for (k, v) in params.iter() { + let key = k.to_string(); + let value = v.to_json(); + + body.insert(key, value); + } + } + +} + +#[derive(Default)] +pub struct NetworkCreateOptionsBuilder { + name: Option<String>, + params: HashMap<&'static str, String>, + params_hash: HashMap<String, Vec<HashMap<String, String>>> +} + +impl NetworkCreateOptionsBuilder { + pub fn new(name: &str) -> NetworkCreateOptionsBuilder { + let mut params = HashMap::new(); + let params_hash = HashMap::new(); + + params.insert("Name", name.to_owned()); + NetworkCreateOptionsBuilder { + name: None, + params: params, + params_hash: params_hash, + } + } + + pub fn driver(&mut self, name: &str) -> &mut NetworkCreateOptionsBuilder { + if !name.is_empty() { + self.params.insert("Driver", name.to_owned()); + } + self + } + + pub fn label(&mut self, labels: Vec<HashMap<String, String>>) -> &mut NetworkCreateOptionsBuilder { + for l in labels { + self.params_hash.entry("Labels".to_string()).or_insert(Vec::new()).push(l) + } + self + } + + pub fn build(&self) -> NetworkCreateOptions { + NetworkCreateOptions { + name: self.name.clone(), + params: self.params.clone(), + params_hash: self.params_hash.clone(), + } + } +} + +/// Interface for connect container to network +pub struct ContainerConnectionOptions { + pub Container: Option<String>, + params: HashMap<&'static str, String> +} + +impl ToJson for ContainerConnectionOptions { + fn to_json(&self) -> Json { + let mut body: BTreeMap<String, Json> = BTreeMap::new(); + self.parse_from(&self.params, &mut body); + body.to_json() + } +} + + +impl ContainerConnectionOptions { + /// serialize options as a string. returns None if no options are defined + pub fn serialize(&self) -> Result<String> { + Ok(try!(json::encode(&self.to_json()))) + } + + pub fn parse_from<'a, K, V>(&self, + params: &'a HashMap<K, V>, + body: &mut BTreeMap<String, Json>) + where &'a HashMap<K, V>: IntoIterator, + K: ToString + Eq + Hash, + V: ToJson + { + for (k, v) in params.iter() { + let key = k.to_string(); + let value = v.to_json(); + + body.insert(key, value); + } + } + + pub fn new(container_id: &str) -> ContainerConnectionOptions { + let mut params = HashMap::new(); + params.insert("Container", container_id.to_owned()); + ContainerConnectionOptions { + Container: None, + params: params.clone(), + } + } + + pub fn force(&mut self) -> ContainerConnectionOptions { + self.params.insert("Force", "true".to_owned()); + ContainerConnectionOptions { + Container: None, + params: self.params.clone() + } + } +} @@ -37,8 +37,8 @@ mod tarball; pub use errors::Error; pub use builder::{BuildOptions, ContainerOptions, ContainerListOptions, ContainerFilter, EventsOptions, ImageFilter, ImageListOptions, LogsOptions, - PullOptions, RmContainerOptions, ExecContainerOptions - }; + PullOptions, RmContainerOptions, ExecContainerOptions, + NetworkListOptions, NetworkCreateOptions, ContainerConnectionOptions}; use hyper::{Client, Url}; use hyper::header::ContentType; use hyper::net::{HttpsConnector}; @@ -48,6 +48,7 @@ use hyperlocal::UnixSocketConnector; use openssl::x509::X509_FILETYPE_PEM; use openssl::ssl::{SslMethod, SslConnectorBuilder}; use rep::Image as ImageRep; +use rep::{NetworkDetails as NetworkInfo, NetworkCreateInfo}; use rep::{Output, PullInfo, Change, ContainerCreateInfo, ContainerDetails, Container as ContainerRep, Event, Exit, History, ImageDetails, Info, SearchResult, Stats, Status, Top, Version}; @@ -488,6 +489,97 @@ impl<'a> Containers<'a> { } } +/// Interface for docker network +pub struct Networks<'a> { + docker: &'a Docker, +} + +impl<'a> Networks<'a> { + /// Exports an interface for interacting with docker Networks + pub fn new(docker: &'a Docker) -> Networks<'a> { + Networks { docker: docker } + } + + /// List the docker networks on the current docker host + pub fn list(&self, opts: &NetworkListOptions) -> Result<Vec<NetworkInfo>> { + let mut path = vec!["/networks".to_owned()]; + if let Some(query) = opts.serialize() { + path.push(query); + } + let raw = try!(self.docker.get(&path.join("?"))); + Ok(try!(json::decode::<Vec<NetworkInfo>>(&raw))) + } + + /// Returns a reference to a set of operations available to a specific network instance + pub fn get(&'a self, id: &'a str) -> Network { + Network::new(self.docker, id) + } + + pub fn create(&'a self, opts: &NetworkCreateOptions) -> Result<NetworkCreateInfo> { + let data = try!(opts.serialize()); + let mut bytes = data.as_bytes(); + let path = vec!["/networks/create".to_owned()]; + + let raw = try!(self.docker.post(&path.join("?"), + Some((&mut bytes, ContentType::json())))); + Ok(try!(json::decode::<NetworkCreateInfo>(&raw))) + } + +} + +/// Interface for accessing and manipulating a docker network +pub struct Network<'a, 'b> { + docker: &'a Docker, + id: Cow<'b, str>, +} + +impl<'a, 'b> Network<'a, 'b> { + /// Exports an interface exposing operations against a network instance + pub fn new<S>(docker: &'a Docker, id: S) -> Network<'a, 'b> + where S: Into<Cow<'b, str>> + { + Network { + docker: 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 + pub fn inspect(&self) -> Result<NetworkInfo> { + let raw = try!(self.docker.get(&format!("/networks/{}", self.id)[..])); + Ok(try!(json::decode::<NetworkInfo>(&raw))) + } + + /// Delete the network instance + pub fn delete(&self) -> Result<()> { + self.docker.delete(&format!("/networks/{}", self.id)[..]).map(|_| ()) + } + + /// Connect container to network + pub fn connect(&self, opts: &ContainerConnectionOptions) -> Result<()> { + self.do_connection("connect", opts) + } + + /// Disconnect container to network + pub fn disconnect(&self, opts: &ContainerConnectionOptions) -> Result<()> { + self.do_connection("disconnect", opts) + } + + fn do_connection(&self, segment: &str, opts: &ContainerConnectionOptions) -> Result<()> { + let data = try!(opts.serialize()); + let mut bytes = data.as_bytes(); + + self.docker + .post(&format!("/networks/{}/{}", self.id, segment)[..], + Some((&mut bytes, ContentType::json()))) + .map(|_| ()) + } + +} + // https://docs.docker.com/reference/api/docker_remote_api_v1.17/ impl Docker { /// constructs a new Docker instance for a docker host listening at a url specified by an env var `DOCKER_HOST`, @@ -557,6 +649,10 @@ impl Docker { Containers::new(self) } + pub fn networks<'a>(&'a self) -> Networks { + Networks::new(self) + } + /// Returns version information associated with the docker daemon pub fn version(&self) -> Result<Version> { let raw = try!(self.get("/version")); @@ -204,6 +204,46 @@ pub struct Network { } #[derive(Debug, RustcEncodable, RustcDecodable)] +#[allow(non_snake_case)] +pub struct IPAM { + pub Driver: String, + pub Config: Vec<HashMap<String, String>>, + pub Options: Option<HashMap<String, String>>, +} + +#[derive(Debug, RustcEncodable, RustcDecodable)] +#[allow(non_snake_case)] +pub struct NetworkDetails { + pub Name: String, + pub Id: String, + pub Scope: String, + pub Driver: String, + pub EnableIPv6: bool, + 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(Debug, RustcEncodable, RustcDecodable)] +#[allow(non_snake_case)] +pub struct NetworkContainerDetails { + pub EndpointID: String, + pub MacAddress: String, + pub IPv4Address: String, + pub IPv6Address: String, +} + +#[derive(Debug, RustcEncodable, RustcDecodable)] +#[allow(non_snake_case)] +pub struct NetworkCreateInfo { + pub Id: String, + pub Warning: String, +} + +#[derive(Debug, RustcEncodable, RustcDecodable)] pub struct MemoryStats { pub max_usage: u64, pub usage: u64, |