docker_wrapper/command/
run.rs

1//! Docker run command implementation.
2//!
3//! This module provides a comprehensive implementation of the `docker run` command
4//! with support for common options and an extensible architecture for any additional options.
5
6use super::{CommandExecutor, DockerCommand, EnvironmentBuilder, PortBuilder};
7use crate::error::{Error, Result};
8use crate::stream::{OutputLine, StreamResult, StreamableCommand};
9use async_trait::async_trait;
10use std::path::PathBuf;
11use tokio::process::Command as TokioCommand;
12use tokio::sync::mpsc;
13
14/// Docker run command builder with fluent API
15#[derive(Debug, Clone)]
16#[allow(clippy::struct_excessive_bools)]
17pub struct RunCommand {
18    /// The Docker image to run
19    image: String,
20    /// Command executor for extensibility
21    pub executor: CommandExecutor,
22    /// Container name
23    name: Option<String>,
24    /// Run in detached mode
25    detach: bool,
26    /// Environment variables
27    environment: EnvironmentBuilder,
28    /// Port mappings
29    ports: PortBuilder,
30    /// Volume mounts
31    volumes: Vec<VolumeMount>,
32    /// Working directory
33    workdir: Option<PathBuf>,
34    /// Entrypoint override
35    entrypoint: Option<String>,
36    /// Command to run in container
37    command: Option<Vec<String>>,
38    /// Interactive mode
39    interactive: bool,
40    /// Allocate TTY
41    tty: bool,
42    /// Remove container on exit
43    remove: bool,
44
45    // Resource Limits
46    /// Memory limit
47    memory: Option<String>,
48    /// Number of CPUs
49    cpus: Option<String>,
50    /// CPU shares (relative weight)
51    cpu_shares: Option<i64>,
52    /// CPU CFS period
53    cpu_period: Option<i64>,
54    /// CPU CFS quota
55    cpu_quota: Option<i64>,
56    /// CPUs in which to allow execution
57    cpuset_cpus: Option<String>,
58    /// MEMs in which to allow execution
59    cpuset_mems: Option<String>,
60    /// Memory + swap limit
61    memory_swap: Option<String>,
62    /// Memory soft limit
63    memory_reservation: Option<String>,
64
65    // Security & User Context
66    /// Username or UID
67    user: Option<String>,
68    /// Give extended privileges
69    privileged: bool,
70    /// Container host name
71    hostname: Option<String>,
72
73    // Lifecycle Management
74    /// Restart policy
75    restart: Option<String>,
76
77    // System Integration
78    /// Set platform if server is multi-platform capable
79    platform: Option<String>,
80    /// Runtime to use for this container
81    runtime: Option<String>,
82    /// Container isolation technology
83    isolation: Option<String>,
84    /// Pull image before running
85    pull: Option<String>,
86    /// Write the container ID to the file
87    cidfile: Option<String>,
88    /// Container NIS domain name
89    domainname: Option<String>,
90    /// Container MAC address
91    mac_address: Option<String>,
92
93    // Logging & Drivers
94    /// Logging driver for the container
95    log_driver: Option<String>,
96    /// Optional volume driver for the container
97    volume_driver: Option<String>,
98
99    // Namespaces
100    /// User namespace to use
101    userns: Option<String>,
102    /// UTS namespace to use
103    uts: Option<String>,
104    /// PID namespace to use
105    pid: Option<String>,
106    /// IPC mode to use
107    ipc: Option<String>,
108    /// Cgroup namespace to use
109    cgroupns: Option<String>,
110    /// Optional parent cgroup for the container
111    cgroup_parent: Option<String>,
112
113    // Advanced Memory & Performance
114    /// Kernel memory limit
115    kernel_memory: Option<String>,
116    /// Tune container memory swappiness (0 to 100)
117    memory_swappiness: Option<i32>,
118    /// Tune host's OOM preferences (-1000 to 1000)
119    oom_score_adj: Option<i32>,
120    /// Tune container pids limit
121    pids_limit: Option<i64>,
122    /// Size of /dev/shm
123    shm_size: Option<String>,
124
125    // Process Control
126    /// Signal to stop the container
127    stop_signal: Option<String>,
128    /// Timeout (in seconds) to stop a container
129    stop_timeout: Option<i32>,
130    /// Override the key sequence for detaching a container
131    detach_keys: Option<String>,
132
133    // Simple Flags
134    /// Proxy received signals to the process
135    sig_proxy: bool,
136    /// Mount the container's root filesystem as read only
137    read_only: bool,
138    /// Run an init inside the container
139    init: bool,
140    /// Disable OOM Killer
141    oom_kill_disable: bool,
142    /// Disable any container-specified HEALTHCHECK
143    no_healthcheck: bool,
144    /// Skip image verification
145    disable_content_trust: bool,
146    /// Publish all exposed ports to random ports
147    publish_all: bool,
148    /// Suppress the pull output
149    quiet: bool,
150
151    // High-Impact List Options
152    // DNS & Network
153    /// Custom DNS servers
154    dns: Vec<String>,
155    /// DNS options
156    dns_option: Vec<String>,
157    /// DNS search domains
158    dns_search: Vec<String>,
159    /// Add host-to-IP mappings (host:ip)
160    add_host: Vec<String>,
161
162    // Security & Capabilities
163    /// Add Linux capabilities
164    cap_add: Vec<String>,
165    /// Drop Linux capabilities
166    cap_drop: Vec<String>,
167    /// Security options
168    security_opt: Vec<String>,
169
170    // Device & Filesystem
171    /// Add host devices to container
172    device: Vec<String>,
173    /// Mount tmpfs directories
174    tmpfs: Vec<String>,
175    /// Expose ports without publishing them
176    expose: Vec<String>,
177
178    // Environment & Labels
179    /// Read environment from files
180    env_file: Vec<PathBuf>,
181    /// Set metadata labels
182    label: Vec<String>,
183    /// Read labels from files
184    label_file: Vec<PathBuf>,
185
186    // Additional List/Vec Options
187    /// Network aliases for the container
188    network_alias: Vec<String>,
189    /// Additional groups for the user
190    group_add: Vec<String>,
191    /// Attach to STDIN, STDOUT or STDERR
192    attach: Vec<String>,
193    /// Log driver options
194    log_opt: Vec<String>,
195    /// Storage driver options
196    storage_opt: Vec<String>,
197    /// Ulimit options
198    ulimit: Vec<String>,
199    /// Mount volumes from other containers
200    volumes_from: Vec<String>,
201    /// Add link to another container (deprecated)
202    link: Vec<String>,
203    /// Container IPv4/IPv6 link-local addresses
204    link_local_ip: Vec<String>,
205
206    // Health Check Options
207    // Health checks
208    /// Command to run to check health
209    health_cmd: Option<String>,
210    /// Time between running the check (ms|s|m|h)
211    health_interval: Option<String>,
212    /// Consecutive failures needed to report unhealthy
213    health_retries: Option<i32>,
214    /// Maximum time to allow one check to run (ms|s|m|h)
215    health_timeout: Option<String>,
216    /// Start period for the container to initialize before health-checking (ms|s|m|h)
217    health_start_period: Option<String>,
218    /// Time between health checks during the start period (ms|s|m|h)
219    health_start_interval: Option<String>,
220
221    // Advanced options
222    /// Advanced mount configuration
223    mount: Vec<String>,
224    /// Connect to a network
225    network: Vec<String>,
226    /// GPU devices to add to the container
227    gpus: Option<String>,
228
229    // Map-based options (stored as Vec<String> in key=value format)
230    /// Add custom annotations
231    annotation: Vec<String>,
232    /// Kernel parameters to set
233    sysctl: Vec<String>,
234
235    // Advanced System Options
236    // Block I/O controls
237    /// Block IO weight (relative weight)
238    blkio_weight: Option<u16>,
239    /// Block IO weight per device
240    blkio_weight_device: Vec<String>,
241    /// Limit read rate (bytes per second) from a device
242    device_read_bps: Vec<String>,
243    /// Limit write rate (bytes per second) to a device
244    device_write_bps: Vec<String>,
245    /// Limit read rate (IO per second) from a device
246    device_read_iops: Vec<String>,
247    /// Limit write rate (IO per second) to a device
248    device_write_iops: Vec<String>,
249
250    // Real-time CPU scheduling
251    /// Limit CPU real-time period in microseconds
252    cpu_rt_period: Option<i64>,
253    /// Limit CPU real-time runtime in microseconds
254    cpu_rt_runtime: Option<i64>,
255
256    // Advanced networking
257    /// Container IPv4 address
258    ip: Option<String>,
259    /// Container IPv6 address
260    ip6: Option<String>,
261
262    // Advanced system options
263    /// Cgroup rule for devices
264    device_cgroup_rule: Vec<String>,
265}
266
267/// Volume mount configuration
268#[derive(Debug, Clone)]
269pub struct VolumeMount {
270    /// Source path on host or volume name
271    pub source: String,
272    /// Target path in container
273    pub target: String,
274    /// Mount type (bind, volume, tmpfs)
275    pub mount_type: MountType,
276    /// Read-only mount
277    pub readonly: bool,
278}
279
280/// Type of volume mount
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282pub enum MountType {
283    /// Bind mount from host filesystem
284    Bind,
285    /// Named volume
286    Volume,
287    /// Temporary filesystem
288    Tmpfs,
289}
290
291impl std::fmt::Display for VolumeMount {
292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293        let readonly_suffix = if self.readonly { ":ro" } else { "" };
294        write!(f, "{}:{}{}", self.source, self.target, readonly_suffix)
295    }
296}
297
298/// Container ID returned by docker run
299#[derive(Debug, Clone, PartialEq, Eq)]
300pub struct ContainerId(pub String);
301
302impl ContainerId {
303    /// Get the container ID as a string
304    #[must_use]
305    pub fn as_str(&self) -> &str {
306        &self.0
307    }
308
309    /// Get the short form of the container ID (first 12 characters)
310    #[must_use]
311    pub fn short(&self) -> &str {
312        if self.0.len() >= 12 {
313            &self.0[..12]
314        } else {
315            &self.0
316        }
317    }
318}
319
320impl std::fmt::Display for ContainerId {
321    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
322        write!(f, "{}", self.0)
323    }
324}
325
326impl RunCommand {
327    /// Create a new run command for the specified image
328    #[allow(clippy::too_many_lines)]
329    pub fn new(image: impl Into<String>) -> Self {
330        Self {
331            image: image.into(),
332            executor: CommandExecutor::new(),
333            name: None,
334            detach: false,
335            environment: EnvironmentBuilder::new(),
336            ports: PortBuilder::new(),
337            volumes: Vec::new(),
338            workdir: None,
339            entrypoint: None,
340            command: None,
341            interactive: false,
342            tty: false,
343            remove: false,
344
345            // Resource Limits
346            memory: None,
347            cpus: None,
348            cpu_shares: None,
349            cpu_period: None,
350            cpu_quota: None,
351            cpuset_cpus: None,
352            cpuset_mems: None,
353            memory_swap: None,
354            memory_reservation: None,
355
356            // Security & User Context
357            user: None,
358            privileged: false,
359            hostname: None,
360
361            // Lifecycle Management
362            restart: None,
363
364            // System Integration
365            platform: None,
366            runtime: None,
367            isolation: None,
368            pull: None,
369            cidfile: None,
370            domainname: None,
371            mac_address: None,
372
373            // Logging & Drivers
374            log_driver: None,
375            volume_driver: None,
376
377            // Namespaces
378            userns: None,
379            uts: None,
380            pid: None,
381            ipc: None,
382            cgroupns: None,
383            cgroup_parent: None,
384
385            // Advanced Memory & Performance
386            kernel_memory: None,
387            memory_swappiness: None,
388            oom_score_adj: None,
389            pids_limit: None,
390            shm_size: None,
391
392            // Process Control
393            stop_signal: None,
394            stop_timeout: None,
395            detach_keys: None,
396
397            // Simple Flags
398            sig_proxy: true, // Default is true in Docker
399            read_only: false,
400            init: false,
401            oom_kill_disable: false,
402            no_healthcheck: false,
403            disable_content_trust: true, // Default is true in Docker
404            publish_all: false,
405            quiet: false,
406
407            // High-Impact List Options
408            // DNS & Network
409            dns: Vec::new(),
410            dns_option: Vec::new(),
411            dns_search: Vec::new(),
412            add_host: Vec::new(),
413
414            // Security & Capabilities
415            cap_add: Vec::new(),
416            cap_drop: Vec::new(),
417            security_opt: Vec::new(),
418
419            // Device & Filesystem
420            device: Vec::new(),
421            tmpfs: Vec::new(),
422            expose: Vec::new(),
423
424            // Environment & Labels
425            env_file: Vec::new(),
426            label: Vec::new(),
427            label_file: Vec::new(),
428
429            // Additional List/Vec Options
430            network_alias: Vec::new(),
431            group_add: Vec::new(),
432            attach: Vec::new(),
433            log_opt: Vec::new(),
434            storage_opt: Vec::new(),
435            ulimit: Vec::new(),
436            volumes_from: Vec::new(),
437            link: Vec::new(),
438            link_local_ip: Vec::new(),
439
440            // Health Check Options
441            health_cmd: None,
442            health_interval: None,
443            health_retries: None,
444            health_timeout: None,
445            health_start_period: None,
446            health_start_interval: None,
447            mount: Vec::new(),
448            network: Vec::new(),
449            gpus: None,
450            annotation: Vec::new(),
451            sysctl: Vec::new(),
452
453            // Advanced System Options
454            blkio_weight: None,
455            blkio_weight_device: Vec::new(),
456            device_read_bps: Vec::new(),
457            device_write_bps: Vec::new(),
458            device_read_iops: Vec::new(),
459            device_write_iops: Vec::new(),
460            cpu_rt_period: None,
461            cpu_rt_runtime: None,
462            ip: None,
463            ip6: None,
464            device_cgroup_rule: Vec::new(),
465        }
466    }
467
468    /// Set the container name
469    #[must_use]
470    pub fn name(mut self, name: impl Into<String>) -> Self {
471        self.name = Some(name.into());
472        self
473    }
474
475    /// Run in detached mode (background)
476    #[must_use]
477    pub fn detach(mut self) -> Self {
478        self.detach = true;
479        self
480    }
481
482    /// Add an environment variable
483    #[must_use]
484    pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
485        self.environment = self.environment.var(key, value);
486        self
487    }
488
489    /// Add multiple environment variables
490    #[must_use]
491    pub fn envs(mut self, vars: std::collections::HashMap<String, String>) -> Self {
492        self.environment = self.environment.vars(vars);
493        self
494    }
495
496    /// Add a port mapping
497    #[must_use]
498    pub fn port(mut self, host_port: u16, container_port: u16) -> Self {
499        self.ports = self.ports.port(host_port, container_port);
500        self
501    }
502
503    /// Add a dynamic port mapping (Docker assigns host port)
504    #[must_use]
505    pub fn dynamic_port(mut self, container_port: u16) -> Self {
506        self.ports = self.ports.dynamic_port(container_port);
507        self
508    }
509
510    /// Add a volume mount
511    #[must_use]
512    pub fn volume(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
513        self.volumes.push(VolumeMount {
514            source: source.into(),
515            target: target.into(),
516            mount_type: MountType::Volume,
517            readonly: false,
518        });
519        self
520    }
521
522    /// Add a bind mount
523    #[must_use]
524    pub fn bind(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
525        self.volumes.push(VolumeMount {
526            source: source.into(),
527            target: target.into(),
528            mount_type: MountType::Bind,
529            readonly: false,
530        });
531        self
532    }
533
534    /// Add a read-only volume mount
535    #[must_use]
536    pub fn volume_ro(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
537        self.volumes.push(VolumeMount {
538            source: source.into(),
539            target: target.into(),
540            mount_type: MountType::Volume,
541            readonly: true,
542        });
543        self
544    }
545
546    /// Set working directory
547    #[must_use]
548    pub fn workdir(mut self, workdir: impl Into<PathBuf>) -> Self {
549        self.workdir = Some(workdir.into());
550        self
551    }
552
553    /// Override entrypoint
554    #[must_use]
555    pub fn entrypoint(mut self, entrypoint: impl Into<String>) -> Self {
556        self.entrypoint = Some(entrypoint.into());
557        self
558    }
559
560    /// Set command to run in container
561    #[must_use]
562    pub fn cmd(mut self, command: Vec<String>) -> Self {
563        self.command = Some(command);
564        self
565    }
566
567    /// Enable interactive mode
568    #[must_use]
569    pub fn interactive(mut self) -> Self {
570        self.interactive = true;
571        self
572    }
573
574    /// Allocate a TTY
575    #[must_use]
576    pub fn tty(mut self) -> Self {
577        self.tty = true;
578        self
579    }
580
581    /// Remove container automatically when it exits
582    #[must_use]
583    pub fn remove(mut self) -> Self {
584        self.remove = true;
585        self
586    }
587
588    /// Convenience method for interactive TTY mode
589    #[must_use]
590    pub fn it(self) -> Self {
591        self.interactive().tty()
592    }
593
594    // Resource Limits
595    /// Set memory limit (e.g., "1g", "512m")
596    #[must_use]
597    pub fn memory(mut self, memory: impl Into<String>) -> Self {
598        self.memory = Some(memory.into());
599        self
600    }
601
602    /// Set number of CPUs (e.g., "2.0", "1.5")
603    #[must_use]
604    pub fn cpus(mut self, cpus: impl Into<String>) -> Self {
605        self.cpus = Some(cpus.into());
606        self
607    }
608
609    /// Set CPU shares (relative weight)
610    #[must_use]
611    pub fn cpu_shares(mut self, shares: i64) -> Self {
612        self.cpu_shares = Some(shares);
613        self
614    }
615
616    /// Set CPU CFS period in microseconds
617    #[must_use]
618    pub fn cpu_period(mut self, period: i64) -> Self {
619        self.cpu_period = Some(period);
620        self
621    }
622
623    /// Set CPU CFS quota in microseconds
624    #[must_use]
625    pub fn cpu_quota(mut self, quota: i64) -> Self {
626        self.cpu_quota = Some(quota);
627        self
628    }
629
630    /// Set CPUs in which to allow execution (e.g., "0-3", "0,1")
631    #[must_use]
632    pub fn cpuset_cpus(mut self, cpus: impl Into<String>) -> Self {
633        self.cpuset_cpus = Some(cpus.into());
634        self
635    }
636
637    /// Set MEMs in which to allow execution (e.g., "0-3", "0,1")
638    #[must_use]
639    pub fn cpuset_mems(mut self, mems: impl Into<String>) -> Self {
640        self.cpuset_mems = Some(mems.into());
641        self
642    }
643
644    /// Set memory + swap limit (e.g., "2g", "-1" for unlimited)
645    #[must_use]
646    pub fn memory_swap(mut self, swap: impl Into<String>) -> Self {
647        self.memory_swap = Some(swap.into());
648        self
649    }
650
651    /// Set memory soft limit (e.g., "500m")
652    #[must_use]
653    pub fn memory_reservation(mut self, reservation: impl Into<String>) -> Self {
654        self.memory_reservation = Some(reservation.into());
655        self
656    }
657
658    // Security & User Context
659    /// Set username or UID (format: <name|uid>[:<group|gid>])
660    #[must_use]
661    pub fn user(mut self, user: impl Into<String>) -> Self {
662        self.user = Some(user.into());
663        self
664    }
665
666    /// Give extended privileges to this container
667    #[must_use]
668    pub fn privileged(mut self) -> Self {
669        self.privileged = true;
670        self
671    }
672
673    /// Set container host name
674    #[must_use]
675    pub fn hostname(mut self, hostname: impl Into<String>) -> Self {
676        self.hostname = Some(hostname.into());
677        self
678    }
679
680    // Lifecycle Management
681    /// Set restart policy (e.g., "always", "unless-stopped", "on-failure", "no")
682    #[must_use]
683    pub fn restart(mut self, restart: impl Into<String>) -> Self {
684        self.restart = Some(restart.into());
685        self
686    }
687
688    // System Integration
689    /// Set platform if server is multi-platform capable (e.g., "linux/amd64")
690    #[must_use]
691    pub fn platform(mut self, platform: impl Into<String>) -> Self {
692        self.platform = Some(platform.into());
693        self
694    }
695
696    /// Set runtime to use for this container
697    #[must_use]
698    pub fn runtime(mut self, runtime: impl Into<String>) -> Self {
699        self.runtime = Some(runtime.into());
700        self
701    }
702
703    /// Set container isolation technology
704    #[must_use]
705    pub fn isolation(mut self, isolation: impl Into<String>) -> Self {
706        self.isolation = Some(isolation.into());
707        self
708    }
709
710    /// Set pull image policy ("always", "missing", "never")
711    #[must_use]
712    pub fn pull(mut self, pull: impl Into<String>) -> Self {
713        self.pull = Some(pull.into());
714        self
715    }
716
717    /// Write the container ID to the specified file
718    #[must_use]
719    pub fn cidfile(mut self, cidfile: impl Into<String>) -> Self {
720        self.cidfile = Some(cidfile.into());
721        self
722    }
723
724    /// Set container NIS domain name
725    #[must_use]
726    pub fn domainname(mut self, domainname: impl Into<String>) -> Self {
727        self.domainname = Some(domainname.into());
728        self
729    }
730
731    /// Set container MAC address (e.g., "92:d0:c6:0a:29:33")
732    #[must_use]
733    pub fn mac_address(mut self, mac: impl Into<String>) -> Self {
734        self.mac_address = Some(mac.into());
735        self
736    }
737
738    // Logging & Drivers
739    /// Set logging driver for the container
740    #[must_use]
741    pub fn log_driver(mut self, driver: impl Into<String>) -> Self {
742        self.log_driver = Some(driver.into());
743        self
744    }
745
746    /// Set optional volume driver for the container
747    #[must_use]
748    pub fn volume_driver(mut self, driver: impl Into<String>) -> Self {
749        self.volume_driver = Some(driver.into());
750        self
751    }
752
753    // Namespaces
754    /// Set user namespace to use
755    #[must_use]
756    pub fn userns(mut self, userns: impl Into<String>) -> Self {
757        self.userns = Some(userns.into());
758        self
759    }
760
761    /// Set UTS namespace to use
762    #[must_use]
763    pub fn uts(mut self, uts: impl Into<String>) -> Self {
764        self.uts = Some(uts.into());
765        self
766    }
767
768    /// Set PID namespace to use
769    #[must_use]
770    pub fn pid(mut self, pid: impl Into<String>) -> Self {
771        self.pid = Some(pid.into());
772        self
773    }
774
775    /// Set IPC mode to use
776    #[must_use]
777    pub fn ipc(mut self, ipc: impl Into<String>) -> Self {
778        self.ipc = Some(ipc.into());
779        self
780    }
781
782    /// Set cgroup namespace to use (host|private)
783    #[must_use]
784    pub fn cgroupns(mut self, cgroupns: impl Into<String>) -> Self {
785        self.cgroupns = Some(cgroupns.into());
786        self
787    }
788
789    /// Set optional parent cgroup for the container
790    #[must_use]
791    pub fn cgroup_parent(mut self, parent: impl Into<String>) -> Self {
792        self.cgroup_parent = Some(parent.into());
793        self
794    }
795
796    // Advanced Memory & Performance
797    /// Set kernel memory limit
798    #[must_use]
799    pub fn kernel_memory(mut self, memory: impl Into<String>) -> Self {
800        self.kernel_memory = Some(memory.into());
801        self
802    }
803
804    /// Tune container memory swappiness (0 to 100)
805    #[must_use]
806    pub fn memory_swappiness(mut self, swappiness: i32) -> Self {
807        self.memory_swappiness = Some(swappiness);
808        self
809    }
810
811    /// Tune host's OOM preferences (-1000 to 1000)
812    #[must_use]
813    pub fn oom_score_adj(mut self, score: i32) -> Self {
814        self.oom_score_adj = Some(score);
815        self
816    }
817
818    /// Tune container pids limit (set -1 for unlimited)
819    #[must_use]
820    pub fn pids_limit(mut self, limit: i64) -> Self {
821        self.pids_limit = Some(limit);
822        self
823    }
824
825    /// Set size of /dev/shm (e.g., "64m")
826    #[must_use]
827    pub fn shm_size(mut self, size: impl Into<String>) -> Self {
828        self.shm_size = Some(size.into());
829        self
830    }
831
832    // Process Control
833    /// Set signal to stop the container (e.g., "SIGTERM", "SIGKILL")
834    #[must_use]
835    pub fn stop_signal(mut self, signal: impl Into<String>) -> Self {
836        self.stop_signal = Some(signal.into());
837        self
838    }
839
840    /// Set timeout (in seconds) to stop a container
841    #[must_use]
842    pub fn stop_timeout(mut self, timeout: i32) -> Self {
843        self.stop_timeout = Some(timeout);
844        self
845    }
846
847    /// Override the key sequence for detaching a container
848    #[must_use]
849    pub fn detach_keys(mut self, keys: impl Into<String>) -> Self {
850        self.detach_keys = Some(keys.into());
851        self
852    }
853
854    // Simple Flags
855    /// Disable proxying received signals to the process
856    #[must_use]
857    pub fn no_sig_proxy(mut self) -> Self {
858        self.sig_proxy = false;
859        self
860    }
861
862    /// Mount the container's root filesystem as read only
863    #[must_use]
864    pub fn read_only(mut self) -> Self {
865        self.read_only = true;
866        self
867    }
868
869    /// Run an init inside the container that forwards signals and reaps processes
870    #[must_use]
871    pub fn init(mut self) -> Self {
872        self.init = true;
873        self
874    }
875
876    /// Disable OOM Killer
877    #[must_use]
878    pub fn oom_kill_disable(mut self) -> Self {
879        self.oom_kill_disable = true;
880        self
881    }
882
883    /// Disable any container-specified HEALTHCHECK
884    #[must_use]
885    pub fn no_healthcheck(mut self) -> Self {
886        self.no_healthcheck = true;
887        self
888    }
889
890    /// Enable image verification (disable content trust is false)
891    #[must_use]
892    pub fn enable_content_trust(mut self) -> Self {
893        self.disable_content_trust = false;
894        self
895    }
896
897    /// Publish all exposed ports to random ports
898    #[must_use]
899    pub fn publish_all(mut self) -> Self {
900        self.publish_all = true;
901        self
902    }
903
904    /// Suppress the pull output
905    #[must_use]
906    pub fn quiet(mut self) -> Self {
907        self.quiet = true;
908        self
909    }
910
911    // High-Impact List Options
912
913    // DNS & Network
914    /// Add custom DNS server
915    #[must_use]
916    pub fn dns(mut self, dns: impl Into<String>) -> Self {
917        self.dns.push(dns.into());
918        self
919    }
920
921    /// Add multiple DNS servers
922    #[must_use]
923    pub fn dns_servers(mut self, servers: Vec<String>) -> Self {
924        self.dns.extend(servers);
925        self
926    }
927
928    /// Add DNS option
929    #[must_use]
930    pub fn dns_option(mut self, option: impl Into<String>) -> Self {
931        self.dns_option.push(option.into());
932        self
933    }
934
935    /// Add DNS search domain
936    #[must_use]
937    pub fn dns_search(mut self, domain: impl Into<String>) -> Self {
938        self.dns_search.push(domain.into());
939        self
940    }
941
942    /// Add host-to-IP mapping (format: "hostname:ip")
943    #[must_use]
944    pub fn add_host(mut self, mapping: impl Into<String>) -> Self {
945        self.add_host.push(mapping.into());
946        self
947    }
948
949    // Security & Capabilities
950    /// Add Linux capability
951    #[must_use]
952    pub fn cap_add(mut self, capability: impl Into<String>) -> Self {
953        self.cap_add.push(capability.into());
954        self
955    }
956
957    /// Drop Linux capability
958    #[must_use]
959    pub fn cap_drop(mut self, capability: impl Into<String>) -> Self {
960        self.cap_drop.push(capability.into());
961        self
962    }
963
964    /// Add security option
965    #[must_use]
966    pub fn security_opt(mut self, option: impl Into<String>) -> Self {
967        self.security_opt.push(option.into());
968        self
969    }
970
971    // Device & Filesystem
972    /// Add host device to container
973    #[must_use]
974    pub fn device(mut self, device: impl Into<String>) -> Self {
975        self.device.push(device.into());
976        self
977    }
978
979    /// Mount tmpfs directory
980    #[must_use]
981    pub fn tmpfs(mut self, path: impl Into<String>) -> Self {
982        self.tmpfs.push(path.into());
983        self
984    }
985
986    /// Expose port without publishing it
987    #[must_use]
988    pub fn expose(mut self, port: impl Into<String>) -> Self {
989        self.expose.push(port.into());
990        self
991    }
992
993    // Environment & Labels
994    /// Read environment variables from file
995    #[must_use]
996    pub fn env_file(mut self, file: impl Into<PathBuf>) -> Self {
997        self.env_file.push(file.into());
998        self
999    }
1000
1001    /// Add metadata label
1002    #[must_use]
1003    pub fn label(mut self, label: impl Into<String>) -> Self {
1004        self.label.push(label.into());
1005        self
1006    }
1007
1008    /// Read labels from file
1009    #[must_use]
1010    pub fn label_file(mut self, file: impl Into<PathBuf>) -> Self {
1011        self.label_file.push(file.into());
1012        self
1013    }
1014
1015    // Additional List/Vec Options
1016
1017    /// Add network alias for the container
1018    #[must_use]
1019    pub fn network_alias(mut self, alias: impl Into<String>) -> Self {
1020        self.network_alias.push(alias.into());
1021        self
1022    }
1023
1024    /// Add supplementary group for the user
1025    #[must_use]
1026    pub fn group_add(mut self, group: impl Into<String>) -> Self {
1027        self.group_add.push(group.into());
1028        self
1029    }
1030
1031    /// Attach to STDIN, STDOUT or STDERR
1032    #[must_use]
1033    pub fn attach(mut self, stream: impl Into<String>) -> Self {
1034        self.attach.push(stream.into());
1035        self
1036    }
1037
1038    /// Add log driver option
1039    #[must_use]
1040    pub fn log_opt(mut self, option: impl Into<String>) -> Self {
1041        self.log_opt.push(option.into());
1042        self
1043    }
1044
1045    /// Add storage driver option
1046    #[must_use]
1047    pub fn storage_opt(mut self, option: impl Into<String>) -> Self {
1048        self.storage_opt.push(option.into());
1049        self
1050    }
1051
1052    /// Set ulimit option
1053    #[must_use]
1054    pub fn ulimit(mut self, limit: impl Into<String>) -> Self {
1055        self.ulimit.push(limit.into());
1056        self
1057    }
1058
1059    /// Mount volumes from another container
1060    #[must_use]
1061    pub fn volumes_from(mut self, container: impl Into<String>) -> Self {
1062        self.volumes_from.push(container.into());
1063        self
1064    }
1065
1066    /// Add link to another container (deprecated)
1067    #[must_use]
1068    pub fn link(mut self, link: impl Into<String>) -> Self {
1069        self.link.push(link.into());
1070        self
1071    }
1072
1073    /// Add container IPv4/IPv6 link-local address
1074    #[must_use]
1075    pub fn link_local_ip(mut self, ip: impl Into<String>) -> Self {
1076        self.link_local_ip.push(ip.into());
1077        self
1078    }
1079
1080    // Health Check Options
1081
1082    // Health check methods
1083    /// Set health check command
1084    #[must_use]
1085    pub fn health_cmd(mut self, cmd: impl Into<String>) -> Self {
1086        self.health_cmd = Some(cmd.into());
1087        self
1088    }
1089
1090    /// Set health check interval
1091    #[must_use]
1092    pub fn health_interval(mut self, interval: impl Into<String>) -> Self {
1093        self.health_interval = Some(interval.into());
1094        self
1095    }
1096
1097    /// Set health check retries
1098    #[must_use]
1099    pub fn health_retries(mut self, retries: i32) -> Self {
1100        self.health_retries = Some(retries);
1101        self
1102    }
1103
1104    /// Set health check timeout
1105    #[must_use]
1106    pub fn health_timeout(mut self, timeout: impl Into<String>) -> Self {
1107        self.health_timeout = Some(timeout.into());
1108        self
1109    }
1110
1111    /// Set health check start period
1112    #[must_use]
1113    pub fn health_start_period(mut self, period: impl Into<String>) -> Self {
1114        self.health_start_period = Some(period.into());
1115        self
1116    }
1117
1118    /// Set health check start interval
1119    #[must_use]
1120    pub fn health_start_interval(mut self, interval: impl Into<String>) -> Self {
1121        self.health_start_interval = Some(interval.into());
1122        self
1123    }
1124
1125    // Advanced options
1126    /// Add advanced mount configuration
1127    #[must_use]
1128    pub fn mount(mut self, mount: impl Into<String>) -> Self {
1129        self.mount.push(mount.into());
1130        self
1131    }
1132
1133    /// Connect to a network
1134    #[must_use]
1135    pub fn network(mut self, network: impl Into<String>) -> Self {
1136        self.network.push(network.into());
1137        self
1138    }
1139
1140    /// Set GPU devices to add to the container
1141    #[must_use]
1142    pub fn gpus(mut self, gpus: impl Into<String>) -> Self {
1143        self.gpus = Some(gpus.into());
1144        self
1145    }
1146
1147    /// Add custom annotation (key=value format)
1148    #[must_use]
1149    pub fn annotation(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1150        self.annotation
1151            .push(format!("{}={}", key.into(), value.into()));
1152        self
1153    }
1154
1155    /// Set kernel parameter (key=value format)
1156    #[must_use]
1157    pub fn sysctl(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1158        self.sysctl.push(format!("{}={}", key.into(), value.into()));
1159        self
1160    }
1161
1162    // Advanced System Options
1163
1164    // Block I/O controls
1165    /// Set block IO weight (10-1000)
1166    #[must_use]
1167    pub fn blkio_weight(mut self, weight: u16) -> Self {
1168        self.blkio_weight = Some(weight);
1169        self
1170    }
1171
1172    /// Set block IO weight for a specific device (format: DEVICE:WEIGHT)
1173    #[must_use]
1174    pub fn blkio_weight_device(mut self, device_weight: impl Into<String>) -> Self {
1175        self.blkio_weight_device.push(device_weight.into());
1176        self
1177    }
1178
1179    /// Limit read rate (bytes per second) from a device (format: DEVICE:RATE)
1180    #[must_use]
1181    pub fn device_read_bps(mut self, device_rate: impl Into<String>) -> Self {
1182        self.device_read_bps.push(device_rate.into());
1183        self
1184    }
1185
1186    /// Limit write rate (bytes per second) to a device (format: DEVICE:RATE)
1187    #[must_use]
1188    pub fn device_write_bps(mut self, device_rate: impl Into<String>) -> Self {
1189        self.device_write_bps.push(device_rate.into());
1190        self
1191    }
1192
1193    /// Limit read rate (IO per second) from a device (format: DEVICE:RATE)
1194    #[must_use]
1195    pub fn device_read_iops(mut self, device_rate: impl Into<String>) -> Self {
1196        self.device_read_iops.push(device_rate.into());
1197        self
1198    }
1199
1200    /// Limit write rate (IO per second) to a device (format: DEVICE:RATE)
1201    #[must_use]
1202    pub fn device_write_iops(mut self, device_rate: impl Into<String>) -> Self {
1203        self.device_write_iops.push(device_rate.into());
1204        self
1205    }
1206
1207    // Real-time CPU scheduling
1208    /// Limit CPU real-time period in microseconds
1209    #[must_use]
1210    pub fn cpu_rt_period(mut self, period: i64) -> Self {
1211        self.cpu_rt_period = Some(period);
1212        self
1213    }
1214
1215    /// Limit CPU real-time runtime in microseconds
1216    #[must_use]
1217    pub fn cpu_rt_runtime(mut self, runtime: i64) -> Self {
1218        self.cpu_rt_runtime = Some(runtime);
1219        self
1220    }
1221
1222    // Advanced networking
1223    /// Set container IPv4 address (e.g., "172.30.100.104")
1224    #[must_use]
1225    pub fn ip(mut self, ip: impl Into<String>) -> Self {
1226        self.ip = Some(ip.into());
1227        self
1228    }
1229
1230    /// Set container IPv6 address (e.g., "`2001:db8::33`")
1231    #[must_use]
1232    pub fn ip6(mut self, ip6: impl Into<String>) -> Self {
1233        self.ip6 = Some(ip6.into());
1234        self
1235    }
1236
1237    /// Add device cgroup rule
1238    #[must_use]
1239    pub fn device_cgroup_rule(mut self, rule: impl Into<String>) -> Self {
1240        self.device_cgroup_rule.push(rule.into());
1241        self
1242    }
1243}
1244
1245#[async_trait]
1246impl DockerCommand for RunCommand {
1247    type Output = ContainerId;
1248
1249    fn get_executor(&self) -> &CommandExecutor {
1250        &self.executor
1251    }
1252
1253    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
1254        &mut self.executor
1255    }
1256
1257    #[allow(clippy::too_many_lines)]
1258    fn build_command_args(&self) -> Vec<String> {
1259        let mut args = vec!["run".to_string()];
1260
1261        // Add flags
1262        if self.detach {
1263            args.push("--detach".to_string());
1264        }
1265        if self.interactive {
1266            args.push("--interactive".to_string());
1267        }
1268        if self.tty {
1269            args.push("--tty".to_string());
1270        }
1271        if self.remove {
1272            args.push("--rm".to_string());
1273        }
1274
1275        // Add container name
1276        if let Some(ref name) = self.name {
1277            args.push("--name".to_string());
1278            args.push(name.clone());
1279        }
1280
1281        // Add working directory
1282        if let Some(ref workdir) = self.workdir {
1283            args.push("--workdir".to_string());
1284            args.push(workdir.to_string_lossy().to_string());
1285        }
1286
1287        // Add entrypoint
1288        if let Some(ref entrypoint) = self.entrypoint {
1289            args.push("--entrypoint".to_string());
1290            args.push(entrypoint.clone());
1291        }
1292
1293        // Add environment variables
1294        args.extend(self.environment.build_args());
1295
1296        // Add port mappings
1297        args.extend(self.ports.build_args());
1298
1299        // Add volume mounts
1300        for volume in &self.volumes {
1301            args.push("--volume".to_string());
1302            args.push(volume.to_string());
1303        }
1304
1305        // Resource Limits
1306        if let Some(ref memory) = self.memory {
1307            args.push("--memory".to_string());
1308            args.push(memory.clone());
1309        }
1310        if let Some(ref cpus) = self.cpus {
1311            args.push("--cpus".to_string());
1312            args.push(cpus.clone());
1313        }
1314        if let Some(cpu_shares) = self.cpu_shares {
1315            args.push("--cpu-shares".to_string());
1316            args.push(cpu_shares.to_string());
1317        }
1318        if let Some(cpu_period) = self.cpu_period {
1319            args.push("--cpu-period".to_string());
1320            args.push(cpu_period.to_string());
1321        }
1322        if let Some(cpu_quota) = self.cpu_quota {
1323            args.push("--cpu-quota".to_string());
1324            args.push(cpu_quota.to_string());
1325        }
1326        if let Some(ref cpuset_cpus) = self.cpuset_cpus {
1327            args.push("--cpuset-cpus".to_string());
1328            args.push(cpuset_cpus.clone());
1329        }
1330        if let Some(ref cpuset_mems) = self.cpuset_mems {
1331            args.push("--cpuset-mems".to_string());
1332            args.push(cpuset_mems.clone());
1333        }
1334        if let Some(ref memory_swap) = self.memory_swap {
1335            args.push("--memory-swap".to_string());
1336            args.push(memory_swap.clone());
1337        }
1338        if let Some(ref memory_reservation) = self.memory_reservation {
1339            args.push("--memory-reservation".to_string());
1340            args.push(memory_reservation.clone());
1341        }
1342
1343        // Security & User Context
1344        if let Some(ref user) = self.user {
1345            args.push("--user".to_string());
1346            args.push(user.clone());
1347        }
1348        if self.privileged {
1349            args.push("--privileged".to_string());
1350        }
1351        if let Some(ref hostname) = self.hostname {
1352            args.push("--hostname".to_string());
1353            args.push(hostname.clone());
1354        }
1355
1356        // Lifecycle Management
1357        if let Some(ref restart) = self.restart {
1358            args.push("--restart".to_string());
1359            args.push(restart.clone());
1360        }
1361
1362        // System Integration
1363        if let Some(ref platform) = self.platform {
1364            args.push("--platform".to_string());
1365            args.push(platform.clone());
1366        }
1367        if let Some(ref runtime) = self.runtime {
1368            args.push("--runtime".to_string());
1369            args.push(runtime.clone());
1370        }
1371        if let Some(ref isolation) = self.isolation {
1372            args.push("--isolation".to_string());
1373            args.push(isolation.clone());
1374        }
1375        if let Some(ref pull) = self.pull {
1376            args.push("--pull".to_string());
1377            args.push(pull.clone());
1378        }
1379        if let Some(ref cidfile) = self.cidfile {
1380            args.push("--cidfile".to_string());
1381            args.push(cidfile.clone());
1382        }
1383        if let Some(ref domainname) = self.domainname {
1384            args.push("--domainname".to_string());
1385            args.push(domainname.clone());
1386        }
1387        if let Some(ref mac_address) = self.mac_address {
1388            args.push("--mac-address".to_string());
1389            args.push(mac_address.clone());
1390        }
1391
1392        // Logging & Drivers
1393        if let Some(ref log_driver) = self.log_driver {
1394            args.push("--log-driver".to_string());
1395            args.push(log_driver.clone());
1396        }
1397        if let Some(ref volume_driver) = self.volume_driver {
1398            args.push("--volume-driver".to_string());
1399            args.push(volume_driver.clone());
1400        }
1401
1402        // Namespaces
1403        if let Some(ref userns) = self.userns {
1404            args.push("--userns".to_string());
1405            args.push(userns.clone());
1406        }
1407        if let Some(ref uts) = self.uts {
1408            args.push("--uts".to_string());
1409            args.push(uts.clone());
1410        }
1411        if let Some(ref pid) = self.pid {
1412            args.push("--pid".to_string());
1413            args.push(pid.clone());
1414        }
1415        if let Some(ref ipc) = self.ipc {
1416            args.push("--ipc".to_string());
1417            args.push(ipc.clone());
1418        }
1419        if let Some(ref cgroupns) = self.cgroupns {
1420            args.push("--cgroupns".to_string());
1421            args.push(cgroupns.clone());
1422        }
1423        if let Some(ref cgroup_parent) = self.cgroup_parent {
1424            args.push("--cgroup-parent".to_string());
1425            args.push(cgroup_parent.clone());
1426        }
1427
1428        // Advanced Memory & Performance
1429        if let Some(ref kernel_memory) = self.kernel_memory {
1430            args.push("--kernel-memory".to_string());
1431            args.push(kernel_memory.clone());
1432        }
1433        if let Some(memory_swappiness) = self.memory_swappiness {
1434            args.push("--memory-swappiness".to_string());
1435            args.push(memory_swappiness.to_string());
1436        }
1437        if let Some(oom_score_adj) = self.oom_score_adj {
1438            args.push("--oom-score-adj".to_string());
1439            args.push(oom_score_adj.to_string());
1440        }
1441        if let Some(pids_limit) = self.pids_limit {
1442            args.push("--pids-limit".to_string());
1443            args.push(pids_limit.to_string());
1444        }
1445        if let Some(ref shm_size) = self.shm_size {
1446            args.push("--shm-size".to_string());
1447            args.push(shm_size.clone());
1448        }
1449
1450        // Process Control
1451        if let Some(ref stop_signal) = self.stop_signal {
1452            args.push("--stop-signal".to_string());
1453            args.push(stop_signal.clone());
1454        }
1455        if let Some(stop_timeout) = self.stop_timeout {
1456            args.push("--stop-timeout".to_string());
1457            args.push(stop_timeout.to_string());
1458        }
1459        if let Some(ref detach_keys) = self.detach_keys {
1460            args.push("--detach-keys".to_string());
1461            args.push(detach_keys.clone());
1462        }
1463
1464        // Simple Flags
1465        if !self.sig_proxy {
1466            args.push("--sig-proxy=false".to_string());
1467        }
1468        if self.read_only {
1469            args.push("--read-only".to_string());
1470        }
1471        if self.init {
1472            args.push("--init".to_string());
1473        }
1474        if self.oom_kill_disable {
1475            args.push("--oom-kill-disable".to_string());
1476        }
1477        if self.no_healthcheck {
1478            args.push("--no-healthcheck".to_string());
1479        }
1480        if !self.disable_content_trust {
1481            args.push("--disable-content-trust=false".to_string());
1482        }
1483        if self.publish_all {
1484            args.push("--publish-all".to_string());
1485        }
1486        if self.quiet {
1487            args.push("--quiet".to_string());
1488        }
1489
1490        // High-Impact List Options
1491        // DNS & Network
1492        for dns in &self.dns {
1493            args.push("--dns".to_string());
1494            args.push(dns.clone());
1495        }
1496        for dns_option in &self.dns_option {
1497            args.push("--dns-option".to_string());
1498            args.push(dns_option.clone());
1499        }
1500        for dns_search in &self.dns_search {
1501            args.push("--dns-search".to_string());
1502            args.push(dns_search.clone());
1503        }
1504        for add_host in &self.add_host {
1505            args.push("--add-host".to_string());
1506            args.push(add_host.clone());
1507        }
1508
1509        // Security & Capabilities
1510        for cap_add in &self.cap_add {
1511            args.push("--cap-add".to_string());
1512            args.push(cap_add.clone());
1513        }
1514        for cap_drop in &self.cap_drop {
1515            args.push("--cap-drop".to_string());
1516            args.push(cap_drop.clone());
1517        }
1518        for security_opt in &self.security_opt {
1519            args.push("--security-opt".to_string());
1520            args.push(security_opt.clone());
1521        }
1522
1523        // Device & Filesystem
1524        for device in &self.device {
1525            args.push("--device".to_string());
1526            args.push(device.clone());
1527        }
1528        for tmpfs in &self.tmpfs {
1529            args.push("--tmpfs".to_string());
1530            args.push(tmpfs.clone());
1531        }
1532        for expose in &self.expose {
1533            args.push("--expose".to_string());
1534            args.push(expose.clone());
1535        }
1536
1537        // Environment & Labels
1538        for env_file in &self.env_file {
1539            args.push("--env-file".to_string());
1540            args.push(env_file.to_string_lossy().to_string());
1541        }
1542        for label in &self.label {
1543            args.push("--label".to_string());
1544            args.push(label.clone());
1545        }
1546        for label_file in &self.label_file {
1547            args.push("--label-file".to_string());
1548            args.push(label_file.to_string_lossy().to_string());
1549        }
1550
1551        // Additional List/Vec Options
1552        for network_alias in &self.network_alias {
1553            args.push("--network-alias".to_string());
1554            args.push(network_alias.clone());
1555        }
1556        for group_add in &self.group_add {
1557            args.push("--group-add".to_string());
1558            args.push(group_add.clone());
1559        }
1560        for attach in &self.attach {
1561            args.push("--attach".to_string());
1562            args.push(attach.clone());
1563        }
1564        for log_opt in &self.log_opt {
1565            args.push("--log-opt".to_string());
1566            args.push(log_opt.clone());
1567        }
1568        for storage_opt in &self.storage_opt {
1569            args.push("--storage-opt".to_string());
1570            args.push(storage_opt.clone());
1571        }
1572        for ulimit in &self.ulimit {
1573            args.push("--ulimit".to_string());
1574            args.push(ulimit.clone());
1575        }
1576        for volumes_from in &self.volumes_from {
1577            args.push("--volumes-from".to_string());
1578            args.push(volumes_from.clone());
1579        }
1580        for link in &self.link {
1581            args.push("--link".to_string());
1582            args.push(link.clone());
1583        }
1584        for link_local_ip in &self.link_local_ip {
1585            args.push("--link-local-ip".to_string());
1586            args.push(link_local_ip.clone());
1587        }
1588
1589        // Health Check Options
1590        // Health checks
1591        if let Some(ref health_cmd) = self.health_cmd {
1592            args.push("--health-cmd".to_string());
1593            args.push(health_cmd.clone());
1594        }
1595        if let Some(ref health_interval) = self.health_interval {
1596            args.push("--health-interval".to_string());
1597            args.push(health_interval.clone());
1598        }
1599        if let Some(health_retries) = self.health_retries {
1600            args.push("--health-retries".to_string());
1601            args.push(health_retries.to_string());
1602        }
1603        if let Some(ref health_timeout) = self.health_timeout {
1604            args.push("--health-timeout".to_string());
1605            args.push(health_timeout.clone());
1606        }
1607        if let Some(ref health_start_period) = self.health_start_period {
1608            args.push("--health-start-period".to_string());
1609            args.push(health_start_period.clone());
1610        }
1611        if let Some(ref health_start_interval) = self.health_start_interval {
1612            args.push("--health-start-interval".to_string());
1613            args.push(health_start_interval.clone());
1614        }
1615
1616        // Advanced options
1617        for mount in &self.mount {
1618            args.push("--mount".to_string());
1619            args.push(mount.clone());
1620        }
1621        for network in &self.network {
1622            args.push("--network".to_string());
1623            args.push(network.clone());
1624        }
1625        if let Some(ref gpus) = self.gpus {
1626            args.push("--gpus".to_string());
1627            args.push(gpus.clone());
1628        }
1629
1630        // Map-based options
1631        for annotation in &self.annotation {
1632            args.push("--annotation".to_string());
1633            args.push(annotation.clone());
1634        }
1635        for sysctl in &self.sysctl {
1636            args.push("--sysctl".to_string());
1637            args.push(sysctl.clone());
1638        }
1639
1640        // Advanced System Options
1641        // Block I/O controls
1642        if let Some(blkio_weight) = self.blkio_weight {
1643            args.push("--blkio-weight".to_string());
1644            args.push(blkio_weight.to_string());
1645        }
1646        for blkio_weight_device in &self.blkio_weight_device {
1647            args.push("--blkio-weight-device".to_string());
1648            args.push(blkio_weight_device.clone());
1649        }
1650        for device_read_bps in &self.device_read_bps {
1651            args.push("--device-read-bps".to_string());
1652            args.push(device_read_bps.clone());
1653        }
1654        for device_write_bps in &self.device_write_bps {
1655            args.push("--device-write-bps".to_string());
1656            args.push(device_write_bps.clone());
1657        }
1658        for device_read_iops in &self.device_read_iops {
1659            args.push("--device-read-iops".to_string());
1660            args.push(device_read_iops.clone());
1661        }
1662        for device_write_iops in &self.device_write_iops {
1663            args.push("--device-write-iops".to_string());
1664            args.push(device_write_iops.clone());
1665        }
1666
1667        // Real-time CPU scheduling
1668        if let Some(cpu_rt_period) = self.cpu_rt_period {
1669            args.push("--cpu-rt-period".to_string());
1670            args.push(cpu_rt_period.to_string());
1671        }
1672        if let Some(cpu_rt_runtime) = self.cpu_rt_runtime {
1673            args.push("--cpu-rt-runtime".to_string());
1674            args.push(cpu_rt_runtime.to_string());
1675        }
1676
1677        // Advanced networking
1678        if let Some(ref ip) = self.ip {
1679            args.push("--ip".to_string());
1680            args.push(ip.clone());
1681        }
1682        if let Some(ref ip6) = self.ip6 {
1683            args.push("--ip6".to_string());
1684            args.push(ip6.clone());
1685        }
1686
1687        // Advanced system options
1688        for device_cgroup_rule in &self.device_cgroup_rule {
1689            args.push("--device-cgroup-rule".to_string());
1690            args.push(device_cgroup_rule.clone());
1691        }
1692
1693        // Add image
1694        args.push(self.image.clone());
1695
1696        // Add command if specified
1697        if let Some(ref command) = self.command {
1698            args.extend(command.clone());
1699        }
1700
1701        // Add raw arguments from executor
1702        args.extend(self.executor.raw_args.clone());
1703
1704        args
1705    }
1706
1707    async fn execute(&self) -> Result<Self::Output> {
1708        let args = self.build_command_args();
1709        let output = self.execute_command(args).await?;
1710
1711        // Parse container ID from output
1712        let container_id = output.stdout.trim().to_string();
1713        if container_id.is_empty() {
1714            return Err(Error::parse_error(
1715                "No container ID returned from docker run",
1716            ));
1717        }
1718
1719        Ok(ContainerId(container_id))
1720    }
1721}
1722
1723// Streaming support for RunCommand
1724#[async_trait]
1725impl StreamableCommand for RunCommand {
1726    async fn stream<F>(&self, handler: F) -> Result<StreamResult>
1727    where
1728        F: FnMut(OutputLine) + Send + 'static,
1729    {
1730        // Don't stream if running detached
1731        if self.detach {
1732            return Err(Error::custom(
1733                "Cannot stream output for detached containers",
1734            ));
1735        }
1736
1737        let mut cmd = TokioCommand::new("docker");
1738        cmd.arg("run");
1739
1740        for arg in self.build_command_args() {
1741            cmd.arg(arg);
1742        }
1743
1744        crate::stream::stream_command(cmd, handler).await
1745    }
1746
1747    async fn stream_channel(&self) -> Result<(mpsc::Receiver<OutputLine>, StreamResult)> {
1748        // Don't stream if running detached
1749        if self.detach {
1750            return Err(Error::custom(
1751                "Cannot stream output for detached containers",
1752            ));
1753        }
1754
1755        let mut cmd = TokioCommand::new("docker");
1756        cmd.arg("run");
1757
1758        for arg in self.build_command_args() {
1759            cmd.arg(arg);
1760        }
1761
1762        crate::stream::stream_command_channel(cmd).await
1763    }
1764}
1765
1766impl RunCommand {
1767    /// Run the container with streaming output
1768    ///
1769    /// Note: This will fail if the container is run in detached mode.
1770    ///
1771    /// # Examples
1772    ///
1773    /// ```no_run
1774    /// use docker_wrapper::RunCommand;
1775    /// use docker_wrapper::StreamHandler;
1776    ///
1777    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1778    /// let result = RunCommand::new("alpine")
1779    ///     .cmd(vec!["echo".to_string(), "Hello, World!".to_string()])
1780    ///     .stream(StreamHandler::print())
1781    ///     .await?;
1782    /// # Ok(())
1783    /// # }
1784    /// ```
1785    ///
1786    /// # Errors
1787    ///
1788    /// Returns an error if the container is detached or encounters an I/O error
1789    pub async fn stream<F>(&self, handler: F) -> Result<StreamResult>
1790    where
1791        F: FnMut(OutputLine) + Send + 'static,
1792    {
1793        <Self as StreamableCommand>::stream(self, handler).await
1794    }
1795}
1796
1797#[cfg(test)]
1798mod tests {
1799    use super::*;
1800
1801    #[test]
1802    fn test_run_command_builder() {
1803        let cmd = RunCommand::new("nginx:latest")
1804            .name("test-nginx")
1805            .detach()
1806            .env("ENV_VAR", "value")
1807            .port(8080, 80)
1808            .volume("data", "/var/data")
1809            .workdir("/app")
1810            .remove();
1811
1812        let args = cmd.build_command_args();
1813
1814        assert!(args.contains(&"--detach".to_string()));
1815        assert!(args.contains(&"--name".to_string()));
1816        assert!(args.contains(&"test-nginx".to_string()));
1817        assert!(args.contains(&"--env".to_string()));
1818        assert!(args.contains(&"ENV_VAR=value".to_string()));
1819        assert!(args.contains(&"--publish".to_string()));
1820        assert!(args.contains(&"8080:80".to_string()));
1821        assert!(args.contains(&"--volume".to_string()));
1822        assert!(args.contains(&"data:/var/data".to_string()));
1823        assert!(args.contains(&"--workdir".to_string()));
1824        assert!(args.contains(&"/app".to_string()));
1825        assert!(args.contains(&"--rm".to_string()));
1826        assert!(args.contains(&"nginx:latest".to_string()));
1827    }
1828
1829    #[test]
1830    fn test_run_command_with_cmd() {
1831        let cmd =
1832            RunCommand::new("alpine:latest").cmd(vec!["echo".to_string(), "hello".to_string()]);
1833
1834        let args = cmd.build_command_args();
1835        assert!(args.contains(&"alpine:latest".to_string()));
1836        assert!(args.contains(&"echo".to_string()));
1837        assert!(args.contains(&"hello".to_string()));
1838    }
1839
1840    #[test]
1841    fn test_run_command_extensibility() {
1842        let mut cmd = RunCommand::new("test:latest");
1843        cmd.flag("privileged")
1844            .option("memory", "1g")
1845            .arg("--custom-option");
1846
1847        // The extensibility is tested via the trait methods
1848        // Full integration testing would require actual Docker execution
1849    }
1850
1851    #[test]
1852    fn test_volume_mount_display() {
1853        let volume = VolumeMount {
1854            source: "data".to_string(),
1855            target: "/var/data".to_string(),
1856            mount_type: MountType::Volume,
1857            readonly: false,
1858        };
1859        assert_eq!(volume.to_string(), "data:/var/data");
1860
1861        let readonly_volume = VolumeMount {
1862            source: "/host/path".to_string(),
1863            target: "/container/path".to_string(),
1864            mount_type: MountType::Bind,
1865            readonly: true,
1866        };
1867        assert_eq!(readonly_volume.to_string(), "/host/path:/container/path:ro");
1868    }
1869
1870    #[test]
1871    fn test_run_command_resource_limits() {
1872        let cmd = RunCommand::new("alpine:latest")
1873            .memory("1g")
1874            .cpus("2.0")
1875            .cpu_shares(1024)
1876            .cpu_period(100_000)
1877            .cpu_quota(50_000)
1878            .cpuset_cpus("0-3")
1879            .cpuset_mems("0,1")
1880            .memory_swap("2g")
1881            .memory_reservation("500m");
1882
1883        let args = cmd.build_command_args();
1884
1885        assert!(args.contains(&"--memory".to_string()));
1886        assert!(args.contains(&"1g".to_string()));
1887        assert!(args.contains(&"--cpus".to_string()));
1888        assert!(args.contains(&"2.0".to_string()));
1889        assert!(args.contains(&"--cpu-shares".to_string()));
1890        assert!(args.contains(&"1024".to_string()));
1891        assert!(args.contains(&"--cpu-period".to_string()));
1892        assert!(args.contains(&"100000".to_string()));
1893        assert!(args.contains(&"--cpu-quota".to_string()));
1894        assert!(args.contains(&"50000".to_string()));
1895        assert!(args.contains(&"--cpuset-cpus".to_string()));
1896        assert!(args.contains(&"0-3".to_string()));
1897        assert!(args.contains(&"--cpuset-mems".to_string()));
1898        assert!(args.contains(&"0,1".to_string()));
1899        assert!(args.contains(&"--memory-swap".to_string()));
1900        assert!(args.contains(&"2g".to_string()));
1901        assert!(args.contains(&"--memory-reservation".to_string()));
1902        assert!(args.contains(&"500m".to_string()));
1903    }
1904
1905    #[test]
1906    fn test_run_command_security_and_user() {
1907        let cmd = RunCommand::new("alpine:latest")
1908            .user("1000:1000")
1909            .privileged()
1910            .hostname("test-host");
1911
1912        let args = cmd.build_command_args();
1913
1914        assert!(args.contains(&"--user".to_string()));
1915        assert!(args.contains(&"1000:1000".to_string()));
1916        assert!(args.contains(&"--privileged".to_string()));
1917        assert!(args.contains(&"--hostname".to_string()));
1918        assert!(args.contains(&"test-host".to_string()));
1919    }
1920
1921    #[test]
1922    fn test_run_command_lifecycle_management() {
1923        let cmd = RunCommand::new("alpine:latest").restart("always");
1924
1925        let args = cmd.build_command_args();
1926
1927        assert!(args.contains(&"--restart".to_string()));
1928        assert!(args.contains(&"always".to_string()));
1929    }
1930
1931    #[test]
1932    fn test_run_command_system_integration() {
1933        let cmd = RunCommand::new("alpine:latest")
1934            .platform("linux/amd64")
1935            .runtime("runc")
1936            .isolation("default")
1937            .pull("always")
1938            .cidfile("/tmp/container.cid")
1939            .domainname("example.com")
1940            .mac_address("92:d0:c6:0a:29:33");
1941
1942        let args = cmd.build_command_args();
1943
1944        assert!(args.contains(&"--platform".to_string()));
1945        assert!(args.contains(&"linux/amd64".to_string()));
1946        assert!(args.contains(&"--runtime".to_string()));
1947        assert!(args.contains(&"runc".to_string()));
1948        assert!(args.contains(&"--isolation".to_string()));
1949        assert!(args.contains(&"default".to_string()));
1950        assert!(args.contains(&"--pull".to_string()));
1951        assert!(args.contains(&"always".to_string()));
1952        assert!(args.contains(&"--cidfile".to_string()));
1953        assert!(args.contains(&"/tmp/container.cid".to_string()));
1954        assert!(args.contains(&"--domainname".to_string()));
1955        assert!(args.contains(&"example.com".to_string()));
1956        assert!(args.contains(&"--mac-address".to_string()));
1957        assert!(args.contains(&"92:d0:c6:0a:29:33".to_string()));
1958    }
1959
1960    #[test]
1961    fn test_run_command_logging_and_drivers() {
1962        let cmd = RunCommand::new("alpine:latest")
1963            .log_driver("json-file")
1964            .volume_driver("local");
1965
1966        let args = cmd.build_command_args();
1967
1968        assert!(args.contains(&"--log-driver".to_string()));
1969        assert!(args.contains(&"json-file".to_string()));
1970        assert!(args.contains(&"--volume-driver".to_string()));
1971        assert!(args.contains(&"local".to_string()));
1972    }
1973
1974    #[test]
1975    fn test_run_command_namespaces() {
1976        let cmd = RunCommand::new("alpine:latest")
1977            .userns("host")
1978            .uts("host")
1979            .pid("host")
1980            .ipc("host")
1981            .cgroupns("private")
1982            .cgroup_parent("/docker");
1983
1984        let args = cmd.build_command_args();
1985
1986        assert!(args.contains(&"--userns".to_string()));
1987        assert!(args.contains(&"host".to_string()));
1988        assert!(args.contains(&"--uts".to_string()));
1989        assert!(args.contains(&"--pid".to_string()));
1990        assert!(args.contains(&"--ipc".to_string()));
1991        assert!(args.contains(&"--cgroupns".to_string()));
1992        assert!(args.contains(&"private".to_string()));
1993        assert!(args.contains(&"--cgroup-parent".to_string()));
1994        assert!(args.contains(&"/docker".to_string()));
1995    }
1996
1997    #[test]
1998    fn test_run_command_advanced_memory_performance() {
1999        let cmd = RunCommand::new("alpine:latest")
2000            .kernel_memory("100m")
2001            .memory_swappiness(60)
2002            .oom_score_adj(-500)
2003            .pids_limit(100)
2004            .shm_size("64m");
2005
2006        let args = cmd.build_command_args();
2007
2008        assert!(args.contains(&"--kernel-memory".to_string()));
2009        assert!(args.contains(&"100m".to_string()));
2010        assert!(args.contains(&"--memory-swappiness".to_string()));
2011        assert!(args.contains(&"60".to_string()));
2012        assert!(args.contains(&"--oom-score-adj".to_string()));
2013        assert!(args.contains(&"-500".to_string()));
2014        assert!(args.contains(&"--pids-limit".to_string()));
2015        assert!(args.contains(&"100".to_string()));
2016        assert!(args.contains(&"--shm-size".to_string()));
2017        assert!(args.contains(&"64m".to_string()));
2018    }
2019
2020    #[test]
2021    fn test_run_command_process_control() {
2022        let cmd = RunCommand::new("alpine:latest")
2023            .stop_signal("SIGTERM")
2024            .stop_timeout(10)
2025            .detach_keys("ctrl-p,ctrl-q");
2026
2027        let args = cmd.build_command_args();
2028
2029        assert!(args.contains(&"--stop-signal".to_string()));
2030        assert!(args.contains(&"SIGTERM".to_string()));
2031        assert!(args.contains(&"--stop-timeout".to_string()));
2032        assert!(args.contains(&"10".to_string()));
2033        assert!(args.contains(&"--detach-keys".to_string()));
2034        assert!(args.contains(&"ctrl-p,ctrl-q".to_string()));
2035    }
2036
2037    #[test]
2038    fn test_run_command_simple_flags() {
2039        let cmd = RunCommand::new("alpine:latest")
2040            .no_sig_proxy()
2041            .read_only()
2042            .init()
2043            .oom_kill_disable()
2044            .no_healthcheck()
2045            .enable_content_trust()
2046            .publish_all()
2047            .quiet();
2048
2049        let args = cmd.build_command_args();
2050
2051        assert!(args.contains(&"--sig-proxy=false".to_string()));
2052        assert!(args.contains(&"--read-only".to_string()));
2053        assert!(args.contains(&"--init".to_string()));
2054        assert!(args.contains(&"--oom-kill-disable".to_string()));
2055        assert!(args.contains(&"--no-healthcheck".to_string()));
2056        assert!(args.contains(&"--disable-content-trust=false".to_string()));
2057        assert!(args.contains(&"--publish-all".to_string()));
2058        assert!(args.contains(&"--quiet".to_string()));
2059    }
2060
2061    #[test]
2062    fn test_run_command_comprehensive_builder() {
2063        let cmd = RunCommand::new("nginx:latest")
2064            .name("production-nginx")
2065            .detach()
2066            .memory("2g")
2067            .cpus("4.0")
2068            .user("nginx:nginx")
2069            .privileged()
2070            .restart("unless-stopped")
2071            .hostname("web-server")
2072            .platform("linux/amd64")
2073            .env("NGINX_PORT", "8080")
2074            .port(80, 8080)
2075            .volume("nginx-data", "/var/lib/nginx")
2076            .workdir("/usr/share/nginx/html")
2077            .read_only()
2078            .init()
2079            .remove();
2080
2081        let args = cmd.build_command_args();
2082
2083        // Verify comprehensive options are all present
2084        assert!(args.contains(&"--name".to_string()));
2085        assert!(args.contains(&"production-nginx".to_string()));
2086        assert!(args.contains(&"--detach".to_string()));
2087        assert!(args.contains(&"--memory".to_string()));
2088        assert!(args.contains(&"2g".to_string()));
2089        assert!(args.contains(&"--cpus".to_string()));
2090        assert!(args.contains(&"4.0".to_string()));
2091        assert!(args.contains(&"--user".to_string()));
2092        assert!(args.contains(&"nginx:nginx".to_string()));
2093        assert!(args.contains(&"--privileged".to_string()));
2094        assert!(args.contains(&"--restart".to_string()));
2095        assert!(args.contains(&"unless-stopped".to_string()));
2096        assert!(args.contains(&"--hostname".to_string()));
2097        assert!(args.contains(&"web-server".to_string()));
2098        assert!(args.contains(&"--platform".to_string()));
2099        assert!(args.contains(&"linux/amd64".to_string()));
2100        assert!(args.contains(&"--read-only".to_string()));
2101        assert!(args.contains(&"--init".to_string()));
2102        assert!(args.contains(&"--rm".to_string()));
2103        assert!(args.contains(&"nginx:latest".to_string()));
2104
2105        // Image should be at the end (before any command args)
2106        let image_pos = args.iter().position(|x| x == "nginx:latest").unwrap();
2107        assert!(image_pos > 10); // Should be after all the options
2108    }
2109
2110    #[test]
2111    fn test_run_command_default_flag_values() {
2112        let cmd = RunCommand::new("alpine:latest");
2113        let args = cmd.build_command_args();
2114
2115        // Default flags should not appear in args unless explicitly changed
2116        assert!(!args.contains(&"--sig-proxy=false".to_string()));
2117        assert!(!args.contains(&"--disable-content-trust=false".to_string()));
2118        assert!(!args.contains(&"--read-only".to_string()));
2119        assert!(!args.contains(&"--privileged".to_string()));
2120        assert!(!args.contains(&"--init".to_string()));
2121    }
2122
2123    #[test]
2124    fn test_container_id() {
2125        let id = ContainerId("abcdef123456789".to_string());
2126        assert_eq!(id.as_str(), "abcdef123456789");
2127        assert_eq!(id.short(), "abcdef123456");
2128        assert_eq!(id.to_string(), "abcdef123456789");
2129
2130        let short_id = ContainerId("abc".to_string());
2131        assert_eq!(short_id.short(), "abc");
2132    }
2133
2134    #[test]
2135    fn test_it_convenience_method() {
2136        let cmd = RunCommand::new("alpine:latest").it();
2137        let args = cmd.build_command_args();
2138        assert!(args.contains(&"--interactive".to_string()));
2139        assert!(args.contains(&"--tty".to_string()));
2140    }
2141
2142    #[test]
2143    fn test_run_command_dns_network_options() {
2144        let cmd = RunCommand::new("alpine:latest")
2145            .dns("8.8.8.8")
2146            .dns("8.8.4.4")
2147            .dns_servers(vec!["1.1.1.1".to_string(), "1.0.0.1".to_string()])
2148            .dns_option("ndots:2")
2149            .dns_option("timeout:1")
2150            .dns_search("example.com")
2151            .dns_search("test.local")
2152            .add_host("api.example.com:127.0.0.1")
2153            .add_host("db.example.com:192.168.1.100");
2154
2155        let args = cmd.build_command_args();
2156
2157        // DNS servers
2158        assert!(args.contains(&"--dns".to_string()));
2159        assert!(args.contains(&"8.8.8.8".to_string()));
2160        assert!(args.contains(&"8.8.4.4".to_string()));
2161        assert!(args.contains(&"1.1.1.1".to_string()));
2162        assert!(args.contains(&"1.0.0.1".to_string()));
2163
2164        // DNS options
2165        assert!(args.contains(&"--dns-option".to_string()));
2166        assert!(args.contains(&"ndots:2".to_string()));
2167        assert!(args.contains(&"timeout:1".to_string()));
2168
2169        // DNS search domains
2170        assert!(args.contains(&"--dns-search".to_string()));
2171        assert!(args.contains(&"example.com".to_string()));
2172        assert!(args.contains(&"test.local".to_string()));
2173
2174        // Host mappings
2175        assert!(args.contains(&"--add-host".to_string()));
2176        assert!(args.contains(&"api.example.com:127.0.0.1".to_string()));
2177        assert!(args.contains(&"db.example.com:192.168.1.100".to_string()));
2178    }
2179
2180    #[test]
2181    fn test_run_command_security_capabilities() {
2182        let cmd = RunCommand::new("alpine:latest")
2183            .cap_add("NET_ADMIN")
2184            .cap_add("SYS_TIME")
2185            .cap_drop("CHOWN")
2186            .cap_drop("DAC_OVERRIDE")
2187            .security_opt("no-new-privileges:true")
2188            .security_opt("seccomp=unconfined");
2189
2190        let args = cmd.build_command_args();
2191
2192        // Capabilities to add
2193        assert!(args.contains(&"--cap-add".to_string()));
2194        assert!(args.contains(&"NET_ADMIN".to_string()));
2195        assert!(args.contains(&"SYS_TIME".to_string()));
2196
2197        // Capabilities to drop
2198        assert!(args.contains(&"--cap-drop".to_string()));
2199        assert!(args.contains(&"CHOWN".to_string()));
2200        assert!(args.contains(&"DAC_OVERRIDE".to_string()));
2201
2202        // Security options
2203        assert!(args.contains(&"--security-opt".to_string()));
2204        assert!(args.contains(&"no-new-privileges:true".to_string()));
2205        assert!(args.contains(&"seccomp=unconfined".to_string()));
2206    }
2207
2208    #[test]
2209    fn test_run_command_device_filesystem() {
2210        let cmd = RunCommand::new("alpine:latest")
2211            .device("/dev/sda:/dev/xvda:rwm")
2212            .device("/dev/zero")
2213            .tmpfs("/tmp:rw,size=100m")
2214            .tmpfs("/var/tmp:ro")
2215            .expose("80")
2216            .expose("443")
2217            .expose("8080/tcp");
2218
2219        let args = cmd.build_command_args();
2220
2221        // Devices
2222        assert!(args.contains(&"--device".to_string()));
2223        assert!(args.contains(&"/dev/sda:/dev/xvda:rwm".to_string()));
2224        assert!(args.contains(&"/dev/zero".to_string()));
2225
2226        // Tmpfs mounts
2227        assert!(args.contains(&"--tmpfs".to_string()));
2228        assert!(args.contains(&"/tmp:rw,size=100m".to_string()));
2229        assert!(args.contains(&"/var/tmp:ro".to_string()));
2230
2231        // Exposed ports
2232        assert!(args.contains(&"--expose".to_string()));
2233        assert!(args.contains(&"80".to_string()));
2234        assert!(args.contains(&"443".to_string()));
2235        assert!(args.contains(&"8080/tcp".to_string()));
2236    }
2237
2238    #[test]
2239    fn test_run_command_environment_labels() {
2240        use std::path::PathBuf;
2241
2242        let cmd = RunCommand::new("alpine:latest")
2243            .env_file(PathBuf::from("/etc/environment"))
2244            .env_file(PathBuf::from("./app.env"))
2245            .label("version=1.0.0")
2246            .label("maintainer=team@example.com")
2247            .label("app=myapp")
2248            .label_file(PathBuf::from("/etc/labels"))
2249            .label_file(PathBuf::from("./metadata.labels"));
2250
2251        let args = cmd.build_command_args();
2252
2253        // Environment files
2254        assert!(args.contains(&"--env-file".to_string()));
2255        assert!(args.contains(&"/etc/environment".to_string()));
2256        assert!(args.contains(&"./app.env".to_string()));
2257
2258        // Labels
2259        assert!(args.contains(&"--label".to_string()));
2260        assert!(args.contains(&"version=1.0.0".to_string()));
2261        assert!(args.contains(&"maintainer=team@example.com".to_string()));
2262        assert!(args.contains(&"app=myapp".to_string()));
2263
2264        // Label files
2265        assert!(args.contains(&"--label-file".to_string()));
2266        assert!(args.contains(&"/etc/labels".to_string()));
2267        assert!(args.contains(&"./metadata.labels".to_string()));
2268    }
2269
2270    #[test]
2271    fn test_run_command_all_high_impact_options() {
2272        use std::path::PathBuf;
2273
2274        let cmd = RunCommand::new("nginx:latest")
2275            .name("production-nginx")
2276            // DNS & Network
2277            .dns("8.8.8.8")
2278            .dns_option("ndots:2")
2279            .dns_search("example.com")
2280            .add_host("api.example.com:127.0.0.1")
2281            // Security & Capabilities
2282            .cap_add("NET_ADMIN")
2283            .cap_drop("CHOWN")
2284            .security_opt("no-new-privileges:true")
2285            // Device & Filesystem
2286            .device("/dev/null")
2287            .tmpfs("/tmp:rw,size=100m")
2288            .expose("80")
2289            // Environment & Labels
2290            .env_file(PathBuf::from(".env"))
2291            .label("version=1.0.0")
2292            .label_file(PathBuf::from("labels"));
2293
2294        let args = cmd.build_command_args();
2295
2296        // Verify all option types are present
2297        assert!(args.contains(&"--dns".to_string()));
2298        assert!(args.contains(&"--dns-option".to_string()));
2299        assert!(args.contains(&"--dns-search".to_string()));
2300        assert!(args.contains(&"--add-host".to_string()));
2301        assert!(args.contains(&"--cap-add".to_string()));
2302        assert!(args.contains(&"--cap-drop".to_string()));
2303        assert!(args.contains(&"--security-opt".to_string()));
2304        assert!(args.contains(&"--device".to_string()));
2305        assert!(args.contains(&"--tmpfs".to_string()));
2306        assert!(args.contains(&"--expose".to_string()));
2307        assert!(args.contains(&"--env-file".to_string()));
2308        assert!(args.contains(&"--label".to_string()));
2309        assert!(args.contains(&"--label-file".to_string()));
2310
2311        // Verify image is still at the end
2312        let image_pos = args.iter().position(|x| x == "nginx:latest").unwrap();
2313        assert!(image_pos > 0); // Should not be first
2314        assert!(image_pos < args.len() - 1 || args.len() == image_pos + 1); // Should be near end
2315    }
2316
2317    #[test]
2318    fn test_run_command_empty_lists_not_added() {
2319        let cmd = RunCommand::new("alpine:latest");
2320        let args = cmd.build_command_args();
2321
2322        // Ensure empty lists don't add any arguments
2323        assert!(!args.contains(&"--dns".to_string()));
2324        assert!(!args.contains(&"--dns-option".to_string()));
2325        assert!(!args.contains(&"--dns-search".to_string()));
2326        assert!(!args.contains(&"--add-host".to_string()));
2327        assert!(!args.contains(&"--cap-add".to_string()));
2328        assert!(!args.contains(&"--cap-drop".to_string()));
2329        assert!(!args.contains(&"--security-opt".to_string()));
2330        assert!(!args.contains(&"--device".to_string()));
2331        assert!(!args.contains(&"--tmpfs".to_string()));
2332        assert!(!args.contains(&"--expose".to_string()));
2333        assert!(!args.contains(&"--env-file".to_string()));
2334        assert!(!args.contains(&"--label".to_string()));
2335        assert!(!args.contains(&"--label-file".to_string()));
2336
2337        // Additional list options should also not be present
2338        assert!(!args.contains(&"--network-alias".to_string()));
2339        assert!(!args.contains(&"--group-add".to_string()));
2340        assert!(!args.contains(&"--attach".to_string()));
2341        assert!(!args.contains(&"--log-opt".to_string()));
2342        assert!(!args.contains(&"--storage-opt".to_string()));
2343        assert!(!args.contains(&"--ulimit".to_string()));
2344        assert!(!args.contains(&"--volumes-from".to_string()));
2345        assert!(!args.contains(&"--link".to_string()));
2346        assert!(!args.contains(&"--link-local-ip".to_string()));
2347
2348        // But image should still be there
2349        assert!(args.contains(&"alpine:latest".to_string()));
2350    }
2351
2352    #[test]
2353    fn test_run_command_additional_list_options() {
2354        let cmd = RunCommand::new("alpine:latest")
2355            .network_alias("web")
2356            .network_alias("frontend")
2357            .group_add("staff")
2358            .group_add("docker")
2359            .attach("stdout")
2360            .attach("stderr")
2361            .log_opt("max-size=10m")
2362            .log_opt("max-file=3")
2363            .storage_opt("size=20G")
2364            .ulimit("nofile=1024:65536")
2365            .ulimit("nproc=1024")
2366            .volumes_from("data-container")
2367            .volumes_from("config-container:ro")
2368            .link("db:database")
2369            .link("cache:redis")
2370            .link_local_ip("169.254.1.1")
2371            .link_local_ip("fe80::1");
2372
2373        let args = cmd.build_command_args();
2374
2375        // Network aliases
2376        assert!(args.contains(&"--network-alias".to_string()));
2377        assert!(args.contains(&"web".to_string()));
2378        assert!(args.contains(&"frontend".to_string()));
2379
2380        // Group additions
2381        assert!(args.contains(&"--group-add".to_string()));
2382        assert!(args.contains(&"staff".to_string()));
2383        assert!(args.contains(&"docker".to_string()));
2384
2385        // Stream attachments
2386        assert!(args.contains(&"--attach".to_string()));
2387        assert!(args.contains(&"stdout".to_string()));
2388        assert!(args.contains(&"stderr".to_string()));
2389
2390        // Log options
2391        assert!(args.contains(&"--log-opt".to_string()));
2392        assert!(args.contains(&"max-size=10m".to_string()));
2393        assert!(args.contains(&"max-file=3".to_string()));
2394
2395        // Storage options
2396        assert!(args.contains(&"--storage-opt".to_string()));
2397        assert!(args.contains(&"size=20G".to_string()));
2398
2399        // Ulimits
2400        assert!(args.contains(&"--ulimit".to_string()));
2401        assert!(args.contains(&"nofile=1024:65536".to_string()));
2402        assert!(args.contains(&"nproc=1024".to_string()));
2403
2404        // Volumes from containers
2405        assert!(args.contains(&"--volumes-from".to_string()));
2406        assert!(args.contains(&"data-container".to_string()));
2407        assert!(args.contains(&"config-container:ro".to_string()));
2408
2409        // Container links
2410        assert!(args.contains(&"--link".to_string()));
2411        assert!(args.contains(&"db:database".to_string()));
2412        assert!(args.contains(&"cache:redis".to_string()));
2413
2414        // Link-local IPs
2415        assert!(args.contains(&"--link-local-ip".to_string()));
2416        assert!(args.contains(&"169.254.1.1".to_string()));
2417        assert!(args.contains(&"fe80::1".to_string()));
2418    }
2419
2420    #[test]
2421    fn test_run_command_additional_list_individual_options() {
2422        // Test each additional list option individually for proper argument generation
2423        let network_cmd = RunCommand::new("alpine:latest").network_alias("api");
2424        let network_args = network_cmd.build_command_args();
2425        assert!(network_args.contains(&"--network-alias".to_string()));
2426        assert!(network_args.contains(&"api".to_string()));
2427
2428        let group_cmd = RunCommand::new("alpine:latest").group_add("wheel");
2429        let group_args = group_cmd.build_command_args();
2430        assert!(group_args.contains(&"--group-add".to_string()));
2431        assert!(group_args.contains(&"wheel".to_string()));
2432
2433        let attach_cmd = RunCommand::new("alpine:latest").attach("stdin");
2434        let attach_args = attach_cmd.build_command_args();
2435        assert!(attach_args.contains(&"--attach".to_string()));
2436        assert!(attach_args.contains(&"stdin".to_string()));
2437
2438        let log_cmd = RunCommand::new("alpine:latest").log_opt("compress=true");
2439        let log_args = log_cmd.build_command_args();
2440        assert!(log_args.contains(&"--log-opt".to_string()));
2441        assert!(log_args.contains(&"compress=true".to_string()));
2442
2443        let storage_cmd =
2444            RunCommand::new("alpine:latest").storage_opt("dm.thinpooldev=/dev/mapper/thin-pool");
2445        let storage_args = storage_cmd.build_command_args();
2446        assert!(storage_args.contains(&"--storage-opt".to_string()));
2447        assert!(storage_args.contains(&"dm.thinpooldev=/dev/mapper/thin-pool".to_string()));
2448
2449        let ulimit_cmd = RunCommand::new("alpine:latest").ulimit("memlock=-1:-1");
2450        let ulimit_args = ulimit_cmd.build_command_args();
2451        assert!(ulimit_args.contains(&"--ulimit".to_string()));
2452        assert!(ulimit_args.contains(&"memlock=-1:-1".to_string()));
2453
2454        let volumes_cmd = RunCommand::new("alpine:latest").volumes_from("shared-data");
2455        let volumes_args = volumes_cmd.build_command_args();
2456        assert!(volumes_args.contains(&"--volumes-from".to_string()));
2457        assert!(volumes_args.contains(&"shared-data".to_string()));
2458
2459        let link_cmd = RunCommand::new("alpine:latest").link("mysql:db");
2460        let link_args = link_cmd.build_command_args();
2461        assert!(link_args.contains(&"--link".to_string()));
2462        assert!(link_args.contains(&"mysql:db".to_string()));
2463
2464        let ip_cmd = RunCommand::new("alpine:latest").link_local_ip("169.254.100.1");
2465        let ip_args = ip_cmd.build_command_args();
2466        assert!(ip_args.contains(&"--link-local-ip".to_string()));
2467        assert!(ip_args.contains(&"169.254.100.1".to_string()));
2468    }
2469
2470    #[test]
2471    fn test_run_command_health_check_options() {
2472        let cmd = RunCommand::new("nginx:latest")
2473            .health_cmd("curl -f http://localhost/ || exit 1")
2474            .health_interval("30s")
2475            .health_retries(3)
2476            .health_timeout("5s")
2477            .health_start_period("60s")
2478            .health_start_interval("5s");
2479
2480        let args = cmd.build_command_args();
2481
2482        // Health check options
2483        assert!(args.contains(&"--health-cmd".to_string()));
2484        assert!(args.contains(&"curl -f http://localhost/ || exit 1".to_string()));
2485        assert!(args.contains(&"--health-interval".to_string()));
2486        assert!(args.contains(&"30s".to_string()));
2487        assert!(args.contains(&"--health-retries".to_string()));
2488        assert!(args.contains(&"3".to_string()));
2489        assert!(args.contains(&"--health-timeout".to_string()));
2490        assert!(args.contains(&"5s".to_string()));
2491        assert!(args.contains(&"--health-start-period".to_string()));
2492        assert!(args.contains(&"60s".to_string()));
2493        assert!(args.contains(&"--health-start-interval".to_string()));
2494        // Note: "5s" appears twice, so we can't easily test for this specific one
2495    }
2496
2497    #[test]
2498    fn test_run_command_advanced_mount_network_options() {
2499        let cmd = RunCommand::new("alpine:latest")
2500            .mount("type=bind,source=/host/path,target=/container/path")
2501            .mount("type=volume,source=data-vol,target=/data")
2502            .network("frontend")
2503            .network("backend")
2504            .gpus("all")
2505            .annotation("io.kubernetes.cri-o.Devices", "/dev/fuse")
2506            .annotation("io.kubernetes.cri-o.ShmSize", "64m")
2507            .sysctl("net.core.somaxconn", "1024")
2508            .sysctl("kernel.shm_rmid_forced", "1");
2509
2510        let args = cmd.build_command_args();
2511
2512        // Mount options
2513        assert!(args.contains(&"--mount".to_string()));
2514        assert!(args.contains(&"type=bind,source=/host/path,target=/container/path".to_string()));
2515        assert!(args.contains(&"type=volume,source=data-vol,target=/data".to_string()));
2516
2517        // Network options
2518        assert!(args.contains(&"--network".to_string()));
2519        assert!(args.contains(&"frontend".to_string()));
2520        assert!(args.contains(&"backend".to_string()));
2521
2522        // GPU options
2523        assert!(args.contains(&"--gpus".to_string()));
2524        assert!(args.contains(&"all".to_string()));
2525
2526        // Annotation options
2527        assert!(args.contains(&"--annotation".to_string()));
2528        assert!(args.contains(&"io.kubernetes.cri-o.Devices=/dev/fuse".to_string()));
2529        assert!(args.contains(&"io.kubernetes.cri-o.ShmSize=64m".to_string()));
2530
2531        // Sysctl options
2532        assert!(args.contains(&"--sysctl".to_string()));
2533        assert!(args.contains(&"net.core.somaxconn=1024".to_string()));
2534        assert!(args.contains(&"kernel.shm_rmid_forced=1".to_string()));
2535    }
2536
2537    #[test]
2538    fn test_run_command_health_advanced_individual_options() {
2539        // Test each health check and advanced option individually
2540        let health_cmd = RunCommand::new("alpine:latest").health_cmd("ping -c 1 localhost");
2541        let health_args = health_cmd.build_command_args();
2542        assert!(health_args.contains(&"--health-cmd".to_string()));
2543        assert!(health_args.contains(&"ping -c 1 localhost".to_string()));
2544
2545        let health_interval = RunCommand::new("alpine:latest").health_interval("10s");
2546        let interval_args = health_interval.build_command_args();
2547        assert!(interval_args.contains(&"--health-interval".to_string()));
2548        assert!(interval_args.contains(&"10s".to_string()));
2549
2550        let health_retries = RunCommand::new("alpine:latest").health_retries(5);
2551        let retries_args = health_retries.build_command_args();
2552        assert!(retries_args.contains(&"--health-retries".to_string()));
2553        assert!(retries_args.contains(&"5".to_string()));
2554
2555        let mount_cmd = RunCommand::new("alpine:latest").mount("type=tmpfs,destination=/app");
2556        let mount_args = mount_cmd.build_command_args();
2557        assert!(mount_args.contains(&"--mount".to_string()));
2558        assert!(mount_args.contains(&"type=tmpfs,destination=/app".to_string()));
2559
2560        let network_cmd = RunCommand::new("alpine:latest").network("my-network");
2561        let network_args = network_cmd.build_command_args();
2562        assert!(network_args.contains(&"--network".to_string()));
2563        assert!(network_args.contains(&"my-network".to_string()));
2564
2565        let gpu_cmd = RunCommand::new("alpine:latest").gpus("device=0");
2566        let gpu_args = gpu_cmd.build_command_args();
2567        assert!(gpu_args.contains(&"--gpus".to_string()));
2568        assert!(gpu_args.contains(&"device=0".to_string()));
2569
2570        let annotation_cmd = RunCommand::new("alpine:latest").annotation("key", "value");
2571        let annotation_args = annotation_cmd.build_command_args();
2572        assert!(annotation_args.contains(&"--annotation".to_string()));
2573        assert!(annotation_args.contains(&"key=value".to_string()));
2574
2575        let sysctl_cmd = RunCommand::new("alpine:latest").sysctl("net.ipv4.ip_forward", "1");
2576        let sysctl_args = sysctl_cmd.build_command_args();
2577        assert!(sysctl_args.contains(&"--sysctl".to_string()));
2578        assert!(sysctl_args.contains(&"net.ipv4.ip_forward=1".to_string()));
2579    }
2580
2581    #[test]
2582    fn test_run_command_comprehensive_health_advanced_integration() {
2583        let cmd = RunCommand::new("web-app:latest")
2584            .name("production-web-app")
2585            // Health checks for production readiness
2586            .health_cmd("curl -f http://localhost:8080/health || exit 1")
2587            .health_interval("30s")
2588            .health_retries(3)
2589            .health_timeout("10s")
2590            .health_start_period("120s")
2591            // Advanced mounting and networking
2592            .mount("type=bind,source=/var/log/app,target=/app/logs")
2593            .mount("type=volume,source=app-data,target=/app/data")
2594            .network("frontend")
2595            .network("backend")
2596            // GPU support for ML workloads
2597            .gpus("device=0,1")
2598            // Kubernetes annotations
2599            .annotation(
2600                "io.kubernetes.container.apparmor.security.beta.kubernetes.io/app",
2601                "runtime/default",
2602            )
2603            .annotation(
2604                "io.kubernetes.container.seccomp.security.alpha.kubernetes.io/app",
2605                "runtime/default",
2606            )
2607            // System tuning
2608            .sysctl("net.core.somaxconn", "65535")
2609            .sysctl("net.ipv4.tcp_keepalive_time", "600")
2610            // Additional standard options
2611            .port(8080, 8080)
2612            .env("NODE_ENV", "production")
2613            .memory("2g")
2614            .cpus("2.0")
2615            .restart("unless-stopped")
2616            .detach();
2617
2618        let args = cmd.build_command_args();
2619
2620        // Verify all health check and advanced options are present
2621        assert!(args.contains(&"--health-cmd".to_string()));
2622        assert!(args.contains(&"--health-interval".to_string()));
2623        assert!(args.contains(&"--health-retries".to_string()));
2624        assert!(args.contains(&"--health-timeout".to_string()));
2625        assert!(args.contains(&"--health-start-period".to_string()));
2626        assert!(args.contains(&"--mount".to_string()));
2627        assert!(args.contains(&"--network".to_string()));
2628        assert!(args.contains(&"--gpus".to_string()));
2629        assert!(args.contains(&"--annotation".to_string()));
2630        assert!(args.contains(&"--sysctl".to_string()));
2631
2632        // Verify image is still at the end
2633        let image_pos = args.iter().position(|x| x == "web-app:latest").unwrap();
2634        assert!(image_pos > 0);
2635    }
2636
2637    #[test]
2638    fn test_run_command_block_io_controls() {
2639        let cmd = RunCommand::new("alpine:latest")
2640            .blkio_weight(500)
2641            .blkio_weight_device("/dev/sda:300")
2642            .blkio_weight_device("/dev/sdb:700")
2643            .device_read_bps("/dev/sda:50mb")
2644            .device_write_bps("/dev/sda:30mb")
2645            .device_read_iops("/dev/sda:1000")
2646            .device_write_iops("/dev/sda:800");
2647
2648        let args = cmd.build_command_args();
2649
2650        // Block I/O weight
2651        assert!(args.contains(&"--blkio-weight".to_string()));
2652        assert!(args.contains(&"500".to_string()));
2653
2654        // Block I/O weight per device
2655        assert!(args.contains(&"--blkio-weight-device".to_string()));
2656        assert!(args.contains(&"/dev/sda:300".to_string()));
2657        assert!(args.contains(&"/dev/sdb:700".to_string()));
2658
2659        // Device read/write BPS
2660        assert!(args.contains(&"--device-read-bps".to_string()));
2661        assert!(args.contains(&"/dev/sda:50mb".to_string()));
2662        assert!(args.contains(&"--device-write-bps".to_string()));
2663        assert!(args.contains(&"/dev/sda:30mb".to_string()));
2664
2665        // Device read/write IOPS
2666        assert!(args.contains(&"--device-read-iops".to_string()));
2667        assert!(args.contains(&"/dev/sda:1000".to_string()));
2668        assert!(args.contains(&"--device-write-iops".to_string()));
2669        assert!(args.contains(&"/dev/sda:800".to_string()));
2670    }
2671
2672    #[test]
2673    fn test_run_command_realtime_cpu_networking() {
2674        let cmd = RunCommand::new("alpine:latest")
2675            .cpu_rt_period(1_000_000)
2676            .cpu_rt_runtime(950_000)
2677            .ip("172.30.100.104")
2678            .ip6("2001:db8::33")
2679            .device_cgroup_rule("c 1:3 mr")
2680            .device_cgroup_rule("a 7:* rmw");
2681
2682        let args = cmd.build_command_args();
2683
2684        // Real-time CPU scheduling
2685        assert!(args.contains(&"--cpu-rt-period".to_string()));
2686        assert!(args.contains(&"1000000".to_string()));
2687        assert!(args.contains(&"--cpu-rt-runtime".to_string()));
2688        assert!(args.contains(&"950000".to_string()));
2689
2690        // Advanced networking
2691        assert!(args.contains(&"--ip".to_string()));
2692        assert!(args.contains(&"172.30.100.104".to_string()));
2693        assert!(args.contains(&"--ip6".to_string()));
2694        assert!(args.contains(&"2001:db8::33".to_string()));
2695
2696        // Device cgroup rules
2697        assert!(args.contains(&"--device-cgroup-rule".to_string()));
2698        assert!(args.contains(&"c 1:3 mr".to_string()));
2699        assert!(args.contains(&"a 7:* rmw".to_string()));
2700    }
2701
2702    #[test]
2703    fn test_run_command_advanced_system_individual_options() {
2704        // Test each advanced system option individually
2705        let blkio_cmd = RunCommand::new("alpine:latest").blkio_weight(100);
2706        let blkio_args = blkio_cmd.build_command_args();
2707        assert!(blkio_args.contains(&"--blkio-weight".to_string()));
2708        assert!(blkio_args.contains(&"100".to_string()));
2709
2710        let weight_device_cmd =
2711            RunCommand::new("alpine:latest").blkio_weight_device("/dev/sda:500");
2712        let weight_device_args = weight_device_cmd.build_command_args();
2713        assert!(weight_device_args.contains(&"--blkio-weight-device".to_string()));
2714        assert!(weight_device_args.contains(&"/dev/sda:500".to_string()));
2715
2716        let read_bps_cmd = RunCommand::new("alpine:latest").device_read_bps("/dev/sda:1mb");
2717        let read_bps_args = read_bps_cmd.build_command_args();
2718        assert!(read_bps_args.contains(&"--device-read-bps".to_string()));
2719        assert!(read_bps_args.contains(&"/dev/sda:1mb".to_string()));
2720
2721        let write_bps_cmd = RunCommand::new("alpine:latest").device_write_bps("/dev/sda:1mb");
2722        let write_bps_args = write_bps_cmd.build_command_args();
2723        assert!(write_bps_args.contains(&"--device-write-bps".to_string()));
2724        assert!(write_bps_args.contains(&"/dev/sda:1mb".to_string()));
2725
2726        let read_iops_cmd = RunCommand::new("alpine:latest").device_read_iops("/dev/sda:100");
2727        let read_iops_args = read_iops_cmd.build_command_args();
2728        assert!(read_iops_args.contains(&"--device-read-iops".to_string()));
2729        assert!(read_iops_args.contains(&"/dev/sda:100".to_string()));
2730
2731        let write_iops_cmd = RunCommand::new("alpine:latest").device_write_iops("/dev/sda:100");
2732        let write_iops_args = write_iops_cmd.build_command_args();
2733        assert!(write_iops_args.contains(&"--device-write-iops".to_string()));
2734        assert!(write_iops_args.contains(&"/dev/sda:100".to_string()));
2735
2736        let rt_period_cmd = RunCommand::new("alpine:latest").cpu_rt_period(100_000);
2737        let rt_period_args = rt_period_cmd.build_command_args();
2738        assert!(rt_period_args.contains(&"--cpu-rt-period".to_string()));
2739        assert!(rt_period_args.contains(&"100000".to_string()));
2740
2741        let rt_runtime_cmd = RunCommand::new("alpine:latest").cpu_rt_runtime(95_000);
2742        let rt_runtime_args = rt_runtime_cmd.build_command_args();
2743        assert!(rt_runtime_args.contains(&"--cpu-rt-runtime".to_string()));
2744        assert!(rt_runtime_args.contains(&"95000".to_string()));
2745
2746        let ip_cmd = RunCommand::new("alpine:latest").ip("192.168.1.100");
2747        let ip_args = ip_cmd.build_command_args();
2748        assert!(ip_args.contains(&"--ip".to_string()));
2749        assert!(ip_args.contains(&"192.168.1.100".to_string()));
2750
2751        let ipv6_cmd = RunCommand::new("alpine:latest").ip6("fe80::1");
2752        let ipv6_args = ipv6_cmd.build_command_args();
2753        assert!(ipv6_args.contains(&"--ip6".to_string()));
2754        assert!(ipv6_args.contains(&"fe80::1".to_string()));
2755
2756        let cgroup_rule_cmd = RunCommand::new("alpine:latest").device_cgroup_rule("c 1:1 rwm");
2757        let cgroup_rule_args = cgroup_rule_cmd.build_command_args();
2758        assert!(cgroup_rule_args.contains(&"--device-cgroup-rule".to_string()));
2759        assert!(cgroup_rule_args.contains(&"c 1:1 rwm".to_string()));
2760    }
2761
2762    #[test]
2763    #[allow(clippy::too_many_lines)]
2764    fn test_run_command_complete_100_percent_coverage() {
2765        use std::path::PathBuf;
2766
2767        // Test demonstrating ALL 96 Docker run options are now implemented
2768        let cmd = RunCommand::new("enterprise-app:latest")
2769            .name("production-enterprise")
2770            // Basic options
2771            .detach()
2772            .interactive()
2773            .tty()
2774            .remove()
2775            // Environment and ports
2776            .env("NODE_ENV", "production")
2777            .port(8080, 8080)
2778            .volume("/data", "/app/data")
2779            .workdir("/app")
2780            .entrypoint("/app/start.sh")
2781            // Resource limits (Phase 2)
2782            .memory("4g")
2783            .cpus("2.0")
2784            .cpu_shares(1024)
2785            .cpu_period(100_000)
2786            .cpu_quota(50000)
2787            .cpuset_cpus("0-1")
2788            .cpuset_mems("0")
2789            .memory_swap("8g")
2790            .memory_reservation("2g")
2791            // Security & User Context (Phase 2)
2792            .user("app:app")
2793            .privileged()
2794            .hostname("enterprise-app")
2795            // Lifecycle Management (Phase 2)
2796            .restart("unless-stopped")
2797            // System Integration (Phase 2)
2798            .platform("linux/amd64")
2799            .runtime("runc")
2800            .isolation("default")
2801            .pull("always")
2802            .cidfile("/tmp/container.cid")
2803            .domainname("enterprise.local")
2804            .mac_address("02:42:ac:11:00:02")
2805            // Logging & Drivers (Phase 2)
2806            .log_driver("json-file")
2807            .volume_driver("local")
2808            // Namespaces (Phase 2)
2809            .userns("host")
2810            .uts("host")
2811            .pid("host")
2812            .ipc("host")
2813            .cgroupns("host")
2814            .cgroup_parent("/docker")
2815            // Advanced Memory & Performance (Phase 2)
2816            .kernel_memory("1g")
2817            .memory_swappiness(10)
2818            .oom_score_adj(-500)
2819            .pids_limit(1000)
2820            .shm_size("64m")
2821            // Process Control (Phase 2)
2822            .stop_signal("SIGTERM")
2823            .stop_timeout(30)
2824            .detach_keys("ctrl-p,ctrl-q")
2825            // Simple Flags (Phase 2)
2826            .no_sig_proxy()
2827            .read_only()
2828            .init()
2829            .oom_kill_disable()
2830            .no_healthcheck()
2831            .enable_content_trust()
2832            .publish_all()
2833            .quiet()
2834            // High-Impact List Options (Phase 3)
2835            .dns("8.8.8.8")
2836            .dns_option("ndots:2")
2837            .dns_search("enterprise.local")
2838            .add_host("api.enterprise.local:10.0.1.100")
2839            .cap_add("NET_ADMIN")
2840            .cap_drop("ALL")
2841            .security_opt("no-new-privileges:true")
2842            .device("/dev/null")
2843            .tmpfs("/tmp:size=100m")
2844            .expose("9090")
2845            .env_file(PathBuf::from(".env.production"))
2846            .label("app=enterprise")
2847            .label_file(PathBuf::from("labels.txt"))
2848            // Batch 1: Additional List/Vec Options
2849            .network_alias("enterprise-primary")
2850            .group_add("staff")
2851            .attach("stdout")
2852            .log_opt("max-size=10m")
2853            .storage_opt("size=100G")
2854            .ulimit("nofile=65536:65536")
2855            .volumes_from("data-container")
2856            .link("db:database")
2857            .link_local_ip("169.254.1.1")
2858            // Batch 2: Medium Complexity Options
2859            .health_cmd("curl -f http://localhost:8080/health || exit 1")
2860            .health_interval("30s")
2861            .health_retries(3)
2862            .health_timeout("10s")
2863            .health_start_period("60s")
2864            .health_start_interval("5s")
2865            .mount("type=bind,source=/host/config,target=/app/config")
2866            .network("enterprise-net")
2867            .gpus("device=0")
2868            .annotation("io.kubernetes.cri-o.TTY", "true")
2869            .sysctl("net.core.somaxconn", "65535")
2870            // Batch 3: Complex/Advanced Options
2871            .blkio_weight(500)
2872            .blkio_weight_device("/dev/sda:300")
2873            .device_read_bps("/dev/sda:100mb")
2874            .device_write_bps("/dev/sda:50mb")
2875            .device_read_iops("/dev/sda:1000")
2876            .device_write_iops("/dev/sda:500")
2877            .cpu_rt_period(1_000_000)
2878            .cpu_rt_runtime(950_000)
2879            .ip("10.0.1.50")
2880            .ip6("2001:db8::50")
2881            .device_cgroup_rule("c 1:1 rwm");
2882
2883        let args = cmd.build_command_args();
2884
2885        // Verify we have a substantial command with all option types
2886        assert!(args.len() > 150); // Should be a very long command
2887
2888        // Verify key options from each batch are present
2889        assert!(args.contains(&"--detach".to_string()));
2890        assert!(args.contains(&"--memory".to_string()));
2891        assert!(args.contains(&"--dns".to_string()));
2892        assert!(args.contains(&"--network-alias".to_string()));
2893        assert!(args.contains(&"--health-cmd".to_string()));
2894        assert!(args.contains(&"--blkio-weight".to_string()));
2895
2896        // Verify image is still at the end
2897        let image_pos = args
2898            .iter()
2899            .position(|x| x == "enterprise-app:latest")
2900            .unwrap();
2901        assert!(image_pos > 100); // Should be very far into the command
2902        assert_eq!(args[args.len() - 1], "enterprise-app:latest");
2903
2904        println!("COMPLETE! All 96 Docker run options implemented and tested!");
2905    }
2906}