pub mod builder;
pub mod errors;
pub mod read;
pub mod rep;
pub mod transport;
pub mod tty;
mod tarball;
pub use crate::{
builder::{
BuildOptions, ContainerConnectionOptions, ContainerFilter, ContainerListOptions,
ContainerOptions, EventsOptions, ExecContainerOptions, ImageFilter, ImageListOptions,
LogsOptions, NetworkCreateOptions, NetworkListOptions, PullOptions, RegistryAuth,
RmContainerOptions, TagOptions, VolumeCreateOptions,
},
errors::Error,
};
use crate::{
read::StreamReader,
rep::{
Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, Event, Exit,
History, Image as ImageRep, ImageDetails, Info, NetworkCreateInfo,
NetworkDetails as NetworkInfo, SearchResult, Stats, Status, Top, Version,
Volume as VolumeRep, VolumeCreateInfo, Volumes as VolumesRep,
},
transport::{tar, Transport},
tty::TtyDecoder,
};
use futures::{future::Either, Future, IntoFuture, Stream};
use hyper::{client::HttpConnector, Body, Client, Method, Uri};
#[cfg(feature = "tls")]
use hyper_openssl::HttpsConnector;
#[cfg(feature = "unix-socket")]
use hyperlocal::UnixConnector;
use mime::Mime;
#[cfg(feature = "tls")]
use openssl::ssl::{SslConnector, SslFiletype, SslMethod};
use serde_json::Value;
use std::{borrow::Cow, env, io::Read, iter, path::Path, time::Duration};
use tokio_codec::{FramedRead, LinesCodec};
use url::form_urlencoded;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone)]
pub struct Docker {
transport: Transport,
}
pub struct Image<'a, 'b> {
docker: &'a Docker,
name: Cow<'b, str>,
}
impl<'a, 'b> Image<'a, 'b> {
pub fn new<S>(
docker: &'a Docker,
name: S,
) -> Image<'a, 'b>
where
S: Into<Cow<'b, str>>,
{
Image {
docker,
name: name.into(),
}
}
pub fn inspect(&self) -> impl Future<Item = ImageDetails, Error = Error> {
self.docker
.get_json(&format!("/images/{}/json", self.name)[..])
}
pub fn history(&self) -> impl Future<Item = Vec<History>, Error = Error> {
self.docker
.get_json(&format!("/images/{}/history", self.name)[..])
}
pub fn delete(&self) -> impl Future<Item = Vec<Status>, Error = Error> {
self.docker
.delete_json::<Vec<Status>>(&format!("/images/{}", self.name)[..])
}
pub fn export(&self) -> impl Stream<Item = Vec<u8>, Error = Error> {
self.docker
.stream_get(&format!("/images/{}/get", self.name)[..])
.map(|c| c.to_vec())
}
pub fn tag(
&self,
opts: &TagOptions,
) -> impl Future<Item = (), Error = Error> {
let mut path = vec![format!("/images/{}/tag", self.name)];
if let Some(query) = opts.serialize() {
path.push(query)
}
self.docker.post::<Body>(&path.join("?"), None).map(|_| ())
}
}
pub struct Images<'a> {
docker: &'a Docker,
}
impl<'a> Images<'a> {
pub fn new(docker: &'a Docker) -> Images<'a> {
Images { docker }
}
pub fn build(
&self,
opts: &BuildOptions,
) -> impl Stream<Item = Value, Error = Error> {
let mut path = vec!["/build".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query)
}
let mut bytes = vec![];
match tarball::dir(&mut bytes, &opts.path[..]) {
Ok(_) => Box::new(
self.docker
.stream_post(
&path.join("?"),
Some((Body::from(bytes), tar())),
None::<iter::Empty<_>>,
)
.map(|r| {
futures::stream::iter_result(
serde_json::Deserializer::from_slice(&r[..])
.into_iter::<Value>()
.collect::<Vec<_>>(),
)
.map_err(Error::from)
})
.flatten(),
) as Box<dyn Stream<Item = Value, Error = Error> + Send>,
Err(e) => Box::new(futures::future::err(Error::IO(e)).into_stream())
as Box<dyn Stream<Item = Value, Error = Error> + Send>,
}
}
pub fn list(
&self,
opts: &ImageListOptions,
) -> impl Future<Item = Vec<ImageRep>, Error = Error> {
let mut path = vec!["/images/json".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query);
}
self.docker.get_json::<Vec<ImageRep>>(&path.join("?"))
}
pub fn get<'b>(
&self,
name: &'b str,
) -> Image<'a, 'b> {
Image::new(self.docker, name)
}
pub fn search(
&self,
term: &str,
) -> impl Future<Item = Vec<SearchResult>, Error = Error> {
let query = form_urlencoded::Serializer::new(String::new())
.append_pair("term", term)
.finish();
self.docker
.get_json::<Vec<SearchResult>>(&format!("/images/search?{}", query)[..])
}
pub fn pull(
&self,
opts: &PullOptions,
) -> impl Stream<Item = Value, Error = Error> {
let mut path = vec!["/images/create".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query);
}
let headers = opts
.auth_header()
.map(|a| iter::once(("X-Registry-Auth", a)));
self.docker
.stream_post::<Body, _>(&path.join("?"), None, headers)
.map(|r| {
futures::stream::iter_result(
serde_json::Deserializer::from_slice(&r[..])
.into_iter::<Value>()
.collect::<Vec<_>>(),
)
.map_err(Error::from)
})
.flatten()
}
pub fn export(
&self,
names: Vec<&str>,
) -> impl Stream<Item = Vec<u8>, Error = Error> {
let params = names.iter().map(|n| ("names", *n));
let query = form_urlencoded::Serializer::new(String::new())
.extend_pairs(params)
.finish();
self.docker
.stream_get(&format!("/images/get?{}", query)[..])
.map(|c| c.to_vec())
}
pub fn import(
self,
mut tarball: Box<dyn Read>,
) -> impl Stream<Item = Value, Error = Error> {
let mut bytes = Vec::new();
match tarball.read_to_end(&mut bytes) {
Ok(_) => Box::new(
self.docker
.stream_post(
"/images/load",
Some((Body::from(bytes), tar())),
None::<iter::Empty<_>>,
)
.and_then(|bytes| {
serde_json::from_slice::<'_, Value>(&bytes[..])
.map_err(Error::from)
.into_future()
}),
) as Box<dyn Stream<Item = Value, Error = Error> + Send>,
Err(e) => Box::new(futures::future::err(Error::IO(e)).into_stream())
as Box<dyn Stream<Item = Value, Error = Error> + Send>,
}
}
}
pub struct Container<'a, 'b> {
docker: &'a Docker,
id: Cow<'b, str>,
}
impl<'a, 'b> Container<'a, 'b> {
pub fn new<S>(
docker: &'a Docker,
id: S,
) -> Container<'a, 'b>
where
S: Into<Cow<'b, str>>,
{
Container {
docker,
id: id.into(),
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn inspect(&self) -> impl Future<Item = ContainerDetails, Error = Error> {
self.docker
.get_json::<ContainerDetails>(&format!("/containers/{}/json", self.id)[..])
}
pub fn top(
&self,
psargs: Option<&str>,
) -> impl Future<Item = Top, Error = Error> {
let mut path = vec![format!("/containers/{}/top", self.id)];
if let Some(ref args) = psargs {
let encoded = form_urlencoded::Serializer::new(String::new())
.append_pair("ps_args", args)
.finish();
path.push(encoded)
}
self.docker.get_json(&path.join("?"))
}
pub fn logs(
&self,
opts: &LogsOptions,
) -> impl Stream<Item = tty::Chunk, Error = Error> {
let mut path = vec![format!("/containers/{}/logs", self.id)];
if let Some(query) = opts.serialize() {
path.push(query)
}
let decoder = TtyDecoder::new();
let chunk_stream = StreamReader::new(self.docker.stream_get(&path.join("?")));
FramedRead::new(chunk_stream, decoder)
}
pub fn attach(&self) -> impl Future<Item = tty::Multiplexed, Error = Error> {
self.docker.stream_post_upgrade_multiplexed::<Body>(
&format!(
"/containers/{}/attach?stream=1&stdout=1&stderr=1&stdin=1",
self.id
),
None,
)
}
pub fn attach_blocking(&self) -> Result<tty::MultiplexedBlocking> {
self.attach().map(|s| s.wait()).wait()
}
pub fn changes(&self) -> impl Future<Item = Vec<Change>, Error = Error> {
self.docker
.get_json::<Vec<Change>>(&format!("/containers/{}/changes", self.id)[..])
}
pub fn export(&self) -> impl Stream<Item = Vec<u8>, Error = Error> {
self.docker
.stream_get(&format!("/containers/{}/export", self.id)[..])
.map(|c| c.to_vec())
}
pub fn stats(&self) -> impl Stream<Item = Stats, Error = Error> {
let decoder = LinesCodec::new();
let stream_of_chunks = StreamReader::new(
self.docker
.stream_get(&format!("/containers/{}/stats", self.id)[..]),
);
FramedRead::new(stream_of_chunks, decoder)
.map_err(Error::IO)
.and_then(|s| {
serde_json::from_str::<Stats>(&s)
.map_err(Error::SerdeJsonError)
.into_future()
})
}
pub fn start(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.post::<Body>(&format!("/containers/{}/start", self.id)[..], None)
.map(|_| ())
}
pub fn stop(
&self,
wait: Option<Duration>,
) -> impl Future<Item = (), Error = Error> {
let mut path = vec![format!("/containers/{}/stop", self.id)];
if let Some(w) = wait {
let encoded = form_urlencoded::Serializer::new(String::new())
.append_pair("t", &w.as_secs().to_string())
.finish();
path.push(encoded)
}
self.docker.post::<Body>(&path.join("?"), None).map(|_| ())
}
pub fn restart(
&self,
wait: Option<Duration>,
) -> impl Future<Item = (), Error = Error> {
let mut path = vec![format!("/containers/{}/restart", self.id)];
if let Some(w) = wait {
let encoded = form_urlencoded::Serializer::new(String::new())
.append_pair("t", &w.as_secs().to_string())
.finish();
path.push(encoded)
}
self.docker.post::<Body>(&path.join("?"), None).map(|_| ())
}
pub fn kill(
&self,
signal: Option<&str>,
) -> impl Future<Item = (), Error = Error> {
let mut path = vec![format!("/containers/{}/kill", self.id)];
if let Some(sig) = signal {
let encoded = form_urlencoded::Serializer::new(String::new())
.append_pair("signal", &sig.to_owned())
.finish();
path.push(encoded)
}
self.docker.post::<Body>(&path.join("?"), None).map(|_| ())
}
pub fn rename(
&self,
name: &str,
) -> impl Future<Item = (), Error = Error> {
let query = form_urlencoded::Serializer::new(String::new())
.append_pair("name", name)
.finish();
self.docker
.post::<Body>(
&format!("/containers/{}/rename?{}", self.id, query)[..],
None,
)
.map(|_| ())
}
pub fn pause(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.post::<Body>(&format!("/containers/{}/pause", self.id)[..], None)
.map(|_| ())
}
pub fn unpause(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.post::<Body>(&format!("/containers/{}/unpause", self.id)[..], None)
.map(|_| ())
}
pub fn wait(&self) -> impl Future<Item = Exit, Error = Error> {
self.docker
.post_json::<Body, Exit>(&format!("/containers/{}/wait", self.id)[..], None)
}
pub fn delete(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.delete(&format!("/containers/{}", self.id)[..])
.map(|_| ())
}
pub fn remove(
&self,
opts: RmContainerOptions,
) -> impl Future<Item = (), Error = Error> {
let mut path = vec![format!("/containers/{}", self.id)];
if let Some(query) = opts.serialize() {
path.push(query)
}
self.docker.delete(&path.join("?")).map(|_| ())
}
pub fn exec(
&self,
opts: &ExecContainerOptions,
) -> impl Stream<Item = tty::Chunk, Error = Error> {
let data = opts.serialize().unwrap();
let bytes = data.into_bytes();
let docker2 = self.docker.clone();
self.docker
.post(
&format!("/containers/{}/exec", self.id)[..],
Some((bytes, mime::APPLICATION_JSON)),
)
.map(move |res| {
let data = "{}";
let bytes = data.as_bytes();
let id = serde_json::from_str::<Value>(res.as_str())
.ok()
.and_then(|v| {
v.as_object()
.and_then(|v| v.get("Id"))
.and_then(|v| v.as_str().map(|v| v.to_string()))
})
.unwrap();
let decoder = TtyDecoder::new();
let chunk_stream = StreamReader::new(docker2.stream_post(
&format!("/exec/{}/start", id)[..],
Some((bytes, mime::APPLICATION_JSON)),
None::<iter::Empty<_>>,
));
FramedRead::new(chunk_stream, decoder)
})
.flatten_stream()
}
pub fn copy_from(
&self,
path: &Path,
) -> impl Stream<Item = Vec<u8>, Error = Error> {
let path_arg = form_urlencoded::Serializer::new(String::new())
.append_pair("path", &path.to_string_lossy())
.finish();
self.docker
.stream_get(&format!("/containers/{}/archive?{}", self.id, path_arg))
.map(|c| c.to_vec())
}
pub fn copy_file_into<P: AsRef<Path>>(
&self,
path: P,
bytes: &[u8],
) -> impl Future<Item = (), Error = Error> {
let path = path.as_ref();
let mut ar = tar::Builder::new(Vec::new());
let mut header = tar::Header::new_gnu();
header.set_size(bytes.len() as u64);
header.set_mode(0o0644);
ar.append_data(
&mut header,
path.to_path_buf()
.iter()
.skip(1)
.collect::<std::path::PathBuf>(),
bytes,
)
.unwrap();
let data = ar.into_inner().unwrap();
let body = Some((data, "application/x-tar".parse::<Mime>().unwrap()));
let path_arg = form_urlencoded::Serializer::new(String::new())
.append_pair("path", "/")
.finish();
self.docker
.put(
&format!("/containers/{}/archive?{}", self.id, path_arg),
body,
)
.map(|_| ())
}
}
pub struct Containers<'a> {
docker: &'a Docker,
}
impl<'a> Containers<'a> {
pub fn new(docker: &'a Docker) -> Containers<'a> {
Containers { docker }
}
pub fn list(
&self,
opts: &ContainerListOptions,
) -> impl Future<Item = Vec<ContainerRep>, Error = Error> {
let mut path = vec!["/containers/json".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query)
}
self.docker.get_json::<Vec<ContainerRep>>(&path.join("?"))
}
pub fn get<'b>(
&self,
name: &'b str,
) -> Container<'a, 'b> {
Container::new(self.docker, name)
}
pub fn create(
&self,
opts: &ContainerOptions,
) -> impl Future<Item = ContainerCreateInfo, Error = Error> {
let data = match opts.serialize() {
Ok(data) => data,
Err(e) => return Either::A(futures::future::err(e)),
};
let bytes = data.into_bytes();
let mut path = vec!["/containers/create".to_owned()];
if let Some(ref name) = opts.name {
path.push(
form_urlencoded::Serializer::new(String::new())
.append_pair("name", name)
.finish(),
);
}
Either::B(
self.docker
.post_json(&path.join("?"), Some((bytes, mime::APPLICATION_JSON))),
)
}
}
pub struct Networks<'a> {
docker: &'a Docker,
}
impl<'a> Networks<'a> {
pub fn new(docker: &'a Docker) -> Networks<'a> {
Networks { docker }
}
pub fn list(
&self,
opts: &NetworkListOptions,
) -> impl Future<Item = Vec<NetworkInfo>, Error = Error> {
let mut path = vec!["/networks".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query);
}
self.docker.get_json(&path.join("?"))
}
pub fn get<'b>(
&self,
id: &'b str,
) -> Network<'a, 'b> {
Network::new(self.docker, id)
}
pub fn create(
&self,
opts: &NetworkCreateOptions,
) -> impl Future<Item = NetworkCreateInfo, Error = Error> {
let data = match opts.serialize() {
Ok(data) => data,
Err(e) => return Either::A(futures::future::err(e)),
};
let bytes = data.into_bytes();
let path = vec!["/networks/create".to_owned()];
Either::B(
self.docker
.post_json(&path.join("?"), Some((bytes, mime::APPLICATION_JSON))),
)
}
}
pub struct Network<'a, 'b> {
docker: &'a Docker,
id: Cow<'b, str>,
}
impl<'a, 'b> Network<'a, 'b> {
pub fn new<S>(
docker: &'a Docker,
id: S,
) -> Network<'a, 'b>
where
S: Into<Cow<'b, str>>,
{
Network {
docker,
id: id.into(),
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn inspect(&self) -> impl Future<Item = NetworkInfo, Error = Error> {
self.docker.get_json(&format!("/networks/{}", self.id)[..])
}
pub fn delete(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.delete(&format!("/networks/{}", self.id)[..])
.map(|_| ())
}
pub fn connect(
&self,
opts: &ContainerConnectionOptions,
) -> impl Future<Item = (), Error = Error> {
self.do_connection("connect", opts)
}
pub fn disconnect(
&self,
opts: &ContainerConnectionOptions,
) -> impl Future<Item = (), Error = Error> {
self.do_connection("disconnect", opts)
}
fn do_connection(
&self,
segment: &str,
opts: &ContainerConnectionOptions,
) -> impl Future<Item = (), Error = Error> {
let data = match opts.serialize() {
Ok(data) => data,
Err(e) => return Either::A(futures::future::err(e)),
};
let bytes = data.into_bytes();
Either::B(
self.docker
.post(
&format!("/networks/{}/{}", self.id, segment)[..],
Some((bytes, mime::APPLICATION_JSON)),
)
.map(|_| ()),
)
}
}
pub struct Volumes<'a> {
docker: &'a Docker,
}
impl<'a> Volumes<'a> {
pub fn new(docker: &'a Docker) -> Volumes<'a> {
Volumes { docker }
}
pub fn create(
&self,
opts: &VolumeCreateOptions,
) -> impl Future<Item = VolumeCreateInfo, Error = Error> {
let data = match opts.serialize() {
Ok(data) => data,
Err(e) => return Either::A(futures::future::err(e)),
};
let bytes = data.into_bytes();
let path = vec!["/volumes/create".to_owned()];
Either::B(
self.docker
.post_json(&path.join("?"), Some((bytes, mime::APPLICATION_JSON))),
)
}
pub fn list(&self) -> impl Future<Item = Vec<VolumeRep>, Error = Error> {
let path = vec!["/volumes".to_owned()];
self.docker
.get_json::<VolumesRep>(&path.join("?"))
.map(|volumes: VolumesRep| match volumes.volumes {
Some(volumes) => volumes.clone(),
None => vec![],
})
}
pub fn get<'b>(
&self,
name: &'b str,
) -> Volume<'a, 'b> {
Volume::new(self.docker, name)
}
}
pub struct Volume<'a, 'b> {
docker: &'a Docker,
name: Cow<'b, str>,
}
impl<'a, 'b> Volume<'a, 'b> {
pub fn new<S>(
docker: &'a Docker,
name: S,
) -> Volume<'a, 'b>
where
S: Into<Cow<'b, str>>,
{
Volume {
docker,
name: name.into(),
}
}
pub fn delete(&self) -> impl Future<Item = (), Error = Error> {
self.docker
.delete(&format!("/volumes/{}", self.name)[..])
.map(|_| ())
}
}
fn get_http_connector() -> HttpConnector {
let mut http = HttpConnector::new(1);
http.enforce_http(false);
http
}
#[cfg(feature = "tls")]
fn get_docker_for_tcp(tcp_host_str: String) -> Docker {
let http = get_http_connector();
if let Ok(ref certs) = env::var("DOCKER_CERT_PATH") {
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
connector.set_cipher_list("DEFAULT").unwrap();
let cert = &format!("{}/cert.pem", certs);
let key = &format!("{}/key.pem", certs);
connector
.set_certificate_file(&Path::new(cert), SslFiletype::PEM)
.unwrap();
connector
.set_private_key_file(&Path::new(key), SslFiletype::PEM)
.unwrap();
if env::var("DOCKER_TLS_VERIFY").is_ok() {
let ca = &format!("{}/ca.pem", certs);
connector.set_ca_file(&Path::new(ca)).unwrap();
}
Docker {
transport: Transport::EncryptedTcp {
client: Client::builder()
.build(HttpsConnector::with_connector(http, connector).unwrap()),
host: tcp_host_str,
},
}
} else {
Docker {
transport: Transport::Tcp {
client: Client::builder().build(http),
host: tcp_host_str,
},
}
}
}
#[cfg(not(feature = "tls"))]
fn get_docker_for_tcp(tcp_host_str: String) -> Docker {
let http = get_http_connector();
Docker {
transport: Transport::Tcp {
client: Client::builder().build(http),
host: tcp_host_str,
},
}
}
impl Docker {
pub fn new() -> Docker {
match env::var("DOCKER_HOST").ok() {
Some(host) => {
let host = host.parse().expect("invalid url");
Docker::host(host)
}
#[cfg(feature = "unix-socket")]
None => Docker::unix("/var/run/docker.sock"),
#[cfg(not(feature = "unix-socket"))]
None => panic!("Unix socket support is disabled"),
}
}
#[cfg(feature = "unix-socket")]
pub fn unix<S>(socket_path: S) -> Docker
where
S: Into<String>,
{
Docker {
transport: Transport::Unix {
client: Client::builder().keep_alive(false).build(UnixConnector),
path: socket_path.into(),
},
}
}
pub fn host(host: Uri) -> Docker {
let tcp_host_str = format!(
"{}://{}:{}",
host.scheme_part().map(|s| s.as_str()).unwrap(),
host.host().unwrap().to_owned(),
host.port_u16().unwrap_or(80)
);
match host.scheme_part().map(|s| s.as_str()) {
#[cfg(feature = "unix-socket")]
Some("unix") => Docker {
transport: Transport::Unix {
client: Client::builder().build(UnixConnector),
path: host.path().to_owned(),
},
},
#[cfg(not(feature = "unix-socket"))]
Some("unix") => panic!("Unix socket support is disabled"),
_ => get_docker_for_tcp(tcp_host_str),
}
}
pub fn images(&self) -> Images {
Images::new(self)
}
pub fn containers(&self) -> Containers {
Containers::new(self)
}
pub fn networks(&self) -> Networks {
Networks::new(self)
}
pub fn volumes(&self) -> Volumes {
Volumes::new(self)
}
pub fn version(&self) -> impl Future<Item = Version, Error = Error> {
self.get_json("/version")
}
pub fn info(&self) -> impl Future<Item = Info, Error = Error> {
self.get_json("/info")
}
pub fn ping(&self) -> impl Future<Item = String, Error = Error> {
self.get("/_ping")
}
pub fn events(
&self,
opts: &EventsOptions,
) -> impl Stream<Item = Event, Error = Error> {
let mut path = vec!["/events".to_owned()];
if let Some(query) = opts.serialize() {
path.push(query);
}
let stream_of_chunks = self.stream_get(&path.join("?")[..]);
let reader = StreamReader::new(stream_of_chunks);
FramedRead::new(reader, LinesCodec::new())
.map_err(Error::IO)
.and_then(|line| serde_json::from_str::<Event>(&line).map_err(Error::from))
}
fn get(
&self,
endpoint: &str,
) -> impl Future<Item = String, Error = Error> {
self.transport.request::<Body>(Method::GET, endpoint, None)
}
fn get_json<T: serde::de::DeserializeOwned>(
&self,
endpoint: &str,
) -> impl Future<Item = T, Error = Error> {
self.transport
.request::<Body>(Method::GET, endpoint, None)
.and_then(|v| {
serde_json::from_str::<T>(&v)
.map_err(Error::SerdeJsonError)
.into_future()
})
}
fn post<B>(
&self,
endpoint: &str,
body: Option<(B, Mime)>,
) -> impl Future<Item = String, Error = Error>
where
B: Into<Body>,
{
self.transport.request(Method::POST, endpoint, body)
}
fn put<B>(
&self,
endpoint: &str,
body: Option<(B, Mime)>,
) -> impl Future<Item = String, Error = Error>
where
B: Into<Body>,
{
self.transport.request(Method::PUT, endpoint, body)
}
fn post_json<B, T>(
&self,
endpoint: &str,
body: Option<(B, Mime)>,
) -> impl Future<Item = T, Error = Error>
where
B: Into<Body>,
T: serde::de::DeserializeOwned,
{
self.transport
.request(Method::POST, endpoint, body)
.and_then(|v| {
serde_json::from_str::<T>(&v)
.map_err(Error::SerdeJsonError)
.into_future()
})
}
fn delete(
&self,
endpoint: &str,
) -> impl Future<Item = String, Error = Error> {
self.transport
.request::<Body>(Method::DELETE, endpoint, None)
}
fn delete_json<T: serde::de::DeserializeOwned>(
&self,
endpoint: &str,
) -> impl Future<Item = T, Error = Error> {
self.transport
.request::<Body>(Method::DELETE, endpoint, None)
.and_then(|v| {
serde_json::from_str::<T>(&v)
.map_err(Error::SerdeJsonError)
.into_future()
})
}
fn stream_post<B, H>(
&self,
endpoint: &str,
body: Option<(B, Mime)>,
headers: Option<H>,
) -> impl Stream<Item = hyper::Chunk, Error = Error>
where
B: Into<Body>,
H: IntoIterator<Item = (&'static str, String)>,
{
self.transport
.stream_chunks(Method::POST, endpoint, body, headers)
}
fn stream_get(
&self,
endpoint: &str,
) -> impl Stream<Item = hyper::Chunk, Error = Error> {
self.transport
.stream_chunks::<Body, iter::Empty<_>>(Method::GET, endpoint, None, None)
}
fn stream_post_upgrade_multiplexed<B>(
&self,
endpoint: &str,
body: Option<(B, Mime)>,
) -> impl Future<Item = tty::Multiplexed, Error = Error>
where
B: Into<Body> + 'static,
{
self.transport
.stream_upgrade_multiplexed(Method::POST, endpoint, body)
}
}
impl Default for Docker {
fn default() -> Self {
Self::new()
}
}