bollard_next/service.rs
1//! Service API: manage and inspect docker services within a swarm
2
3use crate::docker::BodyType;
4pub use crate::models::*;
5
6use super::Docker;
7use crate::auth::{DockerCredentials, DockerCredentialsHeader};
8use crate::errors::Error;
9use bytes::Bytes;
10use http::header::CONTENT_TYPE;
11use http::request::Builder;
12use http::Method;
13use http_body_util::Full;
14use serde_derive::Serialize;
15
16use std::{collections::HashMap, hash::Hash};
17
18/// Parameters used in the [List Service API](super::Docker::list_services())
19///
20/// ## Examples
21///
22/// ```rust
23/// # use std::collections::HashMap;
24/// # use std::default::Default;
25/// use bollard_next::service::ListServicesOptions;
26///
27/// let mut filters = HashMap::new();
28/// filters.insert("mode", vec!["global"]);
29///
30/// ListServicesOptions{
31/// filters,
32/// ..Default::default()
33/// };
34/// ```
35///
36/// ```rust
37/// # use bollard_next::service::ListServicesOptions;
38/// # use std::default::Default;
39///
40/// let options: ListServicesOptions<&str> = Default::default();
41/// ```
42#[derive(Debug, Clone, Default, PartialEq, Serialize)]
43pub struct ListServicesOptions<T>
44where
45 T: Into<String> + Eq + Hash + serde::ser::Serialize,
46{
47 /// Filters to process on the service list, encoded as JSON. Available filters:
48 /// - `id`=`<ID>` a services's ID
49 /// - `label`=`key` or `label`=`"key=value"` of a service label
50 /// - `mode`=`["replicated"|"global"] a service's scheduling mode
51 /// - `name`=`<name>` a services's name
52 #[serde(serialize_with = "crate::docker::serialize_as_json")]
53 pub filters: HashMap<T, Vec<T>>,
54
55 /// Include service status, with count of running and desired tasks.
56 pub status: bool,
57}
58
59/// Parameters used in the [Inspect Service API](Docker::inspect_service())
60///
61/// ## Examples
62///
63/// ```rust
64/// use bollard_next::service::InspectServiceOptions;
65///
66/// InspectServiceOptions{
67/// insert_defaults: true,
68/// };
69/// ```
70#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
71#[serde(rename_all = "camelCase")]
72pub struct InspectServiceOptions {
73 /// Fill empty fields with default values.
74 pub insert_defaults: bool,
75}
76
77/// Parameters used in the [Update Service API](Docker::update_service())
78///
79/// ## Examples
80///
81/// ```rust
82/// use bollard_next::service::UpdateServiceOptions;
83///
84/// UpdateServiceOptions{
85/// version: 1234,
86/// ..Default::default()
87/// };
88/// ```
89#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize)]
90#[serde(rename_all = "camelCase")]
91pub struct UpdateServiceOptions {
92 /// The version number of the service object being updated. This is required to avoid conflicting writes. This version number should be the value as currently set on the service before the update.
93 pub version: u64,
94 /// If the X-Registry-Auth header is not specified, this parameter indicates whether to use registry authorization credentials from the current or the previous spec.
95 #[serde(serialize_with = "serialize_registry_auth_from")]
96 pub registry_auth_from: bool,
97 /// Set to this parameter to true to cause a server-side rollback to the previous service spec. The supplied spec will be ignored in this case.
98 #[serde(serialize_with = "serialize_rollback")]
99 pub rollback: bool,
100}
101
102#[allow(clippy::trivially_copy_pass_by_ref)]
103pub(crate) fn serialize_registry_auth_from<S>(
104 registry_auth_from: &bool,
105 s: S,
106) -> Result<S::Ok, S::Error>
107where
108 S: serde::Serializer,
109{
110 s.serialize_str(if *registry_auth_from {
111 "previous-spec"
112 } else {
113 "spec"
114 })
115}
116
117#[allow(clippy::trivially_copy_pass_by_ref)]
118fn serialize_rollback<S>(rollback: &bool, s: S) -> Result<S::Ok, S::Error>
119where
120 S: serde::Serializer,
121{
122 s.serialize_str(if *rollback { "previous" } else { "" })
123}
124
125impl Docker {
126 /// ---
127 ///
128 /// # List Services
129 ///
130 /// Returns a list of services.
131 ///
132 /// # Arguments
133 ///
134 /// - Optional [ListServicesOptions](ListServicesOptions) struct.
135 ///
136 /// # Returns
137 ///
138 /// - Vector of [Services](Service), wrapped in a Future.
139 ///
140 /// # Examples
141 ///
142 /// ```rust
143 /// # use bollard_next::Docker;
144 /// # let docker = Docker::connect_with_http_defaults().unwrap();
145 /// use bollard_next::service::ListServicesOptions;
146 ///
147 /// use std::collections::HashMap;
148 /// use std::default::Default;
149 ///
150 /// let mut filters = HashMap::new();
151 /// filters.insert("mode", vec!["global"]);
152 ///
153 /// let options = Some(ListServicesOptions{
154 /// filters,
155 /// ..Default::default()
156 /// });
157 ///
158 /// docker.list_services(options);
159 /// ```
160 pub async fn list_services<T>(
161 &self,
162 options: Option<ListServicesOptions<T>>,
163 ) -> Result<Vec<Service>, Error>
164 where
165 T: Into<String> + Eq + Hash + serde::ser::Serialize,
166 {
167 let url = "/services";
168
169 let req = self.build_request(
170 url,
171 Builder::new().method(hyper::Method::GET),
172 options,
173 Ok(BodyType::Left(Full::new(Bytes::new()))),
174 );
175
176 self.process_into_value(req).await
177 }
178
179 /// ---
180 ///
181 /// # Create Service
182 ///
183 /// Dispatch a new service on the docker swarm
184 ///
185 /// # Arguments
186 ///
187 /// - [ServiceSpec](ServiceSpec) struct.
188 /// - Optional [Docker Credentials](DockerCredentials) struct.
189 ///
190 /// # Returns
191 ///
192 /// - A [Service Create Response](ServiceCreateResponse) struct,
193 /// wrapped in a Future.
194 ///
195 /// # Examples
196 ///
197 /// ```rust
198 /// # use bollard_next::Docker;
199 /// # use std::collections::HashMap;
200 /// # use std::default::Default;
201 /// # let docker = Docker::connect_with_http_defaults().unwrap();
202 /// use bollard_next::service::{
203 /// ServiceSpec,
204 /// ServiceSpecMode,
205 /// ServiceSpecModeReplicated,
206 /// TaskSpec,
207 /// TaskSpecContainerSpec
208 /// };
209 ///
210 /// let service = ServiceSpec {
211 /// name: Some(String::from("my-service")),
212 /// mode: Some(ServiceSpecMode {
213 /// replicated: Some(ServiceSpecModeReplicated {
214 /// replicas: Some(2)
215 /// }),
216 /// ..Default::default()
217 /// }),
218 /// task_template: Some(TaskSpec {
219 /// container_spec: Some(TaskSpecContainerSpec {
220 /// image: Some(String::from("hello-world")),
221 /// ..Default::default()
222 /// }),
223 /// ..Default::default()
224 /// }),
225 /// ..Default::default()
226 /// };
227 /// let credentials = None;
228 ///
229 /// docker.create_service(service, credentials);
230 /// ```
231 pub async fn create_service(
232 &self,
233 service_spec: ServiceSpec,
234 credentials: Option<DockerCredentials>,
235 ) -> Result<ServiceCreateResponse, Error> {
236 let url = "/services/create";
237
238 let req = self.build_request_with_registry_auth(
239 url,
240 Builder::new()
241 .method(Method::POST)
242 .header(CONTENT_TYPE, "application/json"),
243 None::<String>,
244 Docker::serialize_payload(Some(service_spec)),
245 DockerCredentialsHeader::Auth(credentials),
246 );
247
248 self.process_into_value(req).await
249 }
250
251 /// ---
252 ///
253 /// # Inspect Service
254 ///
255 /// Inspect a service.
256 ///
257 /// # Arguments
258 ///
259 /// - Service name or id as a string slice.
260 /// - Optional [Inspect Service Options](InspectServiceOptions) struct.
261 ///
262 /// # Returns
263 ///
264 /// - [Service](Service), wrapped in a Future.
265 ///
266 /// # Examples
267 ///
268 /// ```rust
269 /// # use bollard_next::Docker;
270 /// # let docker = Docker::connect_with_http_defaults().unwrap();
271 /// use bollard_next::service::InspectServiceOptions;
272 ///
273 /// let options = Some(InspectServiceOptions{
274 /// insert_defaults: true,
275 /// });
276 ///
277 /// docker.inspect_service("my-service", options);
278 /// ```
279 pub async fn inspect_service(
280 &self,
281 service_name: &str,
282 options: Option<InspectServiceOptions>,
283 ) -> Result<Service, Error> {
284 let url = format!("/services/{service_name}");
285
286 let req = self.build_request(
287 &url,
288 Builder::new().method(hyper::Method::GET),
289 options,
290 Ok(BodyType::Left(Full::new(Bytes::new()))),
291 );
292
293 self.process_into_value(req).await
294 }
295
296 /// ---
297 ///
298 /// # Delete Service
299 ///
300 /// Delete a service.
301 ///
302 /// # Arguments
303 ///
304 /// - Service name or id as a string slice.
305 ///
306 /// # Returns
307 ///
308 /// - unit type `()`, wrapped in a Future.
309 ///
310 /// # Examples
311 ///
312 /// ```rust
313 /// # use bollard_next::Docker;
314 /// # let docker = Docker::connect_with_http_defaults().unwrap();
315 ///
316 /// docker.delete_service("my-service");
317 /// ```
318 pub async fn delete_service(&self, service_name: &str) -> Result<(), Error> {
319 let url = format!("/services/{service_name}");
320
321 let req = self.build_request(
322 &url,
323 Builder::new().method(hyper::Method::DELETE),
324 None::<String>,
325 Ok(BodyType::Left(Full::new(Bytes::new()))),
326 );
327
328 self.process_into_unit(req).await
329 }
330
331 /// ---
332 ///
333 /// # Update Service
334 ///
335 /// Update an existing service
336 ///
337 /// # Arguments
338 ///
339 /// - Service name or id as a string slice.
340 /// - [ServiceSpec](ServiceSpec) struct.
341 /// - [UpdateServiceOptions](UpdateServiceOptions) struct.
342 /// - Optional [Docker Credentials](DockerCredentials) struct.
343 ///
344 /// # Returns
345 ///
346 /// - A [Service Update Response](ServiceUpdateResponse) struct,
347 /// wrapped in a Future.
348 ///
349 /// # Examples
350 ///
351 /// ```rust
352 /// # use bollard_next::Docker;
353 /// # let docker = Docker::connect_with_http_defaults().unwrap();
354 /// use bollard_next::service::{
355 /// InspectServiceOptions,
356 /// ServiceSpec,
357 /// ServiceSpecMode,
358 /// ServiceSpecModeReplicated,
359 /// TaskSpec,
360 /// TaskSpecContainerSpec,
361 /// UpdateServiceOptions,
362 /// };
363 ///
364 /// use std::collections::HashMap;
365 /// use std::default::Default;
366 ///
367 /// let result = async move {
368 /// let service_name = "my-service";
369 /// let current_version = docker.inspect_service(
370 /// service_name,
371 /// None::<InspectServiceOptions>
372 /// ).await?.version.unwrap().index.unwrap();
373 /// let service = ServiceSpec {
374 /// mode: Some(ServiceSpecMode {
375 /// replicated: Some(ServiceSpecModeReplicated {
376 /// replicas: Some(0)
377 /// }),
378 /// ..Default::default()
379 /// }),
380 /// ..Default::default()
381 /// };
382 /// let options = UpdateServiceOptions {
383 /// version: current_version,
384 /// ..Default::default()
385 /// };
386 /// let credentials = None;
387 ///
388 /// docker.update_service("my-service", service, options, credentials).await
389 /// };
390 /// ```
391 pub async fn update_service(
392 &self,
393 service_name: &str,
394 service_spec: ServiceSpec,
395 options: UpdateServiceOptions,
396 credentials: Option<DockerCredentials>,
397 ) -> Result<ServiceUpdateResponse, Error> {
398 let url = format!("/services/{service_name}/update");
399
400 let req = self.build_request_with_registry_auth(
401 &url,
402 Builder::new()
403 .method(Method::POST)
404 .header(CONTENT_TYPE, "application/json"),
405 Some(options),
406 Docker::serialize_payload(Some(service_spec)),
407 DockerCredentialsHeader::Auth(credentials),
408 );
409
410 self.process_into_value(req).await
411 }
412}