1#![allow(clippy::new_without_default)]
3
4use crate::network;
5use serde::de::{DeserializeOwned, Deserializer};
6use serde::{Deserialize, Serialize};
7
8use std::collections::{HashMap, HashSet};
9use std::path::PathBuf;
10use std::str::FromStr;
11use std::time::Duration;
12use url::{self, form_urlencoded};
13
14fn null_to_default<'de, D, T>(de: D) -> Result<T, D::Error>
15where
16 D: Deserializer<'de>,
17 T: DeserializeOwned + Default,
18{
19 let actual: Option<T> = Option::deserialize(de)?;
20 Ok(actual.unwrap_or_default())
21}
22
23#[derive(Debug, Clone, Default)]
26pub struct ContainerListOptions {
27 all: bool,
28 latest: bool,
31 limit: Option<u64>,
32 size: bool,
34}
35
36impl ContainerListOptions {
37 pub fn all(mut self) -> Self {
39 self.all = true;
40 self
41 }
42
43 pub fn latest(mut self) -> Self {
46 self.latest = true;
47 self
48 }
49
50 pub fn limit(mut self, n: u64) -> Self {
52 self.limit = Some(n);
53 self
54 }
55
56 pub fn size(mut self) -> Self {
59 self.size = true;
60 self
61 }
62
63 pub fn to_url_params(&self) -> String {
65 let mut params = form_urlencoded::Serializer::new(String::new());
66 if self.all {
67 params.append_pair("all", "1");
68 }
69 if self.latest {
70 params.append_pair("latest", "1");
71 }
72 if let Some(limit) = self.limit {
73 params.append_pair("limit", &limit.to_string());
74 }
75 if self.size {
76 params.append_pair("size", "1");
77 }
78 params.finish()
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
84#[allow(non_snake_case)]
85pub struct RestartPolicy {
86 pub Name: String,
89 pub MaximumRetryCount: u16,
91}
92
93impl Default for RestartPolicy {
94 fn default() -> Self {
95 Self::new("no".to_owned(), 0)
96 }
97}
98
99impl RestartPolicy {
100 pub fn new(name: String, maximum_retry_count: u16) -> Self {
101 RestartPolicy {
102 Name: name,
103 MaximumRetryCount: maximum_retry_count,
104 }
105 }
106
107 pub fn no() -> Self {
108 Self::new("no".to_owned(), 0)
109 }
110
111 pub fn always() -> Self {
112 Self::new("always".to_owned(), 0)
113 }
114
115 pub fn on_failure() -> Self {
116 Self::new("on-failure".to_owned(), 10)
117 }
118
119 pub fn unless_stopped() -> Self {
120 Self::new("unless-stopped".to_owned(), 0)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn serde_logconfig() {
130 let cfg = LogConfig::new(LogConfigType::JsonFile);
131 let json = serde_json::to_string(&cfg).unwrap();
132 let json_cfg = serde_json::from_str(&json).unwrap();
133 assert_eq!(&cfg, &json_cfg);
134 }
135
136 #[test]
137 fn serde_logconfig_with_opt() {
138 let config = {
139 let mut cfg = HashMap::new();
140 cfg.insert("tagA".to_string(), "valueA".to_string());
141 cfg.insert("tagB".to_string(), "valueB".to_string());
142 cfg
143 };
144 let cfg = LogConfig {
145 r#type: LogConfigType::JsonFile,
146 config,
147 };
148 let json = serde_json::to_string(&cfg).unwrap();
149 let json_cfg = serde_json::from_str(&json).unwrap();
150 assert_eq!(&cfg, &json_cfg);
151 }
152
153 #[test]
154 fn deser_restart_policy() {
155 let no = r#"{"MaximumRetryCount":0, "Name":"no"}"#;
156 assert_eq!(RestartPolicy::default(), serde_json::from_str(no).unwrap());
157 assert_eq!(RestartPolicy::no(), serde_json::from_str(no).unwrap());
158 let always = r#"{"MaximumRetryCount":0, "Name":"always"}"#;
159 assert_eq!(
160 RestartPolicy::always(),
161 serde_json::from_str(always).unwrap()
162 );
163 let onfailure = r#"{"MaximumRetryCount":10, "Name":"on-failure"}"#;
164 assert_eq!(
165 RestartPolicy::on_failure(),
166 serde_json::from_str(onfailure).unwrap()
167 );
168 let unlessstopped = r#"{"MaximumRetryCount":0, "Name":"unless-stopped"}"#;
169 assert_eq!(
170 RestartPolicy::unless_stopped(),
171 serde_json::from_str(unlessstopped).unwrap()
172 );
173 }
174
175 #[test]
176 fn iso_restart_policy() {
177 let no = RestartPolicy::default();
178 assert_eq!(
179 serde_json::from_str::<RestartPolicy>(&serde_json::to_string(&no).unwrap()).unwrap(),
180 no
181 );
182 let always = RestartPolicy::new("always".to_owned(), 0);
183 assert_eq!(
184 serde_json::from_str::<RestartPolicy>(&serde_json::to_string(&always).unwrap())
185 .unwrap(),
186 always
187 );
188 let onfailure = RestartPolicy::new("on-failure".to_owned(), 10);
189 assert_eq!(
190 serde_json::from_str::<RestartPolicy>(&serde_json::to_string(&onfailure).unwrap())
191 .unwrap(),
192 onfailure
193 );
194 let unlessstopped = RestartPolicy::new("unless-stopped".to_owned(), 0);
195 assert_eq!(
196 serde_json::from_str::<RestartPolicy>(&serde_json::to_string(&unlessstopped).unwrap())
197 .unwrap(),
198 unlessstopped
199 );
200 }
201}
202
203#[derive(Debug, Clone, Default, Serialize, Deserialize)]
204#[allow(non_snake_case)]
205pub struct DeviceMapping {
206 PathOnHost: PathBuf,
207 PathInContainer: PathBuf,
208 CgroupPermissions: String,
210}
211
212impl DeviceMapping {
213 pub fn new(
214 path_on_host: PathBuf,
215 path_in_container: PathBuf,
216 cgroup_permissions: String,
217 ) -> Self {
218 Self {
219 PathOnHost: path_on_host,
220 PathInContainer: path_in_container,
221 CgroupPermissions: cgroup_permissions,
222 }
223 }
224}
225
226#[derive(Debug, Clone, Default, Serialize, Deserialize)]
227#[serde(rename_all = "PascalCase")]
228pub struct ContainerHostConfig {
229 binds: Option<Vec<String>>,
230 tmpfs: Option<HashMap<String, String>>,
231 links: Option<Vec<String>>,
232 memory: Option<u64>,
233 memory_swap: Option<u64>,
234 memory_reservation: Option<u64>,
235 kernel_memory: Option<u64>,
236 cpu_percent: Option<u64>,
237 cpu_shares: Option<u64>,
238 cpu_period: Option<u64>,
239 cpu_quota: Option<u64>,
240 cpuset_cpus: Option<String>,
241 io_maximum_bandwidth: Option<u64>,
242 io_maximum_ops: Option<u64>,
243 blkio_weight: Option<u64>,
244 memory_swappiness: Option<i32>,
245 oom_kill_disable: Option<bool>,
246 oom_score_adj: Option<u16>,
247 pid_mode: Option<String>,
248 pids_limit: Option<i16>,
249 port_bindings: Option<PortBindings>,
250 publish_all_ports: Option<bool>,
251 privileged: Option<bool>,
252 readonly_rootfs: Option<bool>,
253 dns: Option<Vec<String>>,
254 dns_options: Option<Vec<String>>,
255 dns_search: Option<Vec<String>>,
256 auto_remove: Option<bool>,
257 volumes_from: Option<Vec<String>>,
258 cap_add: Option<Vec<String>>,
259 cap_drop: Option<Vec<String>>,
260 security_opt: Option<Vec<String>>,
261 group_add: Option<Vec<String>>,
262 restart_policy: Option<RestartPolicy>,
263 network_mode: Option<String>,
264 devices: Option<Vec<DeviceMapping>>,
265 sysctls: Option<HashMap<String, String>>,
266 runtime: Option<String>,
267 log_config: Option<LogConfig>,
268 cgroup_parent: Option<String>,
269 volume_driver: Option<String>,
270 shm_size: Option<u64>,
271 userns_mode: Option<String>,
272 device_cgroup_rules: Option<Vec<String>>,
273 init: Option<bool>,
274}
275
276impl ContainerHostConfig {
277 pub fn new() -> Self {
278 Self {
279 ..Default::default()
280 }
281 }
282
283 pub fn device_cgroup_rules(&mut self, device_cgroup_rules: Vec<String>) -> &mut Self {
284 self.device_cgroup_rules = Some(device_cgroup_rules);
285 self
286 }
287
288 pub fn userns_mode(&mut self, userns_mode: String) -> &mut Self {
289 self.userns_mode = Some(userns_mode);
290 self
291 }
292
293 pub fn binds(&mut self, binds: Vec<String>) -> &mut Self {
294 self.binds = Some(binds);
295 self
296 }
297
298 pub fn tmpfs(&mut self, tmpfs: HashMap<String, String>) -> &mut Self {
299 self.tmpfs = Some(tmpfs);
300 self
301 }
302
303 pub fn links(&mut self, links: Vec<String>) -> &mut Self {
304 self.links = Some(links);
305 self
306 }
307
308 pub fn memory(&mut self, memory: u64) -> &mut Self {
309 self.memory = Some(memory);
310 self
311 }
312
313 pub fn memory_swap(&mut self, memory_swap: u64) -> &mut Self {
314 self.memory_swap = Some(memory_swap);
315 self
316 }
317
318 pub fn memory_reservation(&mut self, memory_reservation: u64) -> &mut Self {
319 self.memory_reservation = Some(memory_reservation);
320 self
321 }
322
323 pub fn kernel_memory(&mut self, kernel_memory: u64) -> &mut Self {
324 self.kernel_memory = Some(kernel_memory);
325 self
326 }
327
328 pub fn cpu_percent(&mut self, cpu_percent: u64) -> &mut Self {
329 self.cpu_percent = Some(cpu_percent);
330 self
331 }
332
333 pub fn cpu_shares(&mut self, cpu_shares: u64) -> &mut Self {
334 self.cpu_shares = Some(cpu_shares);
335 self
336 }
337
338 pub fn cpu_period(&mut self, cpu_period: u64) -> &mut Self {
339 self.cpu_period = Some(cpu_period);
340 self
341 }
342
343 pub fn cpu_quota(&mut self, cpu_quota: u64) -> &mut Self {
344 self.cpu_quota = Some(cpu_quota);
345 self
346 }
347
348 pub fn cpuset_cpus(&mut self, cpuset_cpus: String) -> &mut Self {
349 self.cpuset_cpus = Some(cpuset_cpus);
350 self
351 }
352
353 pub fn io_maximum_bandwidth(&mut self, io_maximum_bandwidth: u64) -> &mut Self {
354 self.io_maximum_bandwidth = Some(io_maximum_bandwidth);
355 self
356 }
357
358 pub fn io_maximum_ops(&mut self, io_maximum_ops: u64) -> &mut Self {
359 self.io_maximum_ops = Some(io_maximum_ops);
360 self
361 }
362
363 pub fn blkio_weight(&mut self, blkio_weight: u64) -> &mut Self {
364 self.blkio_weight = Some(blkio_weight);
365 self
366 }
367
368 pub fn memory_swappiness(&mut self, memory_swappiness: i32) -> &mut Self {
369 self.memory_swappiness = Some(memory_swappiness);
370 self
371 }
372
373 pub fn oom_kill_disable(&mut self, oom_kill_disable: bool) -> &mut Self {
374 self.oom_kill_disable = Some(oom_kill_disable);
375 self
376 }
377
378 pub fn oom_score_adj(&mut self, oom_score_adj: u16) -> &mut Self {
379 self.oom_score_adj = Some(oom_score_adj);
380 self
381 }
382
383 pub fn pid_mode(&mut self, pid_mode: String) -> &mut Self {
384 self.pid_mode = Some(pid_mode);
385 self
386 }
387
388 pub fn pids_limit(&mut self, pids_limit: i16) -> &mut Self {
389 self.pids_limit = Some(pids_limit);
390 self
391 }
392
393 pub fn publish_all_ports(&mut self, publish_all_ports: bool) -> &mut Self {
394 self.publish_all_ports = Some(publish_all_ports);
395 self
396 }
397
398 pub fn privileged(&mut self, privileged: bool) -> &mut Self {
399 self.privileged = Some(privileged);
400 self
401 }
402
403 pub fn readonly_rootfs(&mut self, readonly_rootfs: bool) -> &mut Self {
404 self.readonly_rootfs = Some(readonly_rootfs);
405 self
406 }
407
408 pub fn dns(&mut self, dns: Vec<String>) -> &mut Self {
409 self.dns = Some(dns);
410 self
411 }
412
413 pub fn dns_options(&mut self, dns_options: Vec<String>) -> &mut Self {
414 self.dns_options = Some(dns_options);
415 self
416 }
417
418 pub fn dns_search(&mut self, dns_search: Vec<String>) -> &mut Self {
419 self.dns_search = Some(dns_search);
420 self
421 }
422
423 pub fn auto_remove(&mut self, auto_remove: bool) -> &mut Self {
424 self.auto_remove = Some(auto_remove);
425 self
426 }
427
428 pub fn volumes_from(&mut self, volumes_from: Vec<String>) -> &mut Self {
429 self.volumes_from = Some(volumes_from);
430 self
431 }
432
433 pub fn cap_add(&mut self, cap_add: Vec<String>) -> &mut Self {
434 self.cap_add = Some(cap_add);
435 self
436 }
437
438 pub fn cap_drop(&mut self, cap_drop: Vec<String>) -> &mut Self {
439 self.cap_drop = Some(cap_drop);
440 self
441 }
442
443 pub fn security_opt(&mut self, security_opt: Vec<String>) -> &mut Self {
444 self.security_opt = Some(security_opt);
445 self
446 }
447
448 pub fn group_add(&mut self, group_add: Vec<String>) -> &mut Self {
449 self.group_add = Some(group_add);
450 self
451 }
452
453 pub fn restart_policy(&mut self, restart_policy: RestartPolicy) -> &mut Self {
454 self.restart_policy = Some(restart_policy);
455 self
456 }
457
458 pub fn network_mode(&mut self, network_mode: String) -> &mut Self {
459 self.network_mode = Some(network_mode);
460 self
461 }
462
463 pub fn devices(&mut self, devices: Vec<DeviceMapping>) -> &mut Self {
464 self.devices = Some(devices);
465 self
466 }
467
468 pub fn sysctls(&mut self, sysctls: HashMap<String, String>) -> &mut Self {
469 self.sysctls = Some(sysctls);
470 self
471 }
472
473 pub fn runtime(&mut self, runtime: String) -> &mut Self {
474 self.runtime = Some(runtime);
475 self
476 }
477
478 pub fn log_config(&mut self, log_config: LogConfig) -> &mut Self {
479 self.log_config = Some(log_config);
480 self
481 }
482
483 pub fn cgroup_parent(&mut self, cgroup_parent: String) -> &mut Self {
484 self.cgroup_parent = Some(cgroup_parent);
485 self
486 }
487
488 pub fn volume_driver(&mut self, volume_driver: String) -> &mut Self {
489 self.volume_driver = Some(volume_driver);
490 self
491 }
492
493 pub fn shm_size(&mut self, shm_size: u64) -> &mut Self {
494 self.shm_size = Some(shm_size);
495 self
496 }
497
498 pub fn port_bindings(&mut self, port_bindings: PortBindings) -> &mut Self {
499 self.port_bindings = Some(port_bindings);
500 self
501 }
502
503 pub fn init(&mut self, init: bool) -> &mut Self {
504 self.init = Some(init);
505 self
506 }
507}
508
509#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
510#[serde(rename_all = "PascalCase")]
511#[derive(Default)]
512pub struct LogConfig {
513 pub r#type: LogConfigType,
514 pub config: HashMap<String, String>,
515}
516
517impl LogConfig {
518 pub fn new(r#type: LogConfigType) -> Self {
519 Self {
520 r#type,
521 config: HashMap::new(),
522 }
523 }
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
527#[serde(rename_all = "kebab-case")]
528#[derive(Default)]
529pub enum LogConfigType {
530 JsonFile,
531 Syslog,
532 #[default]
533 Journald,
534 Gelf,
535 Fluentd,
536 Awslogs,
537 Splunk,
538 Etwlogs,
539 None,
540}
541
542#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
543#[serde(rename_all = "PascalCase")]
544pub struct NetworkingConfig {
545 pub endpoints_config: EndpointsConfig,
546}
547
548#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
550pub struct EndpointsConfig(HashMap<String, network::EndpointConfig>);
551
552impl From<HashMap<String, network::EndpointConfig>> for EndpointsConfig {
553 fn from(endpoints: HashMap<String, network::EndpointConfig>) -> Self {
554 EndpointsConfig(endpoints)
555 }
556}
557
558#[derive(Debug, Clone)]
559pub struct ContainerLogOptions {
560 pub stdout: bool,
561 pub stderr: bool,
562 pub since: Option<i64>,
563 pub timestamps: Option<bool>,
564 pub tail: Option<i64>,
565 pub follow: bool,
566}
567
568impl ContainerLogOptions {
569 pub(crate) fn to_url_params(&self) -> String {
570 let mut param = url::form_urlencoded::Serializer::new(String::new());
571 param.append_pair("stdout", &self.stdout.to_string());
572 param.append_pair("stderr", &self.stderr.to_string());
573 param.append_pair("follow", &self.follow.to_string());
574 if let Some(since) = self.since {
575 param.append_pair("since", &since.to_string());
576 }
577 if let Some(timestamps) = self.timestamps {
578 param.append_pair("timestamps", ×tamps.to_string());
579 }
580 if let Some(tail) = self.tail {
581 param.append_pair("tail", &tail.to_string());
582 }
583 param.finish()
584 }
585}
586
587impl Default for ContainerLogOptions {
588 fn default() -> Self {
589 ContainerLogOptions {
590 stdout: true,
591 stderr: true,
592 follow: false,
593 since: None,
594 timestamps: None,
595 tail: None,
596 }
597 }
598}
599
600#[derive(Debug, Clone, Deserialize, Serialize)]
601pub struct ContainerBuildOptions {
602 pub dockerfile: String,
605
606 pub t: Vec<String>,
609
610 pub extrahosts: Option<String>,
612
613 pub remote: Option<String>,
615
616 pub q: bool,
618
619 pub nocache: bool,
621
622 pub cachefrom: Option<Vec<String>>,
624
625 pub pull: Option<String>,
627
628 pub rm: bool,
630
631 pub forcerm: bool,
633
634 pub memory: Option<u64>,
636
637 pub memswap: Option<i64>,
639
640 pub cpushares: Option<u64>,
642
643 pub cpusetcpus: Option<String>,
645
646 pub cpuperiod: Option<u64>,
648
649 pub cpuquota: Option<u64>,
651
652 pub buildargs: Option<HashMap<String, String>>,
655
656 pub shmsize: Option<u64>,
658
659 pub squash: Option<bool>,
661
662 pub labels: Option<HashMap<String, String>>,
664
665 pub networkmode: Option<String>,
669
670 pub platform: String,
672}
673
674impl ContainerBuildOptions {
675 pub fn to_url_params(&self) -> String {
677 let mut params = form_urlencoded::Serializer::new(String::new());
678 params.append_pair("dockerfile", &self.dockerfile);
679 for tag in &self.t {
680 params.append_pair("t", tag);
681 }
682 if let Some(ref extrahosts) = self.extrahosts {
683 params.append_pair("extrahosts", extrahosts);
684 }
685 if let Some(ref remote) = self.remote {
686 params.append_pair("remote", remote);
687 }
688 if self.q {
689 params.append_pair("q", "true");
690 }
691 if self.nocache {
692 params.append_pair("nocache", "true");
693 }
694 if let Some(ref cachefrom) = self.cachefrom {
695 params.append_pair("cachefrom", &serde_json::to_string(&cachefrom).unwrap());
696 }
697 if let Some(ref pull) = self.pull {
698 params.append_pair("pull", pull);
699 }
700 if self.rm {
701 params.append_pair("rm", "true");
702 }
703 if self.forcerm {
704 params.append_pair("forcerm", "true");
705 }
706 if let Some(ref memory) = self.memory {
707 params.append_pair("memory", &memory.to_string());
708 }
709 if let Some(ref memswap) = self.memswap {
710 params.append_pair("memswap", &memswap.to_string());
711 }
712 if let Some(ref cpushares) = self.cpushares {
713 params.append_pair("cpushares", &cpushares.to_string());
714 }
715 if let Some(ref cpusetcpus) = self.cpusetcpus {
716 params.append_pair("cpusetcpus", cpusetcpus);
717 }
718 if let Some(ref cpuperiod) = self.cpuperiod {
719 params.append_pair("cpuperiod", &cpuperiod.to_string());
720 }
721 if let Some(ref cpuquota) = self.cpuquota {
722 params.append_pair("cpuquota", &cpuquota.to_string());
723 }
724 if let Some(ref buildargs) = self.buildargs {
725 params.append_pair(
726 "buildargs",
727 &serde_json::to_string(&buildargs).expect("Json parsing of buildargs param"),
728 );
729 }
730 if let Some(ref shmsize) = self.shmsize {
731 params.append_pair("shmsize", &shmsize.to_string());
732 }
733 if let Some(ref squash) = self.squash {
734 params.append_pair("squash", &squash.to_string());
735 }
736 if let Some(ref labels) = self.labels {
737 params.append_pair(
738 "labels",
739 &serde_json::to_string(&labels).expect("Json parsing of labels param"),
740 );
741 }
742 if let Some(ref networkmode) = self.networkmode {
743 params.append_pair("networkmode", networkmode);
744 }
745 params.append_pair("platform", &self.platform);
746 params.finish()
747 }
748}
749
750impl Default for ContainerBuildOptions {
751 fn default() -> Self {
752 ContainerBuildOptions {
753 dockerfile: String::from("Dockerfile"),
754 t: Vec::new(),
755 extrahosts: None,
756 remote: None,
757 q: false,
758 nocache: false,
759 cachefrom: None,
760 pull: None,
761 rm: true,
762 forcerm: false,
763 memory: None,
764 memswap: None,
765 cpushares: None,
766 cpusetcpus: None,
767 cpuperiod: None,
768 cpuquota: None,
769 buildargs: None,
770 shmsize: None,
771 squash: Some(false),
772 labels: None,
773 networkmode: None,
774 platform: String::new(),
775 }
776 }
777}
778#[derive(Debug, Clone, Default)]
779pub struct ExposedPorts(pub Vec<(u16, String)>);
780
781impl serde::Serialize for ExposedPorts {
782 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
783 let mut map = HashMap::new();
784 for (port, protocol) in &self.0 {
785 map.insert(
786 format!("{}/{}", port, protocol).clone(),
787 serde_json::Value::Object(serde_json::Map::new()),
788 );
789 }
790 map.serialize(serializer)
791 }
792}
793
794impl<'de> serde::Deserialize<'de> for ExposedPorts {
795 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
796 let map = HashMap::<String, serde_json::Value>::deserialize(deserializer)?;
797 let keys = map
798 .keys()
799 .map(|k| {
800 let mut parts = k.split('/');
801 let port = parts.next().unwrap().parse().unwrap();
802 let protocol = parts.next().unwrap().to_owned();
803 (port, protocol)
804 })
805 .collect();
806 Ok(ExposedPorts(keys))
807 }
808}
809
810#[test]
811fn test_exposed_ports() {
812 let ports = ExposedPorts(vec![
813 (80, "tcp".to_owned()),
814 (443, "tcp".to_owned()),
815 (8080, "tcp".to_owned()),
816 (8443, "tcp".to_owned()),
817 ]);
818 let json = serde_json::to_string(&ports).unwrap();
819 let result_json = serde_json::Value::from_str(&json).unwrap();
821 let expected_json =
822 serde_json::Value::from_str(r#"{"80/tcp":{},"443/tcp":{},"8080/tcp":{},"8443/tcp":{}}"#)
823 .unwrap();
824
825 assert_eq!(result_json, expected_json);
826
827 let ports: ExposedPorts = serde_json::from_str(&json).unwrap();
828 let result: HashSet<&(u16, String)> = HashSet::from_iter(ports.0.iter());
829 assert_eq!(
831 result,
832 HashSet::from_iter(
833 vec![
834 (80, "tcp".to_owned()),
835 (443, "tcp".to_owned()),
836 (8080, "tcp".to_owned()),
837 (8443, "tcp".to_owned())
838 ]
839 .iter()
840 )
841 );
842}
843
844#[derive(Debug, Clone, Default)]
845pub struct PortBindings(pub Vec<(u16, String, u16)>);
846
847impl serde::Serialize for PortBindings {
848 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
849 let mut map = HashMap::new();
850 for (container_port, protocol, host_port) in &self.0 {
851 map.insert(
852 format!("{}/{}", container_port, protocol).clone(),
853 vec![serde_json::json!({"HostPort": host_port.to_string()})],
854 );
855 }
856 map.serialize(serializer)
857 }
858}
859
860impl<'de> serde::Deserialize<'de> for PortBindings {
861 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
862 let map = HashMap::<String, serde_json::Value>::deserialize(deserializer)?;
863 let tuples = map
864 .keys()
865 .map(|k| {
866 let mut parts = k.split('/');
867 let port = parts.next().unwrap().parse().unwrap();
868 let protocol = parts.next().unwrap().to_owned();
869 let host_port = map
870 .get(k)
871 .unwrap()
872 .as_array()
873 .unwrap()
874 .first()
875 .unwrap()
876 .get("HostPort")
877 .unwrap()
878 .as_str()
879 .unwrap()
880 .parse()
881 .unwrap();
882 (port, protocol, host_port)
883 })
884 .collect();
885 Ok(PortBindings(tuples))
886 }
887}
888
889#[test]
890fn test_port_bindings() {
891 let ports = PortBindings(vec![
892 (80, "tcp".to_owned(), 8080),
893 (443, "tcp".to_owned(), 8000),
894 ]);
895 let json = serde_json::to_string(&ports).unwrap();
896 let result_json = serde_json::Value::from_str(&json).unwrap();
898 let expected_json = serde_json::Value::from_str(
899 r#"{"80/tcp":[{"HostPort":"8080"}],"443/tcp":[{"HostPort":"8000"}]}"#,
900 )
901 .unwrap();
902
903 assert_eq!(result_json, expected_json);
904
905 let ports: PortBindings = serde_json::from_str(&json).unwrap();
906 let result: HashSet<&(u16, String, u16)> = HashSet::from_iter(ports.0.iter());
907 assert_eq!(
909 result,
910 HashSet::from_iter(
911 vec![(80, "tcp".to_owned(), 8080), (443, "tcp".to_owned(), 8000),].iter()
912 )
913 );
914}
915#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
917#[serde(rename_all = "PascalCase")]
918pub struct HealthcheckConfig {
919 #[serde(skip_serializing_if = "Option::is_none")]
925 pub test: Option<Vec<String>>,
926
927 #[serde(skip_serializing_if = "Option::is_none")]
930 pub interval: Option<u64>,
931
932 #[serde(skip_serializing_if = "Option::is_none")]
935 pub timeout: Option<u64>,
936
937 #[serde(skip_serializing_if = "Option::is_none")]
940 pub retries: Option<u8>,
941
942 #[serde(skip_serializing_if = "Option::is_none")]
945 pub start_period: Option<u64>,
946
947 #[serde(skip_serializing_if = "Option::is_none")]
950 pub start_interval: Option<u64>,
951}
952
953impl HealthcheckConfig {
954 pub fn new() -> Self {
956 Default::default()
957 }
958
959 pub fn test(&mut self, test: Vec<String>) -> &mut Self {
961 self.test = Some(test);
962 self
963 }
964
965 pub fn interval(&mut self, interval: Duration) -> &mut Self {
968 self.interval = Some(interval.as_nanos() as u64);
969 self
970 }
971
972 pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
975 self.timeout = Some(timeout.as_nanos() as u64);
976 self
977 }
978
979 pub fn retries(&mut self, retries: u8) -> &mut Self {
981 self.retries = Some(retries);
982 self
983 }
984
985 pub fn start_period(&mut self, start_period: Duration) -> &mut Self {
988 self.start_period = Some(start_period.as_nanos() as u64);
989 self
990 }
991}
992
993#[derive(Debug, Clone, Deserialize, Serialize)]
995#[serde(rename_all = "PascalCase")]
996pub struct ContainerCreateOptions {
997 hostname: String,
998 domainname: String,
999 user: String,
1000 attach_stdin: bool,
1001 attach_stdout: bool,
1002 attach_stderr: bool,
1003 tty: bool,
1005 open_stdin: bool,
1006 stdin_once: bool,
1007 env: Vec<String>,
1008 #[serde(skip_serializing_if = "Vec::is_empty")]
1009 cmd: Vec<String>,
1010 #[serde(skip_serializing_if = "Vec::is_empty")]
1011 entrypoint: Vec<String>,
1012 image: String,
1013 labels: HashMap<String, String>,
1014 working_dir: PathBuf,
1017 network_disabled: bool,
1018 mac_address: String,
1019 on_build: Vec<String>,
1020 stop_signal: String,
1021 #[serde(with = "format::duration::DurationDelegate")]
1022 stop_timeout: Duration,
1023 host_config: Option<ContainerHostConfig>,
1024 networking_config: Option<NetworkingConfig>,
1025 exposed_ports: Option<ExposedPorts>,
1026 healthcheck: Option<HealthcheckConfig>,
1027}
1028
1029impl ContainerCreateOptions {
1030 pub fn new(image: &str) -> Self {
1031 Self {
1032 hostname: "".to_owned(),
1033 domainname: "".to_owned(),
1034 user: "".to_owned(),
1035 attach_stdin: false,
1036 attach_stdout: true,
1037 attach_stderr: true,
1038 tty: false,
1039 open_stdin: false,
1040 stdin_once: false,
1041 env: vec![],
1042 cmd: vec![],
1043 image: image.to_owned(),
1044 working_dir: PathBuf::new(),
1045 entrypoint: vec![],
1046 network_disabled: false,
1047 mac_address: "".to_owned(),
1048 on_build: vec![],
1049 labels: HashMap::new(),
1050 stop_signal: "SIGTERM".to_owned(),
1051 stop_timeout: Duration::from_secs(10),
1052 host_config: None,
1053 networking_config: None,
1054 exposed_ports: None,
1055 healthcheck: None,
1056 }
1057 }
1058
1059 pub fn hostname(&mut self, hostname: String) -> &mut Self {
1060 self.hostname = hostname;
1061 self
1062 }
1063
1064 pub fn domainname(&mut self, domainname: String) -> &mut Self {
1065 self.domainname = domainname;
1066 self
1067 }
1068
1069 pub fn user(&mut self, user: String) -> &mut Self {
1070 self.user = user;
1071 self
1072 }
1073
1074 pub fn attach_stdin(&mut self, attach_stdin: bool) -> &mut Self {
1075 self.attach_stdin = attach_stdin;
1076 self
1077 }
1078
1079 pub fn attach_stdout(&mut self, attach_stdout: bool) -> &mut Self {
1080 self.attach_stdout = attach_stdout;
1081 self
1082 }
1083
1084 pub fn attach_stderr(&mut self, attach_stderr: bool) -> &mut Self {
1085 self.attach_stderr = attach_stderr;
1086 self
1087 }
1088
1089 pub fn tty(&mut self, tty: bool) -> &mut Self {
1090 self.tty = tty;
1091 self
1092 }
1093
1094 pub fn open_stdin(&mut self, open_stdin: bool) -> &mut Self {
1095 self.open_stdin = open_stdin;
1096 self
1097 }
1098
1099 pub fn stdin_once(&mut self, stdin_once: bool) -> &mut Self {
1100 self.stdin_once = stdin_once;
1101 self
1102 }
1103
1104 pub fn env(&mut self, env: String) -> &mut Self {
1106 self.env.push(env);
1107 self
1108 }
1109
1110 pub fn cmd(&mut self, cmd: String) -> &mut Self {
1112 self.cmd.push(cmd);
1113 self
1114 }
1115
1116 pub fn entrypoint(&mut self, entrypoint: Vec<String>) -> &mut Self {
1118 self.entrypoint = entrypoint;
1119 self
1120 }
1121
1122 pub fn image(&mut self, image: String) -> &mut Self {
1123 self.image = image;
1124 self
1125 }
1126
1127 pub fn label(&mut self, key: String, value: String) -> &mut Self {
1129 self.labels.insert(key, value);
1130 self
1131 }
1132
1133 pub fn working_dir(&mut self, working_dir: PathBuf) -> &mut Self {
1134 self.working_dir = working_dir;
1135 self
1136 }
1137
1138 pub fn network_disabled(&mut self, network_disabled: bool) -> &mut Self {
1139 self.network_disabled = network_disabled;
1140 self
1141 }
1142
1143 pub fn mac_address(&mut self, mac_address: String) -> &mut Self {
1144 self.mac_address = mac_address;
1145 self
1146 }
1147
1148 pub fn on_build(&mut self, on_build: Vec<String>) -> &mut Self {
1149 self.on_build = on_build;
1150 self
1151 }
1152
1153 pub fn stop_signal(&mut self, stop_signal: String) -> &mut Self {
1154 self.stop_signal = stop_signal;
1155 self
1156 }
1157
1158 pub fn stop_timeout(&mut self, stop_timeout: Duration) -> &mut Self {
1159 self.stop_timeout = stop_timeout;
1160 self
1161 }
1162
1163 pub fn host_config(&mut self, host_config: ContainerHostConfig) -> &mut Self {
1164 self.host_config = Some(host_config);
1165 self
1166 }
1167
1168 pub fn networking_config(&mut self, networking_config: NetworkingConfig) -> &mut Self {
1169 self.networking_config = Some(networking_config);
1170 self
1171 }
1172
1173 pub fn exposed_ports(&mut self, exposed_ports: ExposedPorts) -> &mut Self {
1174 self.exposed_ports = Some(exposed_ports);
1175 self
1176 }
1177
1178 pub fn healthcheck(&mut self, healthcheck: HealthcheckConfig) -> &mut Self {
1179 self.healthcheck = Some(healthcheck);
1180 self
1181 }
1182}
1183
1184mod format {
1185 pub mod duration {
1186 use serde::{Deserialize, Serialize};
1187 use std::time::Duration;
1188
1189 #[derive(Serialize, Deserialize)]
1190 #[serde(remote = "Duration")]
1191 pub struct DurationDelegate(#[serde(getter = "Duration::as_secs")] u64);
1192
1193 impl From<DurationDelegate> for Duration {
1195 fn from(def: DurationDelegate) -> Duration {
1196 Duration::new(def.0, 0)
1197 }
1198 }
1199 }
1200}
1201
1202#[derive(Debug, Clone, Deserialize)]
1203#[serde(rename_all = "PascalCase")]
1204pub struct CreateContainerResponse {
1205 pub id: String,
1206 pub warnings: Option<Vec<String>>,
1207}
1208
1209#[derive(Debug, Clone, Deserialize)]
1210#[serde(rename_all = "PascalCase")]
1211pub struct CreateExecResponse {
1212 pub id: String,
1213}
1214
1215#[derive(Debug, Clone, Deserialize, Serialize)]
1217#[serde(rename_all = "PascalCase")]
1218pub struct CreateExecOptions {
1219 attach_stdin: bool,
1220 attach_stdout: bool,
1221 attach_stderr: bool,
1222 detach_keys: String,
1223 tty: bool,
1224 #[serde(skip_serializing_if = "Vec::is_empty")]
1225 env: Vec<String>,
1226 #[serde(skip_serializing_if = "Vec::is_empty")]
1227 cmd: Vec<String>,
1228 privileged: bool,
1229 user: String,
1230 working_dir: PathBuf,
1231}
1232
1233impl CreateExecOptions {
1234 pub fn new() -> Self {
1235 Self {
1236 attach_stdin: false,
1237 attach_stdout: true,
1238 attach_stderr: true,
1239 detach_keys: "".to_owned(),
1240 tty: false,
1241 env: vec![],
1242 cmd: vec![],
1243 privileged: false,
1244 user: "".to_owned(),
1245 working_dir: PathBuf::new(),
1246 }
1247 }
1248
1249 pub fn attach_stdin(&mut self, attach_stdin: bool) -> &mut Self {
1250 self.attach_stdin = attach_stdin;
1251 self
1252 }
1253
1254 pub fn attach_stdout(&mut self, attach_stdout: bool) -> &mut Self {
1255 self.attach_stdout = attach_stdout;
1256 self
1257 }
1258
1259 pub fn attach_stderr(&mut self, attach_stderr: bool) -> &mut Self {
1260 self.attach_stderr = attach_stderr;
1261 self
1262 }
1263
1264 pub fn tty(&mut self, tty: bool) -> &mut Self {
1265 self.tty = tty;
1266 self
1267 }
1268
1269 pub fn env(&mut self, env: String) -> &mut Self {
1270 self.env.push(env);
1271 self
1272 }
1273
1274 pub fn cmd(&mut self, cmd: String) -> &mut Self {
1276 self.cmd.push(cmd);
1277 self
1278 }
1279
1280 pub fn privileged(&mut self, privileged: bool) -> &mut Self {
1281 self.privileged = privileged;
1282 self
1283 }
1284
1285 pub fn user(&mut self, user: String) -> &mut Self {
1286 self.user = user;
1287 self
1288 }
1289
1290 pub fn working_dir(&mut self, working_dir: PathBuf) -> &mut Self {
1291 self.working_dir = working_dir;
1292 self
1293 }
1294}
1295
1296#[derive(Debug, Clone, Deserialize, Serialize)]
1298#[serde(rename_all = "PascalCase")]
1299pub struct StartExecOptions {
1300 detach: bool,
1301 tty: bool,
1302}
1303
1304impl StartExecOptions {
1305 pub fn new() -> Self {
1306 Self {
1307 detach: false,
1308 tty: false,
1309 }
1310 }
1311
1312 pub fn detach(&mut self, detach: bool) -> &mut Self {
1313 self.detach = detach;
1314 self
1315 }
1316
1317 pub fn tty(&mut self, tty: bool) -> &mut Self {
1318 self.tty = tty;
1319 self
1320 }
1321}
1322
1323#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
1325pub enum RemovedImage {
1326 Untagged(String),
1327 Deleted(String),
1328}
1329
1330#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
1332#[allow(non_snake_case)]
1333pub struct PrunedImages {
1334 #[serde(deserialize_with = "null_to_default")]
1335 ImagesDeleted: Vec<RemovedImage>,
1336 SpaceReclaimed: i64,
1337}
1338
1339#[derive(Debug, Clone, PartialEq, Eq, Default)]
1341pub struct ContainerPruneFilters {
1342 pub until: Vec<String>,
1343 pub label: Vec<String>,
1344}
1345
1346impl ContainerPruneFilters {
1347 pub fn new() -> Self {
1348 Self::default()
1349 }
1350
1351 pub fn is_empty(&self) -> bool {
1352 self.until.is_empty() && self.label.is_empty()
1353 }
1354
1355 pub fn until(&mut self, until: String) -> &mut Self {
1356 self.until.push(until);
1357 self
1358 }
1359
1360 pub fn label(&mut self, label: String) -> &mut Self {
1361 self.label.push(label);
1362 self
1363 }
1364}
1365
1366impl Serialize for ContainerPruneFilters {
1367 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1368 where
1369 S: serde::Serializer,
1370 {
1371 use serde::ser::SerializeMap;
1372 let mut map = serializer.serialize_map(None)?;
1373
1374 if !self.until.is_empty() {
1375 map.serialize_entry("until", &self.until)?;
1376 }
1377 if !self.label.is_empty() {
1378 map.serialize_entry("label", &self.label)?;
1379 }
1380
1381 map.end()
1382 }
1383}
1384
1385#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
1387#[serde(rename_all = "PascalCase")]
1388pub struct PrunedContainers {
1389 #[serde(deserialize_with = "null_to_default")]
1390 pub containers_deleted: Vec<String>,
1391 pub space_reclaimed: i64,
1392}
1393
1394#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
1396#[serde(rename_all = "PascalCase")]
1397pub struct ImageLayer {
1398 pub id: Option<String>,
1399 pub created: i64,
1400 pub created_by: String,
1401 pub tags: Option<Vec<String>>,
1402 pub size: u64,
1403 pub comment: String,
1404}
1405
1406#[derive(Debug, PartialEq, PartialOrd, Serialize, Default)]
1407pub struct EventFilters {
1408 #[serde(skip_serializing_if = "Vec::is_empty")]
1409 config: Vec<String>,
1410 #[serde(skip_serializing_if = "Vec::is_empty")]
1411 container: Vec<String>,
1412 #[serde(skip_serializing_if = "Vec::is_empty")]
1413 daemon: Vec<String>,
1414 #[serde(skip_serializing_if = "Vec::is_empty")]
1415 event: Vec<String>,
1416 #[serde(skip_serializing_if = "Vec::is_empty")]
1417 image: Vec<String>,
1418 #[serde(skip_serializing_if = "Vec::is_empty")]
1419 label: Vec<String>,
1420 #[serde(skip_serializing_if = "Vec::is_empty")]
1421 network: Vec<String>,
1422 #[serde(skip_serializing_if = "Vec::is_empty")]
1423 node: Vec<String>,
1424 #[serde(skip_serializing_if = "Vec::is_empty")]
1425 plugin: Vec<String>,
1426 #[serde(skip_serializing_if = "Vec::is_empty")]
1427 scope: Vec<String>,
1428 #[serde(skip_serializing_if = "Vec::is_empty")]
1429 secret: Vec<String>,
1430 #[serde(skip_serializing_if = "Vec::is_empty")]
1431 service: Vec<String>,
1432 #[serde(skip_serializing_if = "Vec::is_empty")]
1433 #[serde(rename = "type")]
1434 type_: Vec<String>,
1435 #[serde(skip_serializing_if = "Vec::is_empty")]
1436 volume: Vec<String>,
1437}
1438
1439impl EventFilters {
1440 pub fn new() -> Self {
1441 Self::default()
1442 }
1443
1444 pub fn config(&mut self, config: &str) -> &mut Self {
1445 self.config.push(config.to_owned());
1446 self
1447 }
1448
1449 pub fn container(&mut self, container: &str) -> &mut Self {
1450 self.container.push(container.to_owned());
1451 self
1452 }
1453
1454 pub fn daemon(&mut self, daemon: &str) -> &mut Self {
1455 self.daemon.push(daemon.to_owned());
1456 self
1457 }
1458
1459 pub fn event(&mut self, event: &str) -> &mut Self {
1460 self.event.push(event.to_owned());
1461 self
1462 }
1463
1464 pub fn image(&mut self, image: &str) -> &mut Self {
1465 self.image.push(image.to_owned());
1466 self
1467 }
1468
1469 pub fn label(&mut self, label: &str) -> &mut Self {
1470 self.label.push(label.to_owned());
1471 self
1472 }
1473
1474 pub fn network(&mut self, network: &str) -> &mut Self {
1475 self.network.push(network.to_owned());
1476 self
1477 }
1478
1479 pub fn node(&mut self, node: &str) -> &mut Self {
1480 self.node.push(node.to_owned());
1481 self
1482 }
1483
1484 pub fn plugin(&mut self, plugin: &str) -> &mut Self {
1485 self.plugin.push(plugin.to_owned());
1486 self
1487 }
1488
1489 pub fn scope(&mut self, scope: &str) -> &mut Self {
1490 self.scope.push(scope.to_owned());
1491 self
1492 }
1493
1494 pub fn secret(&mut self, secret: &str) -> &mut Self {
1495 self.secret.push(secret.to_owned());
1496 self
1497 }
1498
1499 pub fn service(&mut self, service: &str) -> &mut Self {
1500 self.service.push(service.to_owned());
1501 self
1502 }
1503
1504 pub fn type_(&mut self, type_: &str) -> &mut Self {
1505 self.type_.push(type_.to_owned());
1506 self
1507 }
1508
1509 pub fn volume(&mut self, volume: &str) -> &mut Self {
1510 self.volume.push(volume.to_owned());
1511 self
1512 }
1513}