Skip to main content

dockworker/
options.rs

1//! Options which can be passed to various `Docker` commands.
2#![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/// Options for `Docker::containers`.  This uses a "builder" pattern, so
24/// most methods will consume the object and return a new one.
25#[derive(Debug, Clone, Default)]
26pub struct ContainerListOptions {
27    all: bool,
28    //before: Option<String>,
29    //filter: Filter,
30    latest: bool,
31    limit: Option<u64>,
32    //since: Option<String>,
33    size: bool,
34}
35
36impl ContainerListOptions {
37    /// Return all containers, including stopped ones.
38    pub fn all(mut self) -> Self {
39        self.all = true;
40        self
41    }
42
43    /// Return just the most-recently-started container (even if it has
44    /// stopped).
45    pub fn latest(mut self) -> Self {
46        self.latest = true;
47        self
48    }
49
50    /// Limit the number of containers we return.
51    pub fn limit(mut self, n: u64) -> Self {
52        self.limit = Some(n);
53        self
54    }
55
56    /// Calculate the total file sizes for our containers.  **WARNING:**
57    /// This is very expensive.
58    pub fn size(mut self) -> Self {
59        self.size = true;
60        self
61    }
62
63    /// Convert to URL parameters.
64    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/// Restart policy of a container.
83#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
84#[allow(non_snake_case)]
85pub struct RestartPolicy {
86    /// Restart type
87    /// This option can be "no", "always", "on-failure" or "unless-stopped"
88    pub Name: String,
89    /// Maximum retry count. This value is used only when "on-failure" mode
90    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    /// combination of r,w,m
209    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/// network name to EndpointConfig
549#[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", &timestamps.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    /// Path within the build context to the Dockerfile.
603    /// This is ignored if remote is specified and points to an external Dockerfile.
604    pub dockerfile: String,
605
606    /// A name and optional tag to apply to the image in the name:tag format.
607    /// If you omit the tag the default latest value is assumed. You can provide several t parameters.
608    pub t: Vec<String>,
609
610    /// Extra hosts to add to /etc/hosts
611    pub extrahosts: Option<String>,
612
613    /// A Git repository URI or HTTP/HTTPS context URI
614    pub remote: Option<String>,
615
616    /// Suppress verbose build output.
617    pub q: bool,
618
619    /// Do not use the cache when building the image.
620    pub nocache: bool,
621
622    /// JSON array of images used for build cache resolution.
623    pub cachefrom: Option<Vec<String>>,
624
625    /// Attempt to pull the image even if an older image exists locally.
626    pub pull: Option<String>,
627
628    /// Remove intermediate containers after a successful build.
629    pub rm: bool,
630
631    /// Always remove intermediate containers, even upon failure.
632    pub forcerm: bool,
633
634    /// Set memory limit for build.
635    pub memory: Option<u64>,
636
637    /// Total memory (memory + swap). Set as -1 to disable swap.
638    pub memswap: Option<i64>,
639
640    /// CPU shares (relative weight).
641    pub cpushares: Option<u64>,
642
643    /// CPUs in which to allow execution (e.g., 0-3, 0,1).
644    pub cpusetcpus: Option<String>,
645
646    /// The length of a CPU period in microseconds.
647    pub cpuperiod: Option<u64>,
648
649    /// Microseconds of CPU time that the container can get in a CPU period.
650    pub cpuquota: Option<u64>,
651
652    /// JSON map of string pairs for build-time variables.
653    /// This is not meant for passing secret values.
654    pub buildargs: Option<HashMap<String, String>>,
655
656    /// Size of /dev/shm in bytes. The size must be greater than 0. If omitted the system uses 64MB.
657    pub shmsize: Option<u64>,
658
659    /// Squash the resulting images layers into a single layer. (Experimental release only.)
660    pub squash: Option<bool>,
661
662    /// Arbitrary key/value labels to set on the image, as a JSON map of string pairs.
663    pub labels: Option<HashMap<String, String>>,
664
665    /// Sets the networking mode for the run commands during build.
666    /// Supported standard values are: bridge, host, none, and container:<name|id>.
667    /// Any other value is taken as a custom network's name to which this container should connect to.
668    pub networkmode: Option<String>,
669
670    /// Platform in the format os[/arch[/variant]]
671    pub platform: String,
672}
673
674impl ContainerBuildOptions {
675    /// Convert to URL parameters.
676    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    // hashmapのkey順序は不定であるため,json_valueに変換してから比較が必要
820    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    // hashmapのkey順序は不定であるため,hash_setに変換してから比較する
830    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    // hashmapのkey順序は不定であるため,json_valueに変換してから比較が必要
897    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    // hashmapのkey順序は不定であるため,hash_setに変換してから比較する
908    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/// Configuration for container healthchecks.
916#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
917#[serde(rename_all = "PascalCase")]
918pub struct HealthcheckConfig {
919    /// The test to perform. Possible values are:
920    /// - `[]` inherit healthcheck from image or parent image
921    /// - `["NONE"]` disable healthcheck
922    /// - `["CMD", args...]` exec arguments directly
923    /// - `["CMD-SHELL", command]` run command with system's default shell
924    #[serde(skip_serializing_if = "Option::is_none")]
925    pub test: Option<Vec<String>>,
926
927    /// The time to wait between checks in nanoseconds. It should be 0 or at least 1_000_000 (1 ms).
928    /// 0 means inherit.
929    #[serde(skip_serializing_if = "Option::is_none")]
930    pub interval: Option<u64>,
931
932    /// The time to wait before considering the check to have hung in nanoseconds.
933    /// It should be 0 or at least 1_000_000 (1 ms). 0 means inherit.
934    #[serde(skip_serializing_if = "Option::is_none")]
935    pub timeout: Option<u64>,
936
937    /// The number of consecutive failures needed to consider a container as unhealthy.
938    /// 0 means inherit.
939    #[serde(skip_serializing_if = "Option::is_none")]
940    pub retries: Option<u8>,
941
942    /// Start period for the container to initialize before starting health-retries countdown in nanoseconds.
943    /// It should be 0 or at least 1_000_000 (1 ms). 0 means inherit.
944    #[serde(skip_serializing_if = "Option::is_none")]
945    pub start_period: Option<u64>,
946
947    /// The time to wait between checks in nanoseconds during the start period.
948    /// It should be 0 or at least 1_000_000 (1 ms). 0 means inherit.
949    #[serde(skip_serializing_if = "Option::is_none")]
950    pub start_interval: Option<u64>,
951}
952
953impl HealthcheckConfig {
954    /// Creates a new empty HealthConfig.
955    pub fn new() -> Self {
956        Default::default()
957    }
958
959    /// Sets the test command for the healthcheck.
960    pub fn test(&mut self, test: Vec<String>) -> &mut Self {
961        self.test = Some(test);
962        self
963    }
964
965    /// Sets the interval between health checks.
966    /// The duration will be converted to nanoseconds.
967    pub fn interval(&mut self, interval: Duration) -> &mut Self {
968        self.interval = Some(interval.as_nanos() as u64);
969        self
970    }
971
972    /// Sets the timeout for a single health check.
973    /// The duration will be converted to nanoseconds.
974    pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
975        self.timeout = Some(timeout.as_nanos() as u64);
976        self
977    }
978
979    /// Sets the number of retries before marking the container as unhealthy.
980    pub fn retries(&mut self, retries: u8) -> &mut Self {
981        self.retries = Some(retries);
982        self
983    }
984
985    /// Sets the start period for the container. Health checks will not be considered failed during this period.
986    /// The duration will be converted to nanoseconds.
987    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/// request body of /containers/create api
994#[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    // exposed_ports: HashMap<String, Any>, not sure the type that this would need to be
1004    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    // volumes: HashMap<String, Any>, not sure the type that this would need to be.
1015    // healthcheck: Not sure the type that this would be
1016    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    /// push back an envvar entry
1105    pub fn env(&mut self, env: String) -> &mut Self {
1106        self.env.push(env);
1107        self
1108    }
1109
1110    /// push back a cmd argment
1111    pub fn cmd(&mut self, cmd: String) -> &mut Self {
1112        self.cmd.push(cmd);
1113        self
1114    }
1115
1116    /// update entrypoint
1117    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    /// add a label/value pair
1128    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        // Provide a conversion to construct the remote type.
1194        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/// request body of /containers/Create an exec instance
1216#[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    /// push back a cmd argment
1275    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/// request body of /exec/start an exec instance
1297#[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/// Response of the removing image api
1324#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
1325pub enum RemovedImage {
1326    Untagged(String),
1327    Deleted(String),
1328}
1329
1330/// Response of the prune image api
1331#[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/// Container prune filters
1340#[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/// Response of the prune containers api
1386#[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/// Response of the history image api
1395#[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}