nanite_docker/instruction/run/mod.rs
1// WARNING: This is by far the longest and most complex system in the whole project. Most Instructions have only maybe 30 odd lines of code and were written in an hour
2// This one took me a few weekends to finally figure out how to make it ergonomic
3// ENTER AT YOUR OWN RISK
4
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::fmt::{Display, Formatter};
8
9mod runmount;
10pub use runmount::{
11 RunMount, RunMountBind, RunMountBindOpts, RunMountCache, RunMountCacheOpts, RunMountSSH,
12 RunMountSSHOpts, RunMountSecret, RunMountSecretOpts, RunMountTmpfs, RunMountTmpfsOpts,
13 RunSharing,
14};
15/// Represents a `RUN` instruction.
16/// The RUN instruction is a bit more complex than other instructions, due to how mounting works.
17/// ```rust
18/// use nanite_docker::*;
19///
20/// let run = Run {
21/// argv: vec![
22/// "bash".into(),
23/// "-c".into(),
24/// "echo building && make all".into(),
25/// ],
26/// mounts: vec![
27/// RunMount::Bind(RunMountBind {
28/// target: "/mnt/bind".into(),
29/// opts: vec![RunMountBindOpts::ReadWrite],
30/// }),
31/// RunMount::Cache(RunMountCache {
32/// target: "/mnt/cache".into(),
33/// opts: vec![
34/// RunMountCacheOpts::Id("build-cache".into()),
35/// RunMountCacheOpts::Sharing(RunSharing::Shared),
36/// RunMountCacheOpts::Uid(1000),
37/// RunMountCacheOpts::Gid(1000),
38/// RunMountCacheOpts::Mode("0o755".into()),
39/// ],
40/// }),
41/// RunMount::Ssh(RunMountSSH {
42/// target: None,
43/// opts: vec![
44/// RunMountSSHOpts::Id("default".into()),
45/// RunMountSSHOpts::Required,
46/// ],
47/// }),
48/// RunMount::Secret(RunMountSecret {
49/// target: Some("/run/secrets/mysecret".into()),
50/// opts: vec![
51/// RunMountSecretOpts::Id("mysecret".into()),
52/// RunMountSecretOpts::Required,
53/// ],
54/// }),
55/// RunMount::Tmpfs(RunMountTmpfs {
56/// target: "/mnt/tmpfs".into(),
57/// opts: vec![RunMountTmpfsOpts::Size("65536".into())],
58/// }),
59/// ],
60/// network: Some(RunNetwork::Host),
61/// security: Some(RunSecurity::Sandbox),
62/// };
63///
64/// let run_built = format!("{run}");
65/// assert_eq!(run_built, r#"RUN --mount=type=bind,target=/mnt/bind,readwrite=true --mount=type=cache,target=/mnt/cache,id=build-cache,sharing=shared,uid=1000,gid=1000,mode=0o755 --mount=type=ssh,id=default,required=true --mount=type=secret,source=/run/secrets/mysecret,id=mysecret,required=true --mount=type=tmpfs,target=/mnt/tmpfs,size=65536 --network=host --security=sandbox ["bash", "-c", "echo building && make all"]"#)
66/// ```
67#[derive(Debug, Clone)]
68pub struct Run {
69 pub argv: Vec<String>,
70 pub mounts: Vec<runmount::RunMount>,
71 pub network: Option<RunNetwork>,
72 pub security: Option<RunSecurity>,
73}
74
75impl Display for Run {
76 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
77 write!(f, "RUN ")?;
78 for i in &self.mounts {
79 match i {
80 RunMount::Bind(b) => write!(f, "{b} "),
81 RunMount::Cache(c) => write!(f, "{c} "),
82 RunMount::Tmpfs(t) => write!(f, "{t} "),
83 RunMount::Secret(s) => write!(f, "{s} "),
84 RunMount::Ssh(s) => write!(f, "{s} "),
85 }?;
86 }
87
88 match &self.network {
89 Some(n) => write!(f, "{n} ")?,
90 None => {}
91 };
92
93 match &self.security {
94 Some(s) => write!(f, "{s} ")?,
95 None => {}
96 }
97
98 write!(f, "[")?;
99
100 for (i, arg) in self.argv.iter().enumerate() {
101 if i != 0 {
102 write!(f, ", ")?;
103 }
104 write!(f, r#""{arg}""#)?;
105 }
106
107 write!(f, "]")?;
108
109 Ok(())
110 }
111}
112
113#[derive(Debug, Clone)]
114pub enum RunSecurity {
115 Sandbox,
116 Insecure,
117}
118
119impl Display for RunSecurity {
120 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
121 match self {
122 RunSecurity::Sandbox => write!(f, "--security=sandbox"),
123 RunSecurity::Insecure => write!(f, "--security=insecure"),
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
129pub enum RunNetwork {
130 Default,
131 None,
132 Host,
133}
134
135impl Display for RunNetwork {
136 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
137 match self {
138 RunNetwork::Default => write!(f, "--network=default"),
139 RunNetwork::None => write!(f, "--network=none"),
140 RunNetwork::Host => write!(f, "--network=host"),
141 }
142 }
143}