1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
use std::collections::HashMap;
use std::path::PathBuf;

#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
    /// MUST be in SemVer v2.0.0 format and specifies the version of the Open
    /// Container Initiative Runtime Specification with which the bundle
    /// complies. The Open Container Initiative Runtime Specification follows
    /// semantic versioning and retains forward and backward compatibility
    /// within major versions. For example, if a configuration is compliant with
    /// version 1.1 of this specification, it is compatible with all runtimes
    /// that support any 1.1 or later release of this specification, but is not
    /// compatible with a runtime that supports 1.0 and not 1.1.
    // FIXME: This should probably be a semver::Version
    #[serde(rename = "ociVersion")]
    pub oci_version: String,

    /// specifies the container's root filesystem. On Windows, for Windows
    /// Server Containers, this field is REQUIRED. For Hyper-V Containers,
    /// his field MUST NOT be set.
    ///
    /// On all other platforms, this field is REQUIRED.
    pub root: Option<Root>,

    /// specifies additional mounts beyond `root`. The runtime MUST mount
    /// entries in the listed order. For Linux, the parameters are as documented
    /// in mount(2) system call man page. For Solaris, the mount entry
    /// corresponds to the 'fs' resource in the zonecfg(1M) man page.
    pub mounts: Option<Vec<Mount>>,

    /// specifies the container process. This property is REQUIRED when
    /// [Runtime::start] is called.
    pub process: Option<Process>,

    /// specifies the container's hostname as seen by processes running inside
    /// the container. On Linux, for example, this will change the hostname in
    /// the container UTS namespace. Depending on your namespace configuration,
    /// the container UTS namespace may be the runtime UTS namespace.
    pub hostname: Option<String>,

    /// FIXME: Add Platform-specific configuration
    pub annotations: Option<HashMap<String, String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Root {
    /// Specifies the path to the root filesystem for the container.
    ///
    /// * On Windows, `path` MUST be a volume GUID path.
    /// * On POSIX platforms, `path` is either an absolute path or a relative
    ///   path to the bundle. For example, with a bundle at `/to/bundle` and a
    ///   root filesystem at `/to/bundle/rootfs`, the path value can be either
    ///   `/to/bundle/rootfs` or `rootfs`. The value SHOULD be the conventional
    ///   rootfs.
    pub path: PathBuf,

    /// If true then the root filesystem MUST be read-only inside the container,
    /// defaults to false.
    ///
    /// On Windows, this field MUST be omitted or false.
    pub readonly: Option<bool>,
}

impl Root {
    pub fn readonly(&self) -> bool {
        match self.readonly {
            None => false,
            Some(v) => v,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Mount {
    /// Destination of mount point: path inside container.
    ///
    /// This value MUST be an absolute path.
    ///
    /// * Windows: one mount destination MUST NOT be nested within another mount
    ///   (e.g., c:\foo and c:\foo\bar).
    /// * Solaris: corresponds to "dir" of the fs resource in zonecfg(1M)
    pub destination: PathBuf,

    /// A device name, but can also be a file or directory name for bind mounts
    /// or a dummy.
    ///
    /// Path values for bind mounts are either absolute or relative to the
    /// bundle. A mount is a bind mount if it has either `bind` or `rbind` in
    /// the options.
    ///
    /// * Windows: a local directory on the filesystem of the container host.
    ///   UNC paths and mapped drives are not supported.
    /// * Solaris: corresponds to "special" of the fs resource in zonecfg(1M).
    pub source: Option<PathBuf>,

    /// Mount options of the filesystem to be used.
    ///
    /// * Linux: supported options are listed in the mount(8) man page. Note
    ///   both filesystem-independent and filesystem-specific options are listed.
    /// * Solaris: corresponds to "options" of the fs resource in zonecfg(1M).
    /// * Windows: runtimes MUST support ro, mounting the filesystem read-only
    ///   when ro is given.
    pub options: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Process {
    /// specifies whether a terminal is attached to the process, defaults to
    /// false. As an example, if set to true on Linux a pseudoterminal pair is
    /// allocated for the process and the pseudoterminal slave is duplicated on
    /// the process's standard streams.
    pub terminal: Option<bool>,

    /// specifies the console size in characters of the terminal.
    ///
    /// Runtimes MUST ignore [console_size] if [terminal] is `false`or unset.
    #[serde(rename = "consoleSize")]
    pub console_size: Option<ConsoleSize>,

    /// the working directory that will be set for the executable.
    ///
    /// This value MUST be an absolute path.
    pub cwd: PathBuf,

    /// array of strings with the same semantics as IEEE Std 1003.1-2008's
    /// `environ`.
    pub env: Option<Vec<String>>,

    /// array of strings with similar semantics to IEEE Std 1003.1-2008 execvp's
    /// `argv`. This specification extends the IEEE standard in that at least
    /// one entry is REQUIRED (non-Windows), and that entry is used with the
    /// same semantics as `execvp`'s file. This field is OPTIONAL on Windows,
    /// and commandLine is REQUIRED if this field is omitted.
    pub args: Option<Vec<String>>,

    /// specifies the full command line to be executed on Windows.
    ///
    /// This is the preferred means of supplying the command line on Windows. If
    /// omitted, the runtime will fall back to escaping and concatenating fields
    /// from args before making the system call into Windows.
    pub command_line: Option<String>,

    /// The user for the process is a platform-specific structure that allows
    /// specific control over which user the process runs as.
    user: User,

    #[serde(flatten)]
    pub posix: PosixProcessExt,

    #[serde(flatten)]
    pub linux: LinuxProcessExt,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ConsoleSize {
    pub width: usize,
    pub height: usize,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PosixProcessExt {
    /// Allows setting resource limits for the process.AsMut
    /// If `rlimits` contains duplicated entries with same type, the runtime
    /// MUST generate an error.
    pub rlimits: Option<Vec<RLimit>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct LinuxProcessExt {
    /// specifies the name of the AppArmor profile for the process.
    ///
    /// For more information about AppArmor, see [AppArmor documentation].
    ///
    /// [AppArmor documentation]: https://wiki.ubuntu.com/AppArmor
    #[serde(rename = "apparmorProfile")]
    pub apparmor_profile: Option<String>,

    /// An object containing arrays that specifies the sets of capabilities for
    /// the process. Valid values are defined in the capabilities(7) man page,
    /// such as `CAP_CHOWN`. Any value which cannot be mapped to a relevant
    /// kernel interface MUST cause an error.
    pub capabilities: Option<Capabilities>,

    /// prevents the process from gaining additional privileges. As an example,
    /// the `no_new_privs` article in the kernel documentation has information
    /// on how this is achieved using a `prctl` system call on Linux.
    #[serde(rename = "noNewPrivileges")]
    pub no_new_privileges: Option<bool>,

    /// Adjusts the oom-killer score in `[pid]/oom_score_adj` for the process's
    /// `[pid]` in a proc pseudo-filesystem. If `oomScoreAdj` is set, the
    /// runtime MUST set `oom_score_adj` to the given value.
    /// If `oomScoreAdj` is not set, the runtime MUST NOT change the value of
    /// `oom_score_adj`.
    ///
    /// This is a per-process setting, where as `disableOOMKiller` is scoped for
    /// a memory cgroup. For more information on how these two settings work
    /// together, see the memory cgroup documentation section 10. OOM Contol.
    #[serde(rename = "oomScoreAdj")]
    pub oom_score_adj: Option<i64>,

    /// specifies the SELinux label for the process. For more information about
    /// SELinux, see [SELinux documentation].
    ///
    /// [Selinux documentation]: http://selinuxproject.org/page/Main_Page
    #[serde(rename = "selinuxLabel")]
    pub selinux_label: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RLimit {
    /// The platform resource being limited.
    ///
    /// * Linux: valid values are defined in the getrlimit(2) man page, such as
    ///   `RLIMIT_MSGQUEUE`.
    /// * Solaris: valid values are defined in the getrlimit(3) man page, such
    ///   as `RLIMIT_CORE`.
    ///
    /// The runtime MUST generate an error for any values which cannot be mapped
    /// to a relevant kernel interface. For each entry in `rlimits`, a
    /// `getrlimit(3)` on `type` MUST succeed. For the following properties,
    /// `rlim` refers to the status returned by the `getrlimit(3)` call.
    pub r#type: String,

    /// The value of the limit enforced for the corresponding resource.
    /// `rlim.rlim_cur` MUST match the configured value.
    pub soft: u64,

    /// The ceiling for the soft limit that could be set by an unprivileged
    /// process. `rlim.rlim_max` MUST match the configured value. Only a
    /// privileged process (e.g. one with the `CAP_SYS_RESOURCE` capability) can
    /// raise a hard limit.
    pub hard: u64,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Capabilities {
    /// Effective capabilities that are kept for the process.
    pub effective: Option<Vec<String>>,

    /// Bounding capabilities that are kept for the process.
    pub bounding: Option<Vec<String>>,

    /// Inheritable capabilities that are kept for the process.
    pub inheritable: Option<Vec<String>>,

    /// Permitted capabilities that are kept for the process.
    pub permitted: Option<Vec<String>>,

    /// Ambient capabilities that are kept for the process.
    pub ambient: Option<Vec<String>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct User {
    #[serde(flatten)]
    pub posix: Option<PosixUser>,

    #[serde(flatten)]
    pub windows: Option<WindowsUser>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PosixUser {
    /// specifies the user ID in the container namespace.
    pub uid: u64,

    /// specifies the group ID in the container namespace.
    pub gid: u64,

    /// specifies additional group IDs in the container namespace to be added
    /// to the process.
    #[serde(rename = "additionalGids")]
    pub additional_gids: Option<Vec<u64>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WindowsUser {
    /// specifies the user name for the process.
    pub username: Option<String>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json;

    #[test]
    fn test_runtime_config() {
        let test_data = include_str!("test/config.test.json");

        let config: Config = serde_json::from_str(test_data).expect("Could not deserialize config");

        assert!(config.process.is_some());

        if let Some(ref process) = config.process {
            assert_eq!(process.terminal, Some(true));
            assert_eq!(process.cwd, PathBuf::from("/"));
        }

        assert_eq!(config.hostname.unwrap(), "slartibartfast");

        assert!(config.root.is_some());

        if let Some(ref root) = config.root {
            assert_eq!(root.path, PathBuf::from("rootfs"));
            assert_eq!(root.readonly(), true);
        }
    }
}