1use crate::error::*;
21use crate::types::client::{
22 NetworkConnect, NetworkCreateResponse, NetworkDisconnect, NetworkInspectOptions,
23 NetworkListOptions, NetworksPruneReport,
24};
25use crate::types::filters::Args;
26use crate::types::network::{
27 EndpointSettings, NetworkCreate, NetworkCreateBuilder, NetworkResource,
28};
29use crate::{read_response_body, DockerEngineClient};
30use hyper::client::connect::Connect;
31use hyper::{Body, Method, Request};
32use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
33use snafu::{ensure, ResultExt};
34use std::collections::HashMap;
35use tokio::time::timeout;
36
37impl<C: Connect + Clone + Send + Sync + 'static> DockerEngineClient<C> {
38 pub async fn network_list(
40 &self,
41 options: Option<NetworkListOptions>,
42 ) -> Result<Vec<NetworkResource>, Error> {
43 let mut query_params: HashMap<String, String> = HashMap::new();
44 if let Some(options) = options {
45 if let Some(filter) = options.filters {
46 query_params.insert(
47 "filters".into(),
48 serde_json::to_string(&filter.fields).context(JsonSerializationError {})?,
49 );
50 }
51 }
52 let query_params = if !query_params.is_empty() {
53 Some(query_params)
54 } else {
55 None
56 };
57
58 let request = Request::builder()
59 .method(Method::GET)
60 .uri(self.request_uri("/networks", query_params)?)
61 .header("Accept", "application/json")
62 .body(Body::empty())
63 .context(HttpClientRequestBuilderError {})?;
64
65 let client = self.client.as_ref().unwrap();
66 let response = timeout(self.timeout, client.request(request))
67 .await
68 .context(HttpClientTimeoutError {})?
69 .context(HttpClientError {})?;
70 ensure!(
71 response.status().is_success(),
72 HttpClientResponseError {
73 status: response.status().as_u16()
74 }
75 );
76 let response_body = read_response_body(response, self.timeout).await?;
77 Ok(serde_json::from_str(&response_body).context(JsonDeserializationError {})?)
78 }
79
80 pub async fn network_inspect(
82 &self,
83 network_id: &str,
84 options: Option<NetworkInspectOptions>,
85 ) -> Result<NetworkResource, Error> {
86 let mut query_params: HashMap<String, String> = HashMap::new();
87 if let Some(options) = options {
88 if options.verbose {
89 query_params.insert("verbose".into(), "true".into());
90 }
91 if let Some(scope) = options.scope {
92 query_params.insert("scope".into(), scope);
93 }
94 }
95 let query_params = if !query_params.is_empty() {
96 Some(query_params)
97 } else {
98 None
99 };
100
101 let request = Request::builder()
102 .method(Method::GET)
103 .uri(self.request_uri(
104 &format!(
105 "/networks/{}",
106 utf8_percent_encode(network_id, NON_ALPHANUMERIC).to_string()
107 ),
108 query_params,
109 )?)
110 .header("Accept", "application/json")
111 .body(Body::empty())
112 .context(HttpClientRequestBuilderError {})?;
113
114 let client = self.client.as_ref().unwrap();
115 let response = timeout(self.timeout, client.request(request))
116 .await
117 .context(HttpClientTimeoutError {})?
118 .context(HttpClientError {})?;
119 ensure!(
120 response.status().is_success(),
121 HttpClientResponseError {
122 status: response.status().as_u16()
123 }
124 );
125 let response_body = read_response_body(response, self.timeout).await?;
126 Ok(serde_json::from_str(&response_body).context(JsonDeserializationError {})?)
127 }
128
129 pub async fn network_remove(&self, network_id: &str) -> Result<(), Error> {
131 let request = Request::builder()
132 .method(Method::DELETE)
133 .uri(self.request_uri(
134 &format!(
135 "/networks/{}",
136 utf8_percent_encode(network_id, NON_ALPHANUMERIC).to_string()
137 ),
138 None,
139 )?)
140 .header("Accept", "application/json")
141 .body(Body::empty())
142 .context(HttpClientRequestBuilderError {})?;
143
144 let client = self.client.as_ref().unwrap();
145 let response = timeout(self.timeout, client.request(request))
146 .await
147 .context(HttpClientTimeoutError {})?
148 .context(HttpClientError {})?;
149 ensure!(
150 response.status().is_success(),
151 HttpClientResponseError {
152 status: response.status().as_u16()
153 }
154 );
155
156 Ok(())
157 }
158
159 pub async fn network_create(
161 &self,
162 name: &str,
163 options: Option<NetworkCreate>,
164 ) -> Result<NetworkCreateResponse, Error> {
165 let mut network_create_request =
166 options.unwrap_or_else(|| NetworkCreateBuilder::default().build().unwrap());
167 network_create_request.name = Some(name.into());
168
169 let request = Request::builder()
170 .method(Method::POST)
171 .uri(self.request_uri("/networks/create", None)?)
172 .header("Content-Type", "application/json")
173 .header("Accept", "application/json")
174 .body(Body::from(
175 serde_json::to_string(&network_create_request)
176 .context(JsonSerializationError {})?,
177 ))
178 .context(HttpClientRequestBuilderError {})?;
179
180 let client = self.client.as_ref().unwrap();
181 let response = timeout(self.timeout, client.request(request))
182 .await
183 .context(HttpClientTimeoutError {})?
184 .context(HttpClientError {})?;
185 ensure!(
186 response.status().is_success(),
187 HttpClientResponseError {
188 status: response.status().as_u16()
189 }
190 );
191 let response_body = read_response_body(response, self.timeout).await?;
192 Ok(serde_json::from_str(&response_body).context(JsonDeserializationError {})?)
193 }
194
195 pub async fn network_connect(
197 &self,
198 network_id: &str,
199 container_id: &str,
200 config: Option<EndpointSettings>,
201 ) -> Result<(), Error> {
202 let network_connect_request = NetworkConnect {
203 container: container_id.into(),
204 endpoint_config: config,
205 };
206
207 let request = Request::builder()
208 .method(Method::POST)
209 .uri(self.request_uri(
210 &format!(
211 "/networks/{}/connect",
212 utf8_percent_encode(network_id, NON_ALPHANUMERIC).to_string()
213 ),
214 None,
215 )?)
216 .header("Content-Type", "application/json")
217 .header("Accept", "application/json")
218 .body(Body::from(
219 serde_json::to_string(&network_connect_request)
220 .context(JsonSerializationError {})?,
221 ))
222 .context(HttpClientRequestBuilderError {})?;
223
224 let client = self.client.as_ref().unwrap();
225 let response = timeout(self.timeout, client.request(request))
226 .await
227 .context(HttpClientTimeoutError {})?
228 .context(HttpClientError {})?;
229 ensure!(
230 response.status().is_success(),
231 HttpClientResponseError {
232 status: response.status().as_u16()
233 }
234 );
235
236 Ok(())
237 }
238
239 pub async fn network_disconnect(
241 &self,
242 network_id: &str,
243 container_id: &str,
244 force: bool,
245 ) -> Result<(), Error> {
246 let network_disconnect_request = NetworkDisconnect {
247 container: container_id.into(),
248 force: if force { Some(true) } else { None },
249 };
250
251 let request = Request::builder()
252 .method(Method::POST)
253 .uri(self.request_uri(
254 &format!(
255 "/networks/{}/disconnect",
256 utf8_percent_encode(network_id, NON_ALPHANUMERIC).to_string()
257 ),
258 None,
259 )?)
260 .header("Content-Type", "application/json")
261 .header("Accept", "application/json")
262 .body(Body::from(
263 serde_json::to_string(&network_disconnect_request)
264 .context(JsonSerializationError {})?,
265 ))
266 .context(HttpClientRequestBuilderError {})?;
267
268 let client = self.client.as_ref().unwrap();
269 let response = timeout(self.timeout, client.request(request))
270 .await
271 .context(HttpClientTimeoutError {})?
272 .context(HttpClientError {})?;
273 ensure!(
274 response.status().is_success(),
275 HttpClientResponseError {
276 status: response.status().as_u16()
277 }
278 );
279
280 Ok(())
281 }
282
283 pub async fn networks_prune(
285 &self,
286 prune_filters: Option<Args>,
287 ) -> Result<NetworksPruneReport, Error> {
288 let mut query_params: HashMap<String, String> = HashMap::new();
289 if let Some(prune_filters) = prune_filters {
290 query_params.insert(
291 "filters".into(),
292 serde_json::to_string(&prune_filters.fields).context(JsonSerializationError {})?,
293 );
294 }
295 let query_params = if !query_params.is_empty() {
296 Some(query_params)
297 } else {
298 None
299 };
300
301 let request = Request::builder()
302 .method(Method::POST)
303 .uri(self.request_uri("/networks/prune", query_params)?)
304 .header("Accept", "application/json")
305 .body(Body::empty())
306 .context(HttpClientRequestBuilderError {})?;
307
308 let client = self.client.as_ref().unwrap();
309 let response = timeout(self.timeout, client.request(request))
310 .await
311 .context(HttpClientTimeoutError {})?
312 .context(HttpClientError {})?;
313 ensure!(
314 response.status().is_success(),
315 HttpClientResponseError {
316 status: response.status().as_u16()
317 }
318 );
319 let response_body = read_response_body(response, self.timeout).await?;
320 Ok(serde_json::from_str(&response_body).context(JsonDeserializationError {})?)
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use crate::types::client::*;
327 use crate::types::container::ContainerConfigBuilder;
328 use crate::types::filters::*;
329 use crate::{opts, LocalDockerEngineClient};
330 use maplit::hashmap;
331
332 #[tokio::test]
333 async fn test_network_api() {
334 let docker_client =
335 LocalDockerEngineClient::new_client_with_opts(Some(vec![Box::new(opts::from_env)]))
336 .unwrap();
337
338 let name = "test_network";
340 let network_create_response = docker_client.network_create(name, None).await.unwrap();
341
342 let networks_response = docker_client
344 .network_list(Some(
345 NetworkListOptionsBuilder::default()
346 .filters(Some(
347 ArgsBuilder::default()
348 .fields(hashmap! {
349 "id".into() => vec![network_create_response.id.clone()],
350 })
351 .build()
352 .unwrap(),
353 ))
354 .build()
355 .unwrap(),
356 ))
357 .await
358 .unwrap();
359
360 assert_eq!(networks_response.len(), 1);
361 assert_eq!(networks_response[0].name.as_ref().unwrap(), name);
362
363 let network_inspect_response = docker_client
365 .network_inspect(
366 &network_create_response.id,
367 Some(
368 NetworkInspectOptionsBuilder::default()
369 .verbose(true)
370 .build()
371 .unwrap(),
372 ),
373 )
374 .await
375 .unwrap();
376
377 assert_eq!(network_inspect_response.name.as_ref().unwrap(), name);
378
379 let container_create_response = docker_client
381 .container_create(
382 ContainerConfigBuilder::default()
383 .image(Some("busybox".into()))
384 .cmd(Some(vec!["sleep".into(), "60".into()]))
385 .build()
386 .unwrap(),
387 Some("test_network_api_container".into()),
388 )
389 .await
390 .unwrap();
391
392 docker_client
394 .network_connect(
395 &network_create_response.id,
396 &container_create_response.id,
397 None,
398 )
399 .await
400 .unwrap();
401
402 let container_inspect_response = docker_client
404 .container_inspect(&container_create_response.id)
405 .await
406 .unwrap();
407
408 assert!(container_inspect_response
410 .network_settings
411 .as_ref()
412 .unwrap()
413 .networks
414 .as_ref()
415 .unwrap()
416 .contains_key(name));
417
418 docker_client
420 .network_disconnect(
421 &network_create_response.id,
422 &container_create_response.id,
423 true,
424 )
425 .await
426 .unwrap();
427
428 let container_inspect_response = docker_client
430 .container_inspect(&container_create_response.id)
431 .await
432 .unwrap();
433
434 assert!(!container_inspect_response
436 .network_settings
437 .as_ref()
438 .unwrap()
439 .networks
440 .as_ref()
441 .unwrap()
442 .contains_key(name));
443
444 docker_client
446 .container_remove(
447 &container_create_response.id,
448 Some(
449 ContainerRemoveOptionsBuilder::default()
450 .force(true)
451 .build()
452 .unwrap(),
453 ),
454 )
455 .await
456 .unwrap();
457
458 docker_client
460 .network_remove(&network_create_response.id)
461 .await
462 .unwrap();
463
464 let response = docker_client
466 .network_inspect(
467 &network_create_response.id,
468 Some(
469 NetworkInspectOptionsBuilder::default()
470 .verbose(true)
471 .build()
472 .unwrap(),
473 ),
474 )
475 .await;
476
477 assert!(response.is_err());
478 }
479}