From 5af822c5691a772cad36bd9a69e94419b03d4511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20K=C4=99pka?= <46892771+wojciechkepka@users.noreply.github.com> Date: Tue, 9 Feb 2021 01:22:26 +0100 Subject: Add services api (#263) * Add initial Services models * Add initial Services controllers * Add ServicesListOptions * Rename ServicesList -> ServiceList * Fix some optional fields on ServiceRep * Add Service::inspect * Add Service::delete * Add Service::logs * Rename example logs -> containerlogs * Add ServiceOptions, ServiceCreateInfo, fix typo * Add a way to pass headers to request, add Payload and Headers for easier types * Add Service::create * Add examples * Fix fmt * Fix example --- src/lib.rs | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 149 insertions(+), 11 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index 5d77b83..ac09a41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,7 +30,8 @@ pub use crate::{ BuildOptions, ContainerConnectionOptions, ContainerFilter, ContainerListOptions, ContainerOptions, EventsOptions, ExecContainerOptions, ExecResizeOptions, ImageFilter, ImageListOptions, LogsOptions, NetworkCreateOptions, NetworkListOptions, PullOptions, - RegistryAuth, RmContainerOptions, TagOptions, VolumeCreateOptions, + RegistryAuth, RmContainerOptions, ServiceFilter, ServiceListOptions, ServiceOptions, + TagOptions, VolumeCreateOptions, }, errors::Error, }; @@ -38,10 +39,11 @@ use crate::{ rep::{ Change, Container as ContainerRep, ContainerCreateInfo, ContainerDetails, Event, ExecDetails, Exit, History, Image as ImageRep, ImageDetails, Info, NetworkCreateInfo, - NetworkDetails as NetworkInfo, SearchResult, Stats, Status, Top, Version, - Volume as VolumeRep, VolumeCreateInfo, Volumes as VolumesRep, + NetworkDetails as NetworkInfo, SearchResult, ServiceCreateInfo, ServiceDetails, + Services as ServicesRep, Stats, Status, Top, Version, Volume as VolumeRep, + VolumeCreateInfo, Volumes as VolumesRep, }, - transport::{tar, Transport}, + transport::{tar, Headers, Payload, Transport}, tty::Multiplexer as TtyMultiPlexer, }; use futures_util::{ @@ -983,6 +985,111 @@ impl<'a> Volume<'a> { } } +/// Interface for docker services +pub struct Services<'a> { + docker: &'a Docker, +} + +impl<'a> Services<'a> { + /// Exports an interface for interacting with docker services + pub fn new(docker: &Docker) -> Services { + Services { docker } + } + + /// Lists the docker services on the current docker host + pub async fn list( + &self, + opts: &ServiceListOptions, + ) -> Result { + let mut path = vec!["/services".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 for a named service + pub fn get( + &self, + name: &str, + ) -> Service { + Service::new(self.docker, name) + } +} + +/// Interface for accessing and manipulating a named docker volume +pub struct Service<'a> { + docker: &'a Docker, + name: String, +} + +impl<'a> Service<'a> { + /// Exports an interface for operations that may be performed against a named service + pub fn new( + docker: &Docker, + name: S, + ) -> Service + where + S: Into, + { + Service { + docker, + name: name.into(), + } + } + + /// Creates a new service from ServiceOptions + pub async fn create( + &self, + opts: &ServiceOptions, + ) -> Result { + let body: Body = opts.serialize()?.into(); + let path = vec!["/service/create".to_owned()]; + + let headers = opts + .auth_header() + .map(|a| iter::once(("X-Registry-Auth", a))); + + self.docker + .post_json_headers( + &path.join("?"), + Some((body, mime::APPLICATION_JSON)), + headers, + ) + .await + } + + /// Inspects a named service's details + pub async fn inspect(&self) -> Result { + self.docker + .get_json(&format!("/services/{}", self.name)[..]) + .await + } + + /// Deletes a service + pub async fn delete(&self) -> Result<()> { + self.docker + .delete_json(&format!("/services/{}", self.name)[..]) + .await + } + + /// Returns a stream of logs from a service + pub fn logs( + &self, + opts: &LogsOptions, + ) -> impl Stream> + Unpin + 'a { + let mut path = vec![format!("/services/{}/logs", self.name)]; + if let Some(query) = opts.serialize() { + path.push(query) + } + + let stream = Box::pin(self.docker.stream_get(path.join("?"))); + + Box::pin(tty::decode(stream)) + } +} + fn get_http_connector() -> HttpConnector { let mut http = HttpConnector::new(); http.enforce_http(false); @@ -1122,6 +1229,11 @@ impl Docker { Containers::new(self) } + /// Exports an interface for interacting with docker services + pub fn services(&self) -> Services { + Services::new(self) + } + pub fn networks(&self) -> Networks { Networks::new(self) } @@ -1180,7 +1292,7 @@ impl Docker { endpoint: &str, ) -> Result { self.transport - .request(Method::GET, endpoint, Option::<(Body, Mime)>::None) + .request(Method::GET, endpoint, Payload::None, Headers::None) .await } @@ -1190,7 +1302,7 @@ impl Docker { ) -> Result { let raw_string = self .transport - .request(Method::GET, endpoint, Option::<(Body, Mime)>::None) + .request(Method::GET, endpoint, Payload::None, Headers::None) .await?; Ok(serde_json::from_str::(&raw_string)?) @@ -1201,7 +1313,9 @@ impl Docker { endpoint: &str, body: Option<(Body, Mime)>, ) -> Result { - self.transport.request(Method::POST, endpoint, body).await + self.transport + .request(Method::POST, endpoint, body, Headers::None) + .await } async fn put( @@ -1209,7 +1323,9 @@ impl Docker { endpoint: &str, body: Option<(Body, Mime)>, ) -> Result { - self.transport.request(Method::PUT, endpoint, body).await + self.transport + .request(Method::PUT, endpoint, body, Headers::None) + .await } async fn post_json( @@ -1221,7 +1337,29 @@ impl Docker { T: serde::de::DeserializeOwned, B: Into, { - let string = self.transport.request(Method::POST, endpoint, body).await?; + let string = self + .transport + .request(Method::POST, endpoint, body, Headers::None) + .await?; + + Ok(serde_json::from_str::(&string)?) + } + + async fn post_json_headers<'a, T, B, H>( + &self, + endpoint: impl AsRef, + body: Option<(B, Mime)>, + headers: Option, + ) -> Result + where + T: serde::de::DeserializeOwned, + B: Into, + H: IntoIterator + 'a, + { + let string = self + .transport + .request(Method::POST, endpoint, body, headers) + .await?; Ok(serde_json::from_str::(&string)?) } @@ -1231,7 +1369,7 @@ impl Docker { endpoint: &str, ) -> Result { self.transport - .request(Method::DELETE, endpoint, Option::<(Body, Mime)>::None) + .request(Method::DELETE, endpoint, Payload::None, Headers::None) .await } @@ -1241,7 +1379,7 @@ impl Docker { ) -> Result { let string = self .transport - .request(Method::DELETE, endpoint, Option::<(Body, Mime)>::None) + .request(Method::DELETE, endpoint, Payload::None, Headers::None) .await?; Ok(serde_json::from_str::(&string)?) -- cgit v1.2.3