1use std::{
6 collections::{BTreeMap, HashMap},
7 hash::Hash,
8};
9
10use hyper::Body;
11use serde::{Deserialize, Serialize};
12use serde_json::{json, Value};
13use url::form_urlencoded;
14
15use crate::{
16 docker::Docker,
17 errors::{Error, Result},
18};
19
20pub struct Networks<'docker> {
24 docker: &'docker Docker,
25}
26
27impl<'docker> Networks<'docker> {
28 pub fn new(docker: &'docker Docker) -> Self {
30 Networks { docker }
31 }
32
33 pub async fn list(
37 &self,
38 opts: &NetworkListOptions,
39 ) -> Result<Vec<NetworkDetails>> {
40 let mut path = vec!["/networks".to_owned()];
41 if let Some(query) = opts.serialize() {
42 path.push(query);
43 }
44 self.docker.get_json(&path.join("?")).await
45 }
46
47 pub fn get<S>(
49 &self,
50 id: S,
51 ) -> Network<'docker>
52 where
53 S: Into<String>,
54 {
55 Network::new(self.docker, id)
56 }
57
58 pub async fn create(
62 &self,
63 opts: &NetworkCreateOptions,
64 ) -> Result<NetworkCreateInfo> {
65 let body: Body = opts.serialize()?.into();
66 let path = vec!["/networks/create".to_owned()];
67
68 self.docker
69 .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON)))
70 .await
71 }
72}
73
74pub struct Network<'docker> {
76 docker: &'docker Docker,
77 id: String,
78}
79
80impl<'docker> Network<'docker> {
81 pub fn new<S>(
83 docker: &'docker Docker,
84 id: S,
85 ) -> Self
86 where
87 S: Into<String>,
88 {
89 Network {
90 docker,
91 id: id.into(),
92 }
93 }
94
95 pub fn id(&self) -> &str {
97 &self.id
98 }
99
100 pub async fn inspect(&self) -> Result<NetworkDetails> {
104 self.docker
105 .get_json(&format!("/networks/{}", self.id)[..])
106 .await
107 }
108
109 pub async fn delete(&self) -> Result<()> {
113 self.docker
114 .delete(&format!("/networks/{}", self.id)[..])
115 .await?;
116 Ok(())
117 }
118
119 pub async fn connect(
123 &self,
124 opts: &ContainerConnectionOptions,
125 ) -> Result<()> {
126 self.do_connection("connect", opts).await
127 }
128
129 pub async fn disconnect(
133 &self,
134 opts: &ContainerConnectionOptions,
135 ) -> Result<()> {
136 self.do_connection("disconnect", opts).await
137 }
138
139 async fn do_connection(
140 &self,
141 segment: &str,
142 opts: &ContainerConnectionOptions,
143 ) -> Result<()> {
144 let body: Body = opts.serialize()?.into();
145
146 self.docker
147 .post(
148 &format!("/networks/{}/{}", self.id, segment)[..],
149 Some((body, mime::APPLICATION_JSON)),
150 )
151 .await?;
152 Ok(())
153 }
154}
155
156#[derive(Default, Debug)]
158pub struct NetworkListOptions {
159 params: HashMap<&'static str, String>,
160}
161
162impl NetworkListOptions {
163 pub fn serialize(&self) -> Option<String> {
165 if self.params.is_empty() {
166 None
167 } else {
168 Some(
169 form_urlencoded::Serializer::new(String::new())
170 .extend_pairs(&self.params)
171 .finish(),
172 )
173 }
174 }
175}
176
177#[derive(Serialize, Debug)]
179pub struct NetworkCreateOptions {
180 params: HashMap<&'static str, Value>,
181}
182
183impl NetworkCreateOptions {
184 pub fn builder(name: &str) -> NetworkCreateOptionsBuilder {
186 NetworkCreateOptionsBuilder::new(name)
187 }
188
189 pub fn serialize(&self) -> Result<String> {
191 serde_json::to_string(&self.params).map_err(Error::from)
192 }
193
194 pub fn parse_from<'a, K, V>(
195 &self,
196 params: &'a HashMap<K, V>,
197 body: &mut serde_json::Map<String, Value>,
198 ) where
199 &'a HashMap<K, V>: IntoIterator,
200 K: ToString + Eq + Hash,
201 V: Serialize,
202 {
203 for (k, v) in params.iter() {
204 let key = k.to_string();
205 let value = serde_json::to_value(v).unwrap();
206
207 body.insert(key, value);
208 }
209 }
210}
211
212#[derive(Default)]
213pub struct NetworkCreateOptionsBuilder {
214 params: HashMap<&'static str, Value>,
215}
216
217impl NetworkCreateOptionsBuilder {
218 pub(crate) fn new(name: &str) -> Self {
219 let mut params = HashMap::new();
220 params.insert("Name", json!(name));
221 NetworkCreateOptionsBuilder { params }
222 }
223
224 pub fn driver(
225 &mut self,
226 name: &str,
227 ) -> &mut Self {
228 if !name.is_empty() {
229 self.params.insert("Driver", json!(name));
230 }
231 self
232 }
233
234 pub fn label(
235 &mut self,
236 labels: HashMap<String, String>,
237 ) -> &mut Self {
238 self.params.insert("Labels", json!(labels));
239 self
240 }
241
242 pub fn build(&self) -> NetworkCreateOptions {
243 NetworkCreateOptions {
244 params: self.params.clone(),
245 }
246 }
247}
248
249#[derive(Serialize, Debug)]
251pub struct ContainerConnectionOptions {
252 params: HashMap<&'static str, Value>,
253}
254
255impl ContainerConnectionOptions {
256 pub fn serialize(&self) -> Result<String> {
258 serde_json::to_string(&self.params).map_err(Error::from)
259 }
260
261 pub fn parse_from<'a, K, V>(
262 &self,
263 params: &'a HashMap<K, V>,
264 body: &mut BTreeMap<String, Value>,
265 ) where
266 &'a HashMap<K, V>: IntoIterator,
267 K: ToString + Eq + Hash,
268 V: Serialize,
269 {
270 for (k, v) in params.iter() {
271 let key = k.to_string();
272 let value = serde_json::to_value(v).unwrap();
273
274 body.insert(key, value);
275 }
276 }
277
278 pub fn builder(container_id: &str) -> ContainerConnectionOptionsBuilder {
280 ContainerConnectionOptionsBuilder::new(container_id)
281 }
282}
283
284#[derive(Default)]
285pub struct ContainerConnectionOptionsBuilder {
286 params: HashMap<&'static str, Value>,
287}
288
289impl ContainerConnectionOptionsBuilder {
290 pub(crate) fn new(container_id: &str) -> Self {
291 let mut params = HashMap::new();
292 params.insert("Container", json!(container_id));
293 ContainerConnectionOptionsBuilder { params }
294 }
295
296 pub fn aliases(
297 &mut self,
298 aliases: Vec<&str>,
299 ) -> &mut Self {
300 self.params
301 .insert("EndpointConfig", json!({ "Aliases": json!(aliases) }));
302 self
303 }
304
305 pub fn force(&mut self) -> &mut Self {
306 self.params.insert("Force", json!(true));
307 self
308 }
309
310 pub fn build(&self) -> ContainerConnectionOptions {
311 ContainerConnectionOptions {
312 params: self.params.clone(),
313 }
314 }
315}
316
317type PortDescription = HashMap<String, Option<Vec<HashMap<String, String>>>>;
318
319#[derive(Clone, Debug, Serialize, Deserialize)]
320#[serde(rename_all = "PascalCase")]
321pub struct NetworkSettings {
322 pub bridge: String,
323 pub gateway: String,
324 #[serde(rename = "IPAddress")]
325 pub ip_address: String,
326 #[serde(rename = "IPPrefixLen")]
327 pub ip_prefix_len: u64,
328 pub mac_address: String,
329 pub ports: Option<PortDescription>,
330 pub networks: HashMap<String, NetworkEntry>,
331}
332
333#[derive(Clone, Debug, Serialize, Deserialize)]
334#[serde(rename_all = "PascalCase")]
335pub struct NetworkEntry {
336 #[serde(rename = "NetworkID")]
337 pub network_id: String,
338 #[serde(rename = "EndpointID")]
339 pub endpoint_id: String,
340 pub gateway: String,
341 #[serde(rename = "IPAddress")]
342 pub ip_address: String,
343 #[serde(rename = "IPPrefixLen")]
344 pub ip_prefix_len: u64,
345 #[serde(rename = "IPv6Gateway")]
346 pub ipv6_gateway: String,
347 #[serde(rename = "GlobalIPv6Address")]
348 pub global_ipv6_address: String,
349 #[serde(rename = "GlobalIPv6PrefixLen")]
350 pub global_ipv6_prefix_len: u64,
351 pub mac_address: String,
352 pub links: Option<Vec<String>>,
353 pub aliases: Option<Vec<String>>,
354 #[serde(rename = "IPAMConfig")]
355 pub ipam_config: Option<EndpointIPAMConfig>,
356}
357
358#[derive(Clone, Debug, Serialize, Deserialize)]
359#[allow(clippy::upper_case_acronyms)]
360pub struct EndpointIPAMConfig {
361 #[serde(rename = "IPv4Address")]
362 pub ipv4_address: String,
363 #[serde(rename = "IPv6Address")]
364 pub ipv6_address: String,
365 #[serde(rename = "LinkLocalIPs")]
366 pub link_local_ips: Vec<String>,
367}
368
369#[derive(Clone, Debug, Serialize, Deserialize)]
370#[serde(rename_all = "PascalCase")]
371pub struct Ipam {
372 pub driver: String,
373 pub config: Vec<HashMap<String, String>>,
374 pub options: Option<HashMap<String, String>>,
375}
376
377#[derive(Clone, Debug, Serialize, Deserialize)]
378#[serde(rename_all = "PascalCase")]
379pub struct NetworkDetails {
380 pub name: String,
381 pub id: String,
382 pub scope: String,
383 pub driver: String,
384 #[serde(rename = "EnableIPv6")]
385 pub enable_ipv6: bool,
386 #[serde(rename = "IPAM")]
387 pub ipam: Ipam,
388 pub internal: bool,
389 pub attachable: bool,
390 pub containers: HashMap<String, NetworkContainerDetails>,
391 pub options: Option<HashMap<String, String>>,
392 pub labels: Option<HashMap<String, String>>,
393}
394
395#[derive(Clone, Debug, Serialize, Deserialize)]
396#[serde(rename_all = "PascalCase")]
397pub struct NetworkContainerDetails {
398 #[serde(rename = "EndpointID")]
399 pub endpoint_id: String,
400 pub mac_address: String,
401 #[serde(rename = "IPv4Address")]
402 pub ipv4_address: String,
403 #[serde(rename = "IPv6Address")]
404 pub ipv6_address: String,
405}
406
407#[derive(Clone, Debug, Serialize, Deserialize)]
408#[serde(rename_all = "PascalCase")]
409pub struct NetworkCreateInfo {
410 pub id: String,
411 pub warning: String,
412}