1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-06 15:46:17 +02:00

Updated cgroups oci-spec-rs to 0.5.1 or later (#303)

* Updated cgroup oci-spec-rs to 0.5.1

Signed-off-by: Takashi IIGUNI <iiguni.tks@gmail.com>
This commit is contained in:
Takashi IIGUNI 2021-09-27 10:08:56 +09:00 committed by GitHub
parent 6384a592cb
commit 97848f1ce6
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 1241 additions and 1241 deletions

97
Cargo.lock generated
View File

@ -231,6 +231,41 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "darling"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]] [[package]]
name = "dbus" name = "dbus"
version = "0.9.3" version = "0.9.3"
@ -242,6 +277,37 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "derive_builder"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.8.4" version = "0.8.4"
@ -307,6 +373,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.5.0" version = "0.5.0"
@ -446,6 +518,18 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "getset"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
@ -482,6 +566,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.7.0" version = "1.7.0"
@ -667,10 +757,11 @@ dependencies = [
[[package]] [[package]]
name = "oci-spec" name = "oci-spec"
version = "0.4.0" version = "0.5.1"
source = "git+https://github.com/utam0k/oci-spec-rs/?tag=v0.4.0-with-bugfix#73540d3183136d0188b9c3a40f24b08295bbc92e" source = "git+https://github.com/containers/oci-spec-rs?rev=5018f8e5b0355a82c08962cefa5ab07a05b930c6#5018f8e5b0355a82c08962cefa5ab07a05b930c6"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "derive_builder",
"getset",
"quickcheck", "quickcheck",
"serde", "serde",
"serde_json", "serde_json",

View File

@ -39,7 +39,10 @@ mio = { version = "0.7.13", features = ["os-ext", "os-poll"] }
chrono = { version="0.4", features = ["serde"] } chrono = { version="0.4", features = ["serde"] }
once_cell = "1.6.0" once_cell = "1.6.0"
futures = { version = "0.3", features = ["thread-pool"] } futures = { version = "0.3", features = ["thread-pool"] }
oci-spec = { git="https://github.com/utam0k/oci-spec-rs/", tag = "v0.4.0-with-bugfix"} # TODO: Fetch from crate.io instead of git when next release oci-spec-rs
# ref: https://github.com/containers/oci-spec-rs/pull/69
# oci-spec = "0.5.1"
oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "5018f8e5b0355a82c08962cefa5ab07a05b930c6" }
cgroups = { version = "0.1.0", path = "./cgroups" } cgroups = { version = "0.1.0", path = "./cgroups" }
systemd = { version = "0.8", default-features = false, optional = true } systemd = { version = "0.8", default-features = false, optional = true }
dbus = "0.9.2" dbus = "0.9.2"
@ -49,7 +52,10 @@ crossbeam-channel = "0.5"
seccomp = { version = "0.1.0", path = "./seccomp" } seccomp = { version = "0.1.0", path = "./seccomp" }
[dev-dependencies] [dev-dependencies]
oci-spec = { git = "https://github.com/utam0k/oci-spec-rs/", tag = "v0.4.0-with-bugfix", features = ["proptests"] } # TODO: Fetch from crate.io instead of git when next release oci-spec-rs
# ref: https://github.com/containers/oci-spec-rs/pull/69
# oci-spec = "0.5.1"
oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "5018f8e5b0355a82c08962cefa5ab07a05b930c6", features = ["proptests"] }
quickcheck = "1" quickcheck = "1"
serial_test = "0.5.1" serial_test = "0.5.1"

View File

@ -14,7 +14,10 @@ nix = "0.22.0"
procfs = "0.10.1" procfs = "0.10.1"
log = "0.4" log = "0.4"
anyhow = "1.0" anyhow = "1.0"
oci-spec = { git="https://github.com/utam0k/oci-spec-rs/", tag = "v0.4.0-with-bugfix"} # TODO: Fetch from crate.io instead of git when next release oci-spec-rs
# ref: https://github.com/containers/oci-spec-rs/pull/69
# oci-spec = "0.5.1"
oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "5018f8e5b0355a82c08962cefa5ab07a05b930c6" }
systemd = { version = "0.8", default-features = false, optional = true } systemd = { version = "0.8", default-features = false, optional = true }
dbus = "0.9.2" dbus = "0.9.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
@ -24,7 +27,10 @@ errno = { version = "0.2.7", optional = true }
libc = { version = "0.2.84", optional = true } libc = { version = "0.2.84", optional = true }
[dev-dependencies] [dev-dependencies]
oci-spec = { git = "https://github.com/utam0k/oci-spec-rs/", tag = "v0.4.0-with-bugfix", features = ["proptests"] } # TODO: Fetch from crate.io instead of git when next release oci-spec-rs
# ref: https://github.com/containers/oci-spec-rs/pull/69
# oci-spec = "0.5.1"
oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "5018f8e5b0355a82c08962cefa5ab07a05b930c6", features = ["proptests"] }
quickcheck = "1" quickcheck = "1"
clap = "2" clap = "2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View File

@ -10,7 +10,10 @@ use nix::{
sys::statfs::{statfs, CGROUP2_SUPER_MAGIC, TMPFS_MAGIC}, sys::statfs::{statfs, CGROUP2_SUPER_MAGIC, TMPFS_MAGIC},
unistd::Pid, unistd::Pid,
}; };
use oci_spec::runtime::{LinuxDevice, LinuxDeviceCgroup, LinuxDeviceType, LinuxResources}; use oci_spec::runtime::{
LinuxDevice, LinuxDeviceBuilder, LinuxDeviceCgroup, LinuxDeviceCgroupBuilder, LinuxDeviceType,
LinuxResources,
};
#[cfg(feature = "systemd_cgroups")] #[cfg(feature = "systemd_cgroups")]
use systemd::daemon::booted; use systemd::daemon::booted;
#[cfg(not(feature = "systemd_cgroups"))] #[cfg(not(feature = "systemd_cgroups"))]
@ -249,109 +252,104 @@ impl PathBufExt for PathBuf {
pub(crate) fn default_allow_devices() -> Vec<LinuxDeviceCgroup> { pub(crate) fn default_allow_devices() -> Vec<LinuxDeviceCgroup> {
vec![ vec![
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: None, .access("m")
minor: None, .build()
access: "m".to_string().into(), .unwrap(),
}, LinuxDeviceCgroupBuilder::default()
LinuxDeviceCgroup { .allow(true)
allow: true, .typ(LinuxDeviceType::B)
typ: Some(LinuxDeviceType::B), .access("m")
major: None, .build()
minor: None, .unwrap(),
access: "m".to_string().into(),
},
// /dev/console // /dev/console
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(5), .major(5)
minor: Some(1), .minor(1)
access: "rwm".to_string().into(), .access("rwm")
}, .build()
.unwrap(),
// /dev/pts // /dev/pts
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(136), .major(136)
minor: None, .access("rwm")
access: "rwm".to_string().into(), .build()
}, .unwrap(),
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(5), .major(5)
minor: Some(2), .minor(2)
access: "rwm".to_string().into(), .access("rwm")
}, .build()
.unwrap(),
// tun/tap // tun/tap
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(10), .major(10)
minor: Some(200), .minor(200)
access: "rwm".to_string().into(), .access("rwm")
}, .build()
.unwrap(),
] ]
} }
pub(crate) fn default_devices() -> Vec<LinuxDevice> { pub(crate) fn default_devices() -> Vec<LinuxDevice> {
vec![ vec![
LinuxDevice { LinuxDeviceBuilder::default()
path: PathBuf::from("/dev/null"), .path(PathBuf::from("/dev/null"))
typ: LinuxDeviceType::C, .typ(LinuxDeviceType::C)
major: 1, .major(1)
minor: 3, .minor(3)
file_mode: Some(0o066), .file_mode(0o066u32)
uid: None, .build()
gid: None, .unwrap(),
}, LinuxDeviceBuilder::default()
LinuxDevice { .path(PathBuf::from("/dev/zero"))
path: PathBuf::from("/dev/zero"), .typ(LinuxDeviceType::C)
typ: LinuxDeviceType::C, .major(1)
major: 1, .minor(5)
minor: 5, .file_mode(0o066u32)
file_mode: Some(0o066), .build()
uid: None, .unwrap(),
gid: None, LinuxDeviceBuilder::default()
}, .path(PathBuf::from("/dev/full"))
LinuxDevice { .typ(LinuxDeviceType::C)
path: PathBuf::from("/dev/full"), .major(1)
typ: LinuxDeviceType::C, .minor(7)
major: 1, .file_mode(0o066u32)
minor: 7, .build()
file_mode: Some(0o066), .unwrap(),
uid: None, LinuxDeviceBuilder::default()
gid: None, .path(PathBuf::from("/dev/tty"))
}, .typ(LinuxDeviceType::C)
LinuxDevice { .major(5)
path: PathBuf::from("/dev/tty"), .minor(0)
typ: LinuxDeviceType::C, .file_mode(0o066u32)
major: 5, .build()
minor: 0, .unwrap(),
file_mode: Some(0o066), LinuxDeviceBuilder::default()
uid: None, .path(PathBuf::from("/dev/urandom"))
gid: None, .typ(LinuxDeviceType::C)
}, .major(1)
LinuxDevice { .minor(9)
path: PathBuf::from("/dev/urandom"), .file_mode(0o066u32)
typ: LinuxDeviceType::C, .build()
major: 1, .unwrap(),
minor: 9, LinuxDeviceBuilder::default()
file_mode: Some(0o066), .path(PathBuf::from("/dev/random"))
uid: None, .typ(LinuxDeviceType::C)
gid: None, .major(1)
}, .minor(8)
LinuxDevice { .file_mode(0o066u32)
path: PathBuf::from("/dev/random"), .build()
typ: LinuxDeviceType::C, .unwrap(),
major: 1,
minor: 8,
file_mode: Some(0o066),
uid: None,
gid: None,
},
] ]
} }

View File

@ -8,8 +8,6 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use oci_spec::runtime::LinuxCpu;
pub struct TempDir { pub struct TempDir {
path: Option<PathBuf>, path: Option<PathBuf>,
} }
@ -82,62 +80,3 @@ pub fn set_fixture(temp_dir: &Path, filename: &str, val: &str) -> Result<PathBuf
Ok(full_path) Ok(full_path)
} }
pub struct LinuxCpuBuilder {
resource: LinuxCpu,
}
impl LinuxCpuBuilder {
pub fn new() -> Self {
Self {
resource: LinuxCpu {
shares: None,
quota: None,
period: None,
realtime_runtime: None,
realtime_period: None,
cpus: None,
mems: None,
},
}
}
pub fn with_shares(mut self, shares: u64) -> Self {
self.resource.shares = Some(shares);
self
}
pub fn with_quota(mut self, quota: i64) -> Self {
self.resource.quota = Some(quota);
self
}
pub fn with_period(mut self, period: u64) -> Self {
self.resource.period = Some(period);
self
}
pub fn with_realtime_runtime(mut self, runtime: i64) -> Self {
self.resource.realtime_runtime = Some(runtime);
self
}
pub fn with_realtime_period(mut self, period: u64) -> Self {
self.resource.realtime_period = Some(period);
self
}
pub fn with_cpus(mut self, cpus: String) -> Self {
self.resource.cpus = Some(cpus);
self
}
pub fn with_mems(mut self, mems: String) -> Self {
self.resource.mems = Some(mems);
self
}
pub fn build(self) -> LinuxCpu {
self.resource
}
}

View File

@ -85,7 +85,7 @@ impl Controller for Blkio {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(blkio) = &controller_opt.resources.block_io { if let Some(blkio) = &controller_opt.resources.block_io() {
return Some(blkio); return Some(blkio);
} }
@ -107,38 +107,38 @@ impl StatsProvider for Blkio {
impl Blkio { impl Blkio {
fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> Result<()> { fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> Result<()> {
if let Some(throttle_read_bps_device) = blkio.throttle_read_bps_device.as_ref() { if let Some(throttle_read_bps_device) = blkio.throttle_read_bps_device().as_ref() {
for trbd in throttle_read_bps_device { for trbd in throttle_read_bps_device {
common::write_cgroup_file_str( common::write_cgroup_file_str(
&root_path.join(BLKIO_THROTTLE_READ_BPS), &root_path.join(BLKIO_THROTTLE_READ_BPS),
&format!("{}:{} {}", trbd.major, trbd.minor, trbd.rate), &format!("{}:{} {}", trbd.major(), trbd.minor(), trbd.rate()),
)?; )?;
} }
} }
if let Some(throttle_write_bps_device) = blkio.throttle_write_bps_device.as_ref() { if let Some(throttle_write_bps_device) = blkio.throttle_write_bps_device().as_ref() {
for twbd in throttle_write_bps_device { for twbd in throttle_write_bps_device {
common::write_cgroup_file_str( common::write_cgroup_file_str(
&root_path.join(BLKIO_THROTTLE_WRITE_BPS), &root_path.join(BLKIO_THROTTLE_WRITE_BPS),
&format!("{}:{} {}", twbd.major, twbd.minor, twbd.rate), &format!("{}:{} {}", twbd.major(), twbd.minor(), twbd.rate()),
)?; )?;
} }
} }
if let Some(throttle_read_iops_device) = blkio.throttle_read_iops_device.as_ref() { if let Some(throttle_read_iops_device) = blkio.throttle_read_iops_device().as_ref() {
for trid in throttle_read_iops_device { for trid in throttle_read_iops_device {
common::write_cgroup_file_str( common::write_cgroup_file_str(
&root_path.join(BLKIO_THROTTLE_READ_IOPS), &root_path.join(BLKIO_THROTTLE_READ_IOPS),
&format!("{}:{} {}", trid.major, trid.minor, trid.rate), &format!("{}:{} {}", trid.major(), trid.minor(), trid.rate()),
)?; )?;
} }
} }
if let Some(throttle_write_iops_device) = blkio.throttle_write_iops_device.as_ref() { if let Some(throttle_write_iops_device) = blkio.throttle_write_iops_device().as_ref() {
for twid in throttle_write_iops_device { for twid in throttle_write_iops_device {
common::write_cgroup_file_str( common::write_cgroup_file_str(
&root_path.join(BLKIO_THROTTLE_WRITE_IOPS), &root_path.join(BLKIO_THROTTLE_WRITE_IOPS),
&format!("{}:{} {}", twid.major, twid.minor, twid.rate), &format!("{}:{} {}", twid.major(), twid.minor(), twid.rate()),
)?; )?;
} }
} }
@ -228,63 +228,21 @@ mod tests {
use crate::test::{create_temp_dir, set_fixture, setup}; use crate::test::{create_temp_dir, set_fixture, setup};
use anyhow::Result; use anyhow::Result;
use oci_spec::runtime::{LinuxBlockIo, LinuxThrottleDevice}; use oci_spec::runtime::{LinuxBlockIoBuilder, LinuxThrottleDeviceBuilder};
struct BlockIoBuilder {
block_io: LinuxBlockIo,
}
impl BlockIoBuilder {
fn new() -> Self {
let block_io = LinuxBlockIo {
weight: Some(0),
leaf_weight: Some(0),
weight_device: vec![].into(),
throttle_read_bps_device: vec![].into(),
throttle_write_bps_device: vec![].into(),
throttle_read_iops_device: vec![].into(),
throttle_write_iops_device: vec![].into(),
};
Self { block_io }
}
fn with_read_bps(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_read_bps_device = throttle.into();
self
}
fn with_write_bps(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_write_bps_device = throttle.into();
self
}
fn with_read_iops(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_read_iops_device = throttle.into();
self
}
fn with_write_iops(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_write_iops_device = throttle.into();
self
}
fn build(self) -> LinuxBlockIo {
self.block_io
}
}
#[test] #[test]
fn test_set_blkio_read_bps() { fn test_set_blkio_read_bps() {
let (tmp, throttle) = setup("test_set_blkio_read_bps", BLKIO_THROTTLE_READ_BPS); let (tmp, throttle) = setup("test_set_blkio_read_bps", BLKIO_THROTTLE_READ_BPS);
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_read_bps(vec![LinuxThrottleDevice { .throttle_read_bps_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Blkio::apply(&tmp, &blkio).expect("apply blkio"); Blkio::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle) let content = fs::read_to_string(throttle)
@ -297,13 +255,15 @@ mod tests {
fn test_set_blkio_write_bps() { fn test_set_blkio_write_bps() {
let (tmp, throttle) = setup("test_set_blkio_write_bps", BLKIO_THROTTLE_WRITE_BPS); let (tmp, throttle) = setup("test_set_blkio_write_bps", BLKIO_THROTTLE_WRITE_BPS);
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_write_bps(vec![LinuxThrottleDevice { .throttle_write_bps_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Blkio::apply(&tmp, &blkio).expect("apply blkio"); Blkio::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle) let content = fs::read_to_string(throttle)
@ -316,13 +276,15 @@ mod tests {
fn test_set_blkio_read_iops() { fn test_set_blkio_read_iops() {
let (tmp, throttle) = setup("test_set_blkio_read_iops", BLKIO_THROTTLE_READ_IOPS); let (tmp, throttle) = setup("test_set_blkio_read_iops", BLKIO_THROTTLE_READ_IOPS);
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_read_iops(vec![LinuxThrottleDevice { .throttle_read_iops_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Blkio::apply(&tmp, &blkio).expect("apply blkio"); Blkio::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle) let content = fs::read_to_string(throttle)
@ -335,13 +297,15 @@ mod tests {
fn test_set_blkio_write_iops() { fn test_set_blkio_write_iops() {
let (tmp, throttle) = setup("test_set_blkio_write_iops", BLKIO_THROTTLE_WRITE_IOPS); let (tmp, throttle) = setup("test_set_blkio_write_iops", BLKIO_THROTTLE_WRITE_IOPS);
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_write_iops(vec![LinuxThrottleDevice { .throttle_write_iops_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Blkio::apply(&tmp, &blkio).expect("apply blkio"); Blkio::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle) let content = fs::read_to_string(throttle)

View File

@ -33,12 +33,12 @@ impl Controller for Cpu {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(cpu) = &controller_opt.resources.cpu { if let Some(cpu) = &controller_opt.resources.cpu() {
if cpu.shares.is_some() if cpu.shares().is_some()
|| cpu.period.is_some() || cpu.period().is_some()
|| cpu.quota.is_some() || cpu.quota().is_some()
|| cpu.realtime_period.is_some() || cpu.realtime_period().is_some()
|| cpu.realtime_runtime.is_some() || cpu.realtime_runtime().is_some()
{ {
return Some(cpu); return Some(cpu);
} }
@ -95,31 +95,31 @@ impl StatsProvider for Cpu {
impl Cpu { impl Cpu {
fn apply(root_path: &Path, cpu: &LinuxCpu) -> Result<()> { fn apply(root_path: &Path, cpu: &LinuxCpu) -> Result<()> {
if let Some(cpu_shares) = cpu.shares { if let Some(cpu_shares) = cpu.shares() {
if cpu_shares != 0 { if cpu_shares != 0 {
common::write_cgroup_file(root_path.join(CGROUP_CPU_SHARES), cpu_shares)?; common::write_cgroup_file(root_path.join(CGROUP_CPU_SHARES), cpu_shares)?;
} }
} }
if let Some(cpu_period) = cpu.period { if let Some(cpu_period) = cpu.period() {
if cpu_period != 0 { if cpu_period != 0 {
common::write_cgroup_file(root_path.join(CGROUP_CPU_PERIOD), cpu_period)?; common::write_cgroup_file(root_path.join(CGROUP_CPU_PERIOD), cpu_period)?;
} }
} }
if let Some(cpu_quota) = cpu.quota { if let Some(cpu_quota) = cpu.quota() {
if cpu_quota != 0 { if cpu_quota != 0 {
common::write_cgroup_file(root_path.join(CGROUP_CPU_QUOTA), cpu_quota)?; common::write_cgroup_file(root_path.join(CGROUP_CPU_QUOTA), cpu_quota)?;
} }
} }
if let Some(rt_runtime) = cpu.realtime_runtime { if let Some(rt_runtime) = cpu.realtime_runtime() {
if rt_runtime != 0 { if rt_runtime != 0 {
common::write_cgroup_file(root_path.join(CGROUP_CPU_RT_RUNTIME), rt_runtime)?; common::write_cgroup_file(root_path.join(CGROUP_CPU_RT_RUNTIME), rt_runtime)?;
} }
} }
if let Some(rt_period) = cpu.realtime_period { if let Some(rt_period) = cpu.realtime_period() {
if rt_period != 0 { if rt_period != 0 {
common::write_cgroup_file(root_path.join(CGROUP_CPU_RT_PERIOD), rt_period)?; common::write_cgroup_file(root_path.join(CGROUP_CPU_RT_PERIOD), rt_period)?;
} }
@ -132,7 +132,8 @@ impl Cpu {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture, setup, LinuxCpuBuilder}; use crate::test::{create_temp_dir, set_fixture, setup};
use oci_spec::runtime::LinuxCpuBuilder;
use std::fs; use std::fs;
#[test] #[test]
@ -141,7 +142,7 @@ mod tests {
let (tmp, shares) = setup("test_set_shares", CGROUP_CPU_SHARES); let (tmp, shares) = setup("test_set_shares", CGROUP_CPU_SHARES);
let _ = set_fixture(&tmp, CGROUP_CPU_SHARES, "") let _ = set_fixture(&tmp, CGROUP_CPU_SHARES, "")
.unwrap_or_else(|_| panic!("set test fixture for {}", CGROUP_CPU_SHARES)); .unwrap_or_else(|_| panic!("set test fixture for {}", CGROUP_CPU_SHARES));
let cpu = LinuxCpuBuilder::new().with_shares(2048).build(); let cpu = LinuxCpuBuilder::default().shares(2048u64).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -157,7 +158,7 @@ mod tests {
// arrange // arrange
const QUOTA: i64 = 200000; const QUOTA: i64 = 200000;
let (tmp, max) = setup("test_set_quota", CGROUP_CPU_QUOTA); let (tmp, max) = setup("test_set_quota", CGROUP_CPU_QUOTA);
let cpu = LinuxCpuBuilder::new().with_quota(QUOTA).build(); let cpu = LinuxCpuBuilder::default().quota(QUOTA).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -173,7 +174,7 @@ mod tests {
// arrange // arrange
const PERIOD: u64 = 100000; const PERIOD: u64 = 100000;
let (tmp, max) = setup("test_set_period", CGROUP_CPU_PERIOD); let (tmp, max) = setup("test_set_period", CGROUP_CPU_PERIOD);
let cpu = LinuxCpuBuilder::new().with_period(PERIOD).build(); let cpu = LinuxCpuBuilder::default().period(PERIOD).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -189,9 +190,10 @@ mod tests {
// arrange // arrange
const RUNTIME: i64 = 100000; const RUNTIME: i64 = 100000;
let (tmp, max) = setup("test_set_rt_runtime", CGROUP_CPU_RT_RUNTIME); let (tmp, max) = setup("test_set_rt_runtime", CGROUP_CPU_RT_RUNTIME);
let cpu = LinuxCpuBuilder::new() let cpu = LinuxCpuBuilder::default()
.with_realtime_runtime(RUNTIME) .realtime_runtime(RUNTIME)
.build(); .build()
.unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -207,7 +209,10 @@ mod tests {
// arrange // arrange
const PERIOD: u64 = 100000; const PERIOD: u64 = 100000;
let (tmp, max) = setup("test_set_rt_period", CGROUP_CPU_RT_PERIOD); let (tmp, max) = setup("test_set_rt_period", CGROUP_CPU_RT_PERIOD);
let cpu = LinuxCpuBuilder::new().with_realtime_period(PERIOD).build(); let cpu = LinuxCpuBuilder::default()
.realtime_period(PERIOD)
.build()
.unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");

View File

@ -39,8 +39,8 @@ impl Controller for CpuSet {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(cpuset) = &controller_opt.resources.cpu { if let Some(cpuset) = &controller_opt.resources.cpu() {
if cpuset.cpus.is_some() || cpuset.mems.is_some() { if cpuset.cpus().is_some() || cpuset.mems().is_some() {
return Some(cpuset); return Some(cpuset);
} }
} }
@ -51,11 +51,11 @@ impl Controller for CpuSet {
impl CpuSet { impl CpuSet {
fn apply(cgroup_path: &Path, cpuset: &LinuxCpu) -> Result<()> { fn apply(cgroup_path: &Path, cpuset: &LinuxCpu) -> Result<()> {
if let Some(cpus) = &cpuset.cpus { if let Some(cpus) = &cpuset.cpus() {
common::write_cgroup_file_str(cgroup_path.join(CGROUP_CPUSET_CPUS), cpus)?; common::write_cgroup_file_str(cgroup_path.join(CGROUP_CPUSET_CPUS), cpus)?;
} }
if let Some(mems) = &cpuset.mems { if let Some(mems) = &cpuset.mems() {
common::write_cgroup_file_str(cgroup_path.join(CGROUP_CPUSET_MEMS), mems)?; common::write_cgroup_file_str(cgroup_path.join(CGROUP_CPUSET_MEMS), mems)?;
} }
@ -93,13 +93,17 @@ mod tests {
use std::fs; use std::fs;
use super::*; use super::*;
use crate::test::{setup, LinuxCpuBuilder}; use crate::test::setup;
use oci_spec::runtime::LinuxCpuBuilder;
#[test] #[test]
fn test_set_cpus() { fn test_set_cpus() {
// arrange // arrange
let (tmp, cpus) = setup("test_set_cpus", CGROUP_CPUSET_CPUS); let (tmp, cpus) = setup("test_set_cpus", CGROUP_CPUSET_CPUS);
let cpuset = LinuxCpuBuilder::new().with_cpus("1-3".to_owned()).build(); let cpuset = LinuxCpuBuilder::default()
.cpus("1-3".to_owned())
.build()
.unwrap();
// act // act
CpuSet::apply(&tmp, &cpuset).expect("apply cpuset"); CpuSet::apply(&tmp, &cpuset).expect("apply cpuset");
@ -114,7 +118,10 @@ mod tests {
fn test_set_mems() { fn test_set_mems() {
// arrange // arrange
let (tmp, mems) = setup("test_set_mems", CGROUP_CPUSET_MEMS); let (tmp, mems) = setup("test_set_mems", CGROUP_CPUSET_MEMS);
let cpuset = LinuxCpuBuilder::new().with_mems("1-3".to_owned()).build(); let cpuset = LinuxCpuBuilder::default()
.mems("1-3".to_owned())
.build()
.unwrap();
// act // act
CpuSet::apply(&tmp, &cpuset).expect("apply cpuset"); CpuSet::apply(&tmp, &cpuset).expect("apply cpuset");

View File

@ -14,7 +14,7 @@ impl Controller for Devices {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Devices cgroup config"); log::debug!("Apply Devices cgroup config");
if let Some(devices) = controller_opt.resources.devices.as_ref() { if let Some(devices) = controller_opt.resources.devices().as_ref() {
for d in devices { for d in devices {
Self::apply_device(d, cgroup_root)?; Self::apply_device(d, cgroup_root)?;
} }
@ -40,7 +40,7 @@ impl Controller for Devices {
impl Devices { impl Devices {
fn apply_device(device: &LinuxDeviceCgroup, cgroup_root: &Path) -> Result<()> { fn apply_device(device: &LinuxDeviceCgroup, cgroup_root: &Path) -> Result<()> {
let path = if device.allow { let path = if device.allow() {
cgroup_root.join("devices.allow") cgroup_root.join("devices.allow")
} else { } else {
cgroup_root.join("devices.deny") cgroup_root.join("devices.deny")
@ -56,7 +56,7 @@ mod tests {
use super::*; use super::*;
use crate::test::create_temp_dir; use crate::test::create_temp_dir;
use crate::test::set_fixture; use crate::test::set_fixture;
use oci_spec::runtime::{LinuxDeviceCgroup, LinuxDeviceType}; use oci_spec::runtime::{LinuxDeviceCgroupBuilder, LinuxDeviceType};
use std::fs::read_to_string; use std::fs::read_to_string;
#[test] #[test]
@ -74,7 +74,7 @@ mod tests {
Devices::apply_device(d, &tmp).expect("Apply default device"); Devices::apply_device(d, &tmp).expect("Apply default device");
println!("Device: {}", d.to_string()); println!("Device: {}", d.to_string());
if d.allow { if d.allow() {
let allowed_content = let allowed_content =
read_to_string(tmp.join("devices.allow")).expect("read to string"); read_to_string(tmp.join("devices.allow")).expect("read to string");
assert_eq!(allowed_content, d.to_string()); assert_eq!(allowed_content, d.to_string());
@ -90,34 +90,34 @@ mod tests {
fn test_set_mock_devices() { fn test_set_mock_devices() {
let tmp = create_temp_dir("test_set_mock_devices").expect("create temp directory for test"); let tmp = create_temp_dir("test_set_mock_devices").expect("create temp directory for test");
[ [
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(10), .major(10)
minor: None, .access("rwm")
access: "rwm".to_string().into(), .build()
}, .unwrap(),
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::A), .typ(LinuxDeviceType::A)
major: None, .minor(200)
minor: Some(200), .access("rwm")
access: "rwm".to_string().into(), .build()
}, .unwrap(),
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: false, .allow(false)
typ: Some(LinuxDeviceType::P), .typ(LinuxDeviceType::P)
major: Some(10), .major(10)
minor: Some(200), .minor(200)
access: "m".to_string().into(), .access("m")
}, .build()
LinuxDeviceCgroup { .unwrap(),
allow: false, LinuxDeviceCgroupBuilder::default()
typ: Some(LinuxDeviceType::U), .allow(false)
major: None, .typ(LinuxDeviceType::U)
minor: None, .access("rw")
access: "rw".to_string().into(), .build()
}, .unwrap(),
] ]
.iter() .iter()
.for_each(|d| { .for_each(|d| {
@ -126,7 +126,7 @@ mod tests {
Devices::apply_device(d, &tmp).expect("Apply default device"); Devices::apply_device(d, &tmp).expect("Apply default device");
println!("Device: {}", d.to_string()); println!("Device: {}", d.to_string());
if d.allow { if d.allow() {
let allowed_content = let allowed_content =
read_to_string(tmp.join("devices.allow")).expect("read to string"); read_to_string(tmp.join("devices.allow")).expect("read to string");
assert_eq!(allowed_content, d.to_string()); assert_eq!(allowed_content, d.to_string());
@ -144,7 +144,7 @@ mod tests {
set_fixture(&tmp, "devices.allow", "").expect("create allowed devices list"); set_fixture(&tmp, "devices.allow", "").expect("create allowed devices list");
set_fixture(&tmp, "devices.deny", "").expect("create denied devices list"); set_fixture(&tmp, "devices.deny", "").expect("create denied devices list");
Devices::apply_device(&device, &tmp).expect("Apply default device"); Devices::apply_device(&device, &tmp).expect("Apply default device");
if device.allow { if device.allow() {
let allowed_content = let allowed_content =
read_to_string(tmp.join("devices.allow")).expect("read to string"); read_to_string(tmp.join("devices.allow")).expect("read to string");
allowed_content == device.to_string() allowed_content == device.to_string()
@ -162,7 +162,7 @@ mod tests {
set_fixture(&tmp, "devices.allow", "").expect("create allowed devices list"); set_fixture(&tmp, "devices.allow", "").expect("create allowed devices list");
set_fixture(&tmp, "devices.deny", "").expect("create denied devices list"); set_fixture(&tmp, "devices.deny", "").expect("create denied devices list");
Devices::apply_device(device, &tmp).expect("Apply default device"); Devices::apply_device(device, &tmp).expect("Apply default device");
if device.allow { if device.allow() {
let allowed_content = let allowed_content =
read_to_string(tmp.join("devices.allow")).expect("read to string"); read_to_string(tmp.join("devices.allow")).expect("read to string");
allowed_content == device.to_string() allowed_content == device.to_string()

View File

@ -127,7 +127,7 @@ mod tests {
use crate::common::{FreezerState, CGROUP_PROCS}; use crate::common::{FreezerState, CGROUP_PROCS};
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use nix::unistd::Pid; use nix::unistd::Pid;
use oci_spec::runtime::LinuxResources; use oci_spec::runtime::LinuxResourcesBuilder;
#[test] #[test]
fn test_set_freezer_state() { fn test_set_freezer_state() {
@ -176,17 +176,11 @@ mod tests {
// set Thawed state. // set Thawed state.
{ {
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default()
devices: Some(vec![]), .devices(vec![])
memory: None, .hugepage_limits(vec![])
cpu: None, .build()
pids: None, .unwrap();
block_io: None,
hugepage_limits: Some(vec![]),
network: None,
rdma: None,
unified: None,
};
let state = FreezerState::Thawed; let state = FreezerState::Thawed;
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
@ -209,18 +203,11 @@ mod tests {
// set Frozen state. // set Frozen state.
{ {
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default()
devices: Some(vec![]), .devices(vec![])
memory: None, .hugepage_limits(vec![])
cpu: None, .build()
pids: None, .unwrap();
block_io: None,
hugepage_limits: Some(vec![]),
network: None,
rdma: None,
unified: None,
};
let state = FreezerState::Frozen; let state = FreezerState::Frozen;
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
@ -243,17 +230,11 @@ mod tests {
// set Undefined state. // set Undefined state.
{ {
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default()
devices: Some(vec![]), .devices(vec![])
memory: None, .hugepage_limits(vec![])
cpu: None, .build()
pids: None, .unwrap();
block_io: None,
hugepage_limits: Some(vec![]),
network: None,
rdma: None,
unified: None,
};
let state = FreezerState::Undefined; let state = FreezerState::Undefined;

View File

@ -29,9 +29,9 @@ impl Controller for HugeTlb {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits.as_ref() { if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits().as_ref() {
if !hugepage_limits.is_empty() { if !hugepage_limits.is_empty() {
return controller_opt.resources.hugepage_limits.as_ref(); return controller_opt.resources.hugepage_limits().as_ref();
} }
} }
@ -58,7 +58,7 @@ impl StatsProvider for HugeTlb {
impl HugeTlb { impl HugeTlb {
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> { fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> {
let page_size: String = hugetlb let page_size: String = hugetlb
.page_size .page_size()
.chars() .chars()
.take_while(|c| c.is_digit(10)) .take_while(|c| c.is_digit(10))
.collect(); .collect();
@ -68,8 +68,8 @@ impl HugeTlb {
} }
common::write_cgroup_file( common::write_cgroup_file(
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)), root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size())),
hugetlb.limit, hugetlb.limit(),
)?; )?;
Ok(()) Ok(())
} }
@ -101,7 +101,7 @@ impl HugeTlb {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxHugepageLimit; use oci_spec::runtime::LinuxHugepageLimitBuilder;
use std::fs::read_to_string; use std::fs::read_to_string;
#[test] #[test]
@ -110,13 +110,15 @@ mod tests {
let tmp = create_temp_dir("test_set_hugetlb").expect("create temp directory for test"); let tmp = create_temp_dir("test_set_hugetlb").expect("create temp directory for test");
set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size"); set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size");
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimitBuilder::default()
page_size: "2MB".to_owned(), .page_size("2MB")
limit: 16384, .limit(16384)
}; .build()
.unwrap();
HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb"); HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb");
let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content"); let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
assert_eq!(hugetlb.limit.to_string(), content); assert_eq!(hugetlb.limit().to_string(), content);
} }
#[test] #[test]
@ -124,10 +126,11 @@ mod tests {
let tmp = create_temp_dir("test_set_hugetlb_with_invalid_page_size") let tmp = create_temp_dir("test_set_hugetlb_with_invalid_page_size")
.expect("create temp directory for test"); .expect("create temp directory for test");
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimitBuilder::default()
page_size: "3MB".to_owned(), .page_size("3MB")
limit: 16384, .limit(16384)
}; .build()
.unwrap();
let result = HugeTlb::apply(&tmp, &hugetlb); let result = HugeTlb::apply(&tmp, &hugetlb);
assert!( assert!(
@ -138,14 +141,14 @@ mod tests {
quickcheck! { quickcheck! {
fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool { fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool {
let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size); let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size());
let tmp = create_temp_dir("property_test_set_hugetlb").expect("create temp directory for test"); let tmp = create_temp_dir("property_test_set_hugetlb").expect("create temp directory for test");
set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size"); set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size");
let result = HugeTlb::apply(&tmp, &hugetlb); let result = HugeTlb::apply(&tmp, &hugetlb);
let page_size: String = hugetlb let page_size: String = hugetlb
.page_size .page_size()
.chars() .chars()
.take_while(|c| c.is_digit(10)) .take_while(|c| c.is_digit(10))
.collect(); .collect();
@ -154,7 +157,7 @@ mod tests {
if HugeTlb::is_power_of_two(page_size) && page_size != 1 { if HugeTlb::is_power_of_two(page_size) && page_size != 1 {
let content = let content =
read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content"); read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
hugetlb.limit.to_string() == content hugetlb.limit().to_string() == content
} else { } else {
result.is_err() result.is_err()
} }

View File

@ -72,8 +72,8 @@ impl Manager {
CtrlType::CpuSet => CpuSet::needs_to_handle(controller_opt).is_some(), CtrlType::CpuSet => CpuSet::needs_to_handle(controller_opt).is_some(),
CtrlType::Devices => Devices::needs_to_handle(controller_opt).is_some(), CtrlType::Devices => Devices::needs_to_handle(controller_opt).is_some(),
CtrlType::HugeTlb => HugeTlb::needs_to_handle(controller_opt).is_some(), CtrlType::HugeTlb => HugeTlb::needs_to_handle(controller_opt).is_some(),
CtrlType::Memory => Memory::needs_to_handle(controller_opt).is_some(), CtrlType::Memory => controller_opt.resources.memory().is_some(), // TODO: Fix Memory::need_to_handle
CtrlType::Pids => Pids::needs_to_handle(controller_opt).is_some(), CtrlType::Pids => controller_opt.resources.pids().is_some(), // TODO: Fix Pids::need_to_handle
CtrlType::PerfEvent => PerfEvent::needs_to_handle(controller_opt).is_some(), CtrlType::PerfEvent => PerfEvent::needs_to_handle(controller_opt).is_some(),
CtrlType::Blkio => Blkio::needs_to_handle(controller_opt).is_some(), CtrlType::Blkio => Blkio::needs_to_handle(controller_opt).is_some(),
CtrlType::NetworkPriority => { CtrlType::NetworkPriority => {

View File

@ -51,8 +51,8 @@ impl Controller for Memory {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply Memory cgroup config"); log::debug!("Apply Memory cgroup config");
if let Some(memory) = Self::needs_to_handle(controller_opt) { if let Some(memory) = controller_opt.resources.memory().as_ref() {
let reservation = memory.reservation.unwrap_or(0); let reservation = memory.reservation().unwrap_or(0);
Self::apply(memory, cgroup_root)?; Self::apply(memory, cgroup_root)?;
@ -69,7 +69,7 @@ impl Controller for Memory {
common::write_cgroup_file(cgroup_root.join(CGROUP_MEMORY_OOM_CONTROL), 1)?; common::write_cgroup_file(cgroup_root.join(CGROUP_MEMORY_OOM_CONTROL), 1)?;
} }
if let Some(swappiness) = memory.swappiness { if let Some(swappiness) = memory.swappiness() {
if swappiness <= 100 { if swappiness <= 100 {
common::write_cgroup_file( common::write_cgroup_file(
cgroup_root.join(CGROUP_MEMORY_SWAPPINESS), cgroup_root.join(CGROUP_MEMORY_SWAPPINESS),
@ -87,10 +87,10 @@ impl Controller for Memory {
// NOTE: Seems as though kernel and kernelTCP are both deprecated // NOTE: Seems as though kernel and kernelTCP are both deprecated
// neither are implemented by runc. Tests pass without this, but // neither are implemented by runc. Tests pass without this, but
// kept in per the spec. // kept in per the spec.
if let Some(kmem) = memory.kernel { if let Some(kmem) = memory.kernel() {
common::write_cgroup_file(cgroup_root.join(CGROUP_KERNEL_MEMORY_LIMIT), kmem)?; common::write_cgroup_file(cgroup_root.join(CGROUP_KERNEL_MEMORY_LIMIT), kmem)?;
} }
if let Some(tcp_mem) = memory.kernel_tcp { if let Some(tcp_mem) = memory.kernel_tcp() {
common::write_cgroup_file( common::write_cgroup_file(
cgroup_root.join(CGROUP_KERNEL_TCP_MEMORY_LIMIT), cgroup_root.join(CGROUP_KERNEL_TCP_MEMORY_LIMIT),
tcp_mem, tcp_mem,
@ -101,11 +101,12 @@ impl Controller for Memory {
Ok(()) Ok(())
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(_controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(memory) = &controller_opt.resources.memory { // TODO: fix compile error
return Some(memory); // error[E0515]: cannot return value referencing temporary value
} // if let Some(memory) = &controller_opt.resources.memory() {
// return Some(memory);
// }
None None
} }
} }
@ -292,10 +293,10 @@ impl Memory {
} }
fn apply(resource: &LinuxMemory, cgroup_root: &Path) -> Result<()> { fn apply(resource: &LinuxMemory, cgroup_root: &Path) -> Result<()> {
match resource.limit { match resource.limit() {
Some(limit) => { Some(limit) => {
let current_limit = Self::get_memory_limit(cgroup_root)?; let current_limit = Self::get_memory_limit(cgroup_root)?;
match resource.swap { match resource.swap() {
Some(swap) => { Some(swap) => {
let is_updated = swap == -1 || current_limit < swap; let is_updated = swap == -1 || current_limit < swap;
Self::set_memory_and_swap(limit, swap, is_updated, cgroup_root)?; Self::set_memory_and_swap(limit, swap, is_updated, cgroup_root)?;
@ -310,7 +311,7 @@ impl Memory {
} }
} }
} }
None => match resource.swap { None => match resource.swap() {
Some(swap) => Self::set_memory_and_swap(0, swap, false, cgroup_root)?, Some(swap) => Self::set_memory_and_swap(0, swap, false, cgroup_root)?,
None => Self::set_memory_and_swap(0, 0, false, cgroup_root)?, None => Self::set_memory_and_swap(0, 0, false, cgroup_root)?,
}, },
@ -324,7 +325,7 @@ mod tests {
use super::*; use super::*;
use crate::common::CGROUP_PROCS; use crate::common::CGROUP_PROCS;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::{LinuxMemory, LinuxResources}; use oci_spec::runtime::{LinuxMemoryBuilder, LinuxResourcesBuilder};
#[test] #[test]
fn test_set_memory() { fn test_set_memory() {
@ -375,17 +376,8 @@ mod tests {
// test unlimited memory with no set swap // test unlimited memory with no set swap
{ {
let limit = -1; let limit = -1;
let linux_memory = &LinuxMemory { let linux_memory = LinuxMemoryBuilder::default().limit(limit).build().unwrap();
limit: Some(limit), Memory::apply(&linux_memory, &tmp).expect("Set memory and swap");
swap: None, // Some(0) gives the same outcome
reservation: None,
kernel: None,
kernel_tcp: None,
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
Memory::apply(linux_memory, &tmp).expect("Set memory and swap");
let limit_content = let limit_content =
std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("Read to string"); std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("Read to string");
@ -401,17 +393,12 @@ mod tests {
{ {
let limit = 1024 * 1024 * 1024; let limit = 1024 * 1024 * 1024;
let swap = 1024; let swap = 1024;
let linux_memory = &LinuxMemory { let linux_memory = LinuxMemoryBuilder::default()
limit: Some(limit), .limit(limit)
swap: Some(swap), .swap(swap)
reservation: None, .build()
kernel: None, .unwrap();
kernel_tcp: None, Memory::apply(&linux_memory, &tmp).expect("Set memory and swap");
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
Memory::apply(linux_memory, &tmp).expect("Set memory and swap");
let limit_content = let limit_content =
std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("Read to string"); std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("Read to string");
@ -424,140 +411,130 @@ mod tests {
} }
quickcheck! { quickcheck! {
fn property_test_set_memory(linux_memory: LinuxMemory, disable_oom_killer: bool) -> bool { fn property_test_set_memory(linux_memory: LinuxMemory, disable_oom_killer: bool) -> bool {
let tmp = let tmp =
create_temp_dir("property_test_set_memory").expect("create temp directory for test"); create_temp_dir("property_test_set_memory").expect("create temp directory for test");
set_fixture(&tmp, CGROUP_MEMORY_USAGE, "0").expect("Set fixure for memory usage"); set_fixture(&tmp, CGROUP_MEMORY_USAGE, "0").expect("Set fixure for memory usage");
set_fixture(&tmp, CGROUP_MEMORY_MAX_USAGE, "0").expect("Set fixure for max memory usage"); set_fixture(&tmp, CGROUP_MEMORY_MAX_USAGE, "0").expect("Set fixure for max memory usage");
set_fixture(&tmp, CGROUP_MEMORY_LIMIT, "0").expect("Set fixure for memory limit"); set_fixture(&tmp, CGROUP_MEMORY_LIMIT, "0").expect("Set fixure for memory limit");
set_fixture(&tmp, CGROUP_MEMORY_SWAP_LIMIT, "0").expect("Set fixure for swap limit"); set_fixture(&tmp, CGROUP_MEMORY_SWAP_LIMIT, "0").expect("Set fixure for swap limit");
set_fixture(&tmp, CGROUP_MEMORY_SWAPPINESS, "0").expect("Set fixure for swappiness"); set_fixture(&tmp, CGROUP_MEMORY_SWAPPINESS, "0").expect("Set fixure for swappiness");
set_fixture(&tmp, CGROUP_MEMORY_RESERVATION, "0").expect("Set fixture for memory reservation"); set_fixture(&tmp, CGROUP_MEMORY_RESERVATION, "0").expect("Set fixture for memory reservation");
set_fixture(&tmp, CGROUP_MEMORY_OOM_CONTROL, "0").expect("Set fixture for oom control"); set_fixture(&tmp, CGROUP_MEMORY_OOM_CONTROL, "0").expect("Set fixture for oom control");
set_fixture(&tmp, CGROUP_KERNEL_MEMORY_LIMIT, "0").expect("Set fixture for kernel memory limit"); set_fixture(&tmp, CGROUP_KERNEL_MEMORY_LIMIT, "0").expect("Set fixture for kernel memory limit");
set_fixture(&tmp, CGROUP_KERNEL_TCP_MEMORY_LIMIT, "0").expect("Set fixture for kernel tcp memory limit"); set_fixture(&tmp, CGROUP_KERNEL_TCP_MEMORY_LIMIT, "0").expect("Set fixture for kernel tcp memory limit");
set_fixture(&tmp, CGROUP_PROCS, "").expect("set fixture for proc file"); set_fixture(&tmp, CGROUP_PROCS, "").expect("set fixture for proc file");
// clone to avoid use of moved value later on // clone to avoid use of moved value later on
let memory_limits = linux_memory; let memory_limits = linux_memory;
let linux_resources = LinuxResources { let linux_resources = LinuxResourcesBuilder::default().devices(vec![]).memory(linux_memory).hugepage_limits(vec![]).build().unwrap();
devices: Some(vec![]),
memory: Some(linux_memory),
cpu: None,
pids: None,
block_io: None,
hugepage_limits: Some(vec![]),
network: None,
rdma: None,
unified: None,
};
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
resources: &linux_resources, resources: &linux_resources,
disable_oom_killer, disable_oom_killer,
oom_score_adj: None, oom_score_adj: None,
freezer_state: None, freezer_state: None,
}; };
let result = <Memory as Controller>::apply(&controller_opt, &tmp); let result = <Memory as Controller>::apply(&controller_opt, &tmp);
if result.is_err() { if result.is_err() {
if let Some(swappiness) = memory_limits.swappiness { if let Some(swappiness) = memory_limits.swappiness() {
// error is expected if swappiness is greater than 100 // error is expected if swappiness is greater than 100
if swappiness > 100 { if swappiness > 100 {
return true; return true;
}
} else {
// useful for debugging
println!("Some unexpected error: {:?}", result.unwrap_err());
// any other error should be considered unexpected
return false;
}
}
// check memory reservation
let reservation_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_RESERVATION)).expect("read memory reservation");
let reservation_check = match memory_limits.reservation {
Some(reservation) => {
reservation_content == reservation.to_string()
}
None => reservation_content == "0",
};
// check kernel memory limit
let kernel_content = std::fs::read_to_string(tmp.join(CGROUP_KERNEL_MEMORY_LIMIT)).expect("read kernel memory limit");
let kernel_check = match memory_limits.kernel {
Some(kernel) => {
kernel_content == kernel.to_string()
}
None => kernel_content == "0",
};
// check kernel tcp memory limit
let kernel_tcp_content = std::fs::read_to_string(tmp.join(CGROUP_KERNEL_TCP_MEMORY_LIMIT)).expect("read kernel tcp memory limit");
let kernel_tcp_check = match memory_limits.kernel_tcp {
Some(kernel_tcp) => {
kernel_tcp_content == kernel_tcp.to_string()
}
None => kernel_tcp_content == "0",
};
// check swappiness
let swappiness_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_SWAPPINESS)).expect("read swappiness");
let swappiness_check = match memory_limits.swappiness {
Some(swappiness) if swappiness <= 100 => {
swappiness_content == swappiness.to_string()
}
None => swappiness_content == "0",
// everything else is a failure
_ => false,
};
// check limit and swap
let limit_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("read memory limit");
let swap_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_SWAP_LIMIT)).expect("read swap memory limit");
let limit_swap_check = match memory_limits.limit {
Some(limit) => {
match memory_limits.swap {
Some(swap) => {
limit_content == limit.to_string()
&& swap_content == swap.to_string()
} }
None => { } else {
if limit == -1 { // useful for debugging
println!("Some unexpected error: {:?}", result.unwrap_err());
// any other error should be considered unexpected
return false;
}
}
// check memory reservation
let reservation_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_RESERVATION)).expect("read memory reservation");
let reservation_check = match memory_limits.reservation() {
Some(reservation) => {
reservation_content == reservation.to_string()
}
None => reservation_content == "0",
};
// check kernel memory limit
let kernel_content = std::fs::read_to_string(tmp.join(CGROUP_KERNEL_MEMORY_LIMIT)).expect("read kernel memory limit");
let kernel_check = match memory_limits.kernel() {
Some(kernel) => {
kernel_content == kernel.to_string()
}
None => kernel_content == "0",
};
// check kernel tcp memory limit
let kernel_tcp_content = std::fs::read_to_string(tmp.join(CGROUP_KERNEL_TCP_MEMORY_LIMIT)).expect("read kernel tcp memory limit");
let kernel_tcp_check = match memory_limits.kernel_tcp() {
Some(kernel_tcp) => {
kernel_tcp_content == kernel_tcp.to_string()
}
None => kernel_tcp_content == "0",
};
// check swappiness
let swappiness_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_SWAPPINESS)).expect("read swappiness");
let swappiness_check = match memory_limits.swappiness() {
Some(swappiness) if swappiness <= 100 => {
swappiness_content == swappiness.to_string()
}
None => swappiness_content == "0",
// everything else is a failure
_ => false,
};
// check limit and swap
let limit_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_LIMIT)).expect("read memory limit");
let swap_content = std::fs::read_to_string(tmp.join(CGROUP_MEMORY_SWAP_LIMIT)).expect("read swap memory limit");
let limit_swap_check = match memory_limits.limit() {
Some(limit) => {
match memory_limits.swap() {
Some(swap) => {
limit_content == limit.to_string() limit_content == limit.to_string()
&& swap_content == "-1" && swap_content == swap.to_string()
} else { }
limit_content == limit.to_string() None => {
&& swap_content == "0" if limit == -1 {
limit_content == limit.to_string()
&& swap_content == "-1"
} else {
limit_content == limit.to_string()
&& swap_content == "0"
}
} }
} }
} }
} None => {
None => { match memory_limits.swap() {
match memory_limits.swap { Some(swap) => {
Some(swap) => { limit_content == "0"
limit_content == "0" && swap_content == swap.to_string()
&& swap_content == swap.to_string() }
None => limit_content == "0" && swap_content == "0"
} }
None => limit_content == "0" && swap_content == "0"
} }
} };
};
// useful for debugging // useful for debugging
println!("reservation_check: {:?}", reservation_check); println!("reservation_check: {:?}", reservation_check);
println!("kernel_check: {:?}", kernel_check); println!("kernel_check: {:?}", kernel_check);
println!("kernel_tcp_check: {:?}", kernel_tcp_check); println!("kernel_tcp_check: {:?}", kernel_tcp_check);
println!("swappiness_check: {:?}", swappiness_check); println!("swappiness_check: {:?}", swappiness_check);
println!("limit_swap_check: {:?}", limit_swap_check); println!("limit_swap_check: {:?}", limit_swap_check);
// combine all the checks // combine all the checks
reservation_check && kernel_check && kernel_tcp_check && swappiness_check && limit_swap_check reservation_check && kernel_check && kernel_tcp_check && swappiness_check && limit_swap_check
} }
} }
#[test] #[test]

View File

@ -23,7 +23,7 @@ impl Controller for NetworkClassifier {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(network) = controller_opt.resources.network.as_ref() { if let Some(network) = controller_opt.resources.network().as_ref() {
return Some(network); return Some(network);
} }
@ -33,7 +33,7 @@ impl Controller for NetworkClassifier {
impl NetworkClassifier { impl NetworkClassifier {
fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> { fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> {
if let Some(class_id) = network.class_id { if let Some(class_id) = network.class_id() {
common::write_cgroup_file(root_path.join("net_cls.classid"), class_id)?; common::write_cgroup_file(root_path.join("net_cls.classid"), class_id)?;
} }
@ -45,6 +45,7 @@ impl NetworkClassifier {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxNetworkBuilder;
#[test] #[test]
fn test_apply_network_classifier() { fn test_apply_network_classifier() {
@ -52,11 +53,12 @@ mod tests {
.expect("create temp directory for test"); .expect("create temp directory for test");
set_fixture(&tmp, "net_cls.classid", "0").expect("set fixture for classID"); set_fixture(&tmp, "net_cls.classid", "0").expect("set fixture for classID");
let id = 0x100001; let id = 0x100001u32;
let network = LinuxNetwork { let network = LinuxNetworkBuilder::default()
class_id: Some(id), .class_id(id)
priorities: Some(vec![]), .priorities(vec![])
}; .build()
.unwrap();
NetworkClassifier::apply(&tmp, &network).expect("apply network classID"); NetworkClassifier::apply(&tmp, &network).expect("apply network classID");

View File

@ -23,7 +23,7 @@ impl Controller for NetworkPriority {
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(network) = &controller_opt.resources.network { if let Some(network) = &controller_opt.resources.network() {
return Some(network); return Some(network);
} }
@ -33,7 +33,7 @@ impl Controller for NetworkPriority {
impl NetworkPriority { impl NetworkPriority {
fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> { fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> {
if let Some(ni_priorities) = network.priorities.as_ref() { if let Some(ni_priorities) = network.priorities().as_ref() {
let priorities: String = ni_priorities.iter().map(|p| p.to_string()).collect(); let priorities: String = ni_priorities.iter().map(|p| p.to_string()).collect();
common::write_cgroup_file_str(root_path.join("net_prio.ifpriomap"), priorities.trim())?; common::write_cgroup_file_str(root_path.join("net_prio.ifpriomap"), priorities.trim())?;
} }
@ -46,7 +46,7 @@ impl NetworkPriority {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxInterfacePriority; use oci_spec::runtime::{LinuxInterfacePriorityBuilder, LinuxNetworkBuilder};
#[test] #[test]
fn test_apply_network_priorites() { fn test_apply_network_priorites() {
@ -54,20 +54,22 @@ mod tests {
.expect("create temp directory for test"); .expect("create temp directory for test");
set_fixture(&tmp, "net_prio.ifpriomap", "").expect("set fixture for priority map"); set_fixture(&tmp, "net_prio.ifpriomap", "").expect("set fixture for priority map");
let priorities = vec![ let priorities = vec![
LinuxInterfacePriority { LinuxInterfacePriorityBuilder::default()
name: "a".to_owned(), .name("a")
priority: 1, .priority(1u32)
}, .build()
LinuxInterfacePriority { .unwrap(),
name: "b".to_owned(), LinuxInterfacePriorityBuilder::default()
priority: 2, .name("b")
}, .priority(2u32)
.build()
.unwrap(),
]; ];
let priorities_string = priorities.iter().map(|p| p.to_string()).collect::<String>(); let priorities_string = priorities.iter().map(|p| p.to_string()).collect::<String>();
let network = LinuxNetwork { let network = LinuxNetworkBuilder::default()
class_id: None, .priorities(priorities)
priorities: priorities.into(), .build()
}; .unwrap();
NetworkPriority::apply(&tmp, &network).expect("apply network priorities"); NetworkPriority::apply(&tmp, &network).expect("apply network priorities");

View File

@ -20,17 +20,19 @@ impl Controller for Pids {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply pids cgroup config"); log::debug!("Apply pids cgroup config");
if let Some(pids) = &controller_opt.resources.pids { if let Some(pids) = &controller_opt.resources.pids() {
Self::apply(cgroup_root, pids).context("failed to apply pids resource restrictions")?; Self::apply(cgroup_root, pids).context("failed to apply pids resource restrictions")?;
} }
Ok(()) Ok(())
} }
fn needs_to_handle<'a>(controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> { fn needs_to_handle<'a>(_controller_opt: &'a ControllerOpt) -> Option<&'a Self::Resource> {
if let Some(pids) = &controller_opt.resources.pids { // TODO: fix compile error
return Some(pids); // error[E0515]: cannot return value referencing temporary value
} // if let Some(pids) = &controller_opt.resources.pids() {
// return Some(pids);
// }
None None
} }
@ -46,8 +48,8 @@ impl StatsProvider for Pids {
impl Pids { impl Pids {
fn apply(root_path: &Path, pids: &LinuxPids) -> Result<()> { fn apply(root_path: &Path, pids: &LinuxPids) -> Result<()> {
let limit = if pids.limit > 0 { let limit = if pids.limit() > 0 {
pids.limit.to_string() pids.limit().to_string()
} else { } else {
"max".to_string() "max".to_string()
}; };
@ -61,7 +63,7 @@ impl Pids {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxPids; use oci_spec::runtime::LinuxPidsBuilder;
// Contains the current number of active pids // Contains the current number of active pids
const CGROUP_PIDS_CURRENT: &str = "pids.current"; const CGROUP_PIDS_CURRENT: &str = "pids.current";
@ -71,12 +73,12 @@ mod tests {
let tmp = create_temp_dir("test_set_pids").expect("create temp directory for test"); let tmp = create_temp_dir("test_set_pids").expect("create temp directory for test");
set_fixture(&tmp, CGROUP_PIDS_MAX, "1000").expect("Set fixture for 1000 pids"); set_fixture(&tmp, CGROUP_PIDS_MAX, "1000").expect("Set fixture for 1000 pids");
let pids = LinuxPids { limit: 1000 }; let pids = LinuxPidsBuilder::default().limit(1000).build().unwrap();
Pids::apply(&tmp, &pids).expect("apply pids"); Pids::apply(&tmp, &pids).expect("apply pids");
let content = let content =
std::fs::read_to_string(tmp.join(CGROUP_PIDS_MAX)).expect("Read pids contents"); std::fs::read_to_string(tmp.join(CGROUP_PIDS_MAX)).expect("Read pids contents");
assert_eq!(pids.limit.to_string(), content); assert_eq!(pids.limit().to_string(), content);
} }
#[test] #[test]
@ -84,7 +86,7 @@ mod tests {
let tmp = create_temp_dir("test_set_pids_max").expect("create temp directory for test"); let tmp = create_temp_dir("test_set_pids_max").expect("create temp directory for test");
set_fixture(&tmp, CGROUP_PIDS_MAX, "0").expect("set fixture for 0 pids"); set_fixture(&tmp, CGROUP_PIDS_MAX, "0").expect("set fixture for 0 pids");
let pids = LinuxPids { limit: 0 }; let pids = LinuxPidsBuilder::default().limit(0).build().unwrap();
Pids::apply(&tmp, &pids).expect("apply pids"); Pids::apply(&tmp, &pids).expect("apply pids");

View File

@ -21,7 +21,7 @@ pub struct Cpu {}
impl Controller for Cpu { impl Controller for Cpu {
fn apply(controller_opt: &ControllerOpt, path: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, path: &Path) -> Result<()> {
if let Some(cpu) = &controller_opt.resources.cpu { if let Some(cpu) = &controller_opt.resources.cpu() {
Self::apply(path, cpu).context("failed to apply cpu resource restrictions")?; Self::apply(path, cpu).context("failed to apply cpu resource restrictions")?;
} }
@ -61,7 +61,7 @@ impl Cpu {
bail!("realtime is not supported on cgroup v2 yet"); bail!("realtime is not supported on cgroup v2 yet");
} }
if let Some(mut shares) = cpu.shares { if let Some(mut shares) = cpu.shares() {
shares = Self::convert_shares_to_cgroup2(shares); shares = Self::convert_shares_to_cgroup2(shares);
if shares != 0 { if shares != 0 {
// will result in Erno 34 (numerical result out of range) otherwise // will result in Erno 34 (numerical result out of range) otherwise
@ -71,14 +71,14 @@ impl Cpu {
// if quota is unrestricted set to 'max' // if quota is unrestricted set to 'max'
let mut quota_string = UNRESTRICTED_QUOTA.to_owned(); let mut quota_string = UNRESTRICTED_QUOTA.to_owned();
if let Some(quota) = cpu.quota { if let Some(quota) = cpu.quota() {
if quota > 0 { if quota > 0 {
quota_string = quota.to_string(); quota_string = quota.to_string();
} }
} }
let mut period_string: String = DEFAULT_PERIOD.to_owned(); let mut period_string: String = DEFAULT_PERIOD.to_owned();
if let Some(period) = cpu.period { if let Some(period) = cpu.period() {
if period > 0 { if period > 0 {
period_string = period.to_string(); period_string = period.to_string();
} }
@ -103,11 +103,11 @@ impl Cpu {
} }
fn is_realtime_requested(cpu: &LinuxCpu) -> bool { fn is_realtime_requested(cpu: &LinuxCpu) -> bool {
if cpu.realtime_period.is_some() { if cpu.realtime_period().is_some() {
return true; return true;
} }
if cpu.realtime_runtime.is_some() { if cpu.realtime_runtime().is_some() {
return true; return true;
} }
@ -118,7 +118,8 @@ impl Cpu {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture, setup, LinuxCpuBuilder}; use crate::test::{create_temp_dir, set_fixture, setup};
use oci_spec::runtime::LinuxCpuBuilder;
use std::fs; use std::fs;
#[test] #[test]
@ -127,7 +128,7 @@ mod tests {
let (tmp, weight) = setup("test_set_shares", CGROUP_CPU_WEIGHT); let (tmp, weight) = setup("test_set_shares", CGROUP_CPU_WEIGHT);
let _ = set_fixture(&tmp, CGROUP_CPU_MAX, "") let _ = set_fixture(&tmp, CGROUP_CPU_MAX, "")
.unwrap_or_else(|_| panic!("set test fixture for {}", CGROUP_CPU_MAX)); .unwrap_or_else(|_| panic!("set test fixture for {}", CGROUP_CPU_MAX));
let cpu = LinuxCpuBuilder::new().with_shares(22000).build(); let cpu = LinuxCpuBuilder::default().shares(22000u64).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -143,7 +144,7 @@ mod tests {
// arrange // arrange
const QUOTA: i64 = 200000; const QUOTA: i64 = 200000;
let (tmp, max) = setup("test_set_positive_quota", CGROUP_CPU_MAX); let (tmp, max) = setup("test_set_positive_quota", CGROUP_CPU_MAX);
let cpu = LinuxCpuBuilder::new().with_quota(QUOTA).build(); let cpu = LinuxCpuBuilder::default().quota(QUOTA).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -158,7 +159,7 @@ mod tests {
fn test_set_zero_quota() { fn test_set_zero_quota() {
// arrange // arrange
let (tmp, max) = setup("test_set_zero_quota", CGROUP_CPU_MAX); let (tmp, max) = setup("test_set_zero_quota", CGROUP_CPU_MAX);
let cpu = LinuxCpuBuilder::new().with_quota(0).build(); let cpu = LinuxCpuBuilder::default().quota(0).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -177,7 +178,7 @@ mod tests {
// arrange // arrange
const PERIOD: u64 = 100000; const PERIOD: u64 = 100000;
let (tmp, max) = setup("test_set_positive_period", CGROUP_CPU_MAX); let (tmp, max) = setup("test_set_positive_period", CGROUP_CPU_MAX);
let cpu = LinuxCpuBuilder::new().with_period(PERIOD).build(); let cpu = LinuxCpuBuilder::default().period(PERIOD).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -192,7 +193,7 @@ mod tests {
fn test_set_zero_period() { fn test_set_zero_period() {
// arrange // arrange
let (tmp, max) = setup("test_set_zero_period", CGROUP_CPU_MAX); let (tmp, max) = setup("test_set_zero_period", CGROUP_CPU_MAX);
let cpu = LinuxCpuBuilder::new().with_period(0).build(); let cpu = LinuxCpuBuilder::default().period(0u64).build().unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -212,10 +213,11 @@ mod tests {
const QUOTA: i64 = 200000; const QUOTA: i64 = 200000;
const PERIOD: u64 = 100000; const PERIOD: u64 = 100000;
let (tmp, max) = setup("test_set_quota_and_period", CGROUP_CPU_MAX); let (tmp, max) = setup("test_set_quota_and_period", CGROUP_CPU_MAX);
let cpu = LinuxCpuBuilder::new() let cpu = LinuxCpuBuilder::default()
.with_quota(QUOTA) .quota(QUOTA)
.with_period(PERIOD) .period(PERIOD)
.build(); .build()
.unwrap();
// act // act
Cpu::apply(&tmp, &cpu).expect("apply cpu"); Cpu::apply(&tmp, &cpu).expect("apply cpu");
@ -231,7 +233,10 @@ mod tests {
// arrange // arrange
let tmp = create_temp_dir("test_realtime_runtime_not_supported") let tmp = create_temp_dir("test_realtime_runtime_not_supported")
.expect("create temp directory for test"); .expect("create temp directory for test");
let cpu = LinuxCpuBuilder::new().with_realtime_runtime(5).build(); let cpu = LinuxCpuBuilder::default()
.realtime_runtime(5)
.build()
.unwrap();
// act // act
let result = Cpu::apply(&tmp, &cpu); let result = Cpu::apply(&tmp, &cpu);
@ -248,7 +253,10 @@ mod tests {
// arrange // arrange
let tmp = create_temp_dir("test_realtime_period_not_supported") let tmp = create_temp_dir("test_realtime_period_not_supported")
.expect("create temp directory for test"); .expect("create temp directory for test");
let cpu = LinuxCpuBuilder::new().with_realtime_period(5).build(); let cpu = LinuxCpuBuilder::default()
.realtime_period(5u64)
.build()
.unwrap();
// act // act
let result = Cpu::apply(&tmp, &cpu); let result = Cpu::apply(&tmp, &cpu);

View File

@ -13,7 +13,7 @@ pub struct CpuSet {}
impl Controller for CpuSet { impl Controller for CpuSet {
fn apply(controller_opt: &ControllerOpt, cgroup_path: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_path: &Path) -> Result<()> {
if let Some(cpuset) = &controller_opt.resources.cpu { if let Some(cpuset) = &controller_opt.resources.cpu() {
Self::apply(cgroup_path, cpuset) Self::apply(cgroup_path, cpuset)
.context("failed to apply cpuset resource restrictions")?; .context("failed to apply cpuset resource restrictions")?;
} }
@ -24,11 +24,11 @@ impl Controller for CpuSet {
impl CpuSet { impl CpuSet {
fn apply(path: &Path, cpuset: &LinuxCpu) -> Result<()> { fn apply(path: &Path, cpuset: &LinuxCpu) -> Result<()> {
if let Some(cpus) = &cpuset.cpus { if let Some(cpus) = &cpuset.cpus() {
common::write_cgroup_file_str(path.join(CGROUP_CPUSET_CPUS), cpus)?; common::write_cgroup_file_str(path.join(CGROUP_CPUSET_CPUS), cpus)?;
} }
if let Some(mems) = &cpuset.mems { if let Some(mems) = &cpuset.mems() {
common::write_cgroup_file_str(path.join(CGROUP_CPUSET_MEMS), mems)?; common::write_cgroup_file_str(path.join(CGROUP_CPUSET_MEMS), mems)?;
} }
@ -41,13 +41,17 @@ mod tests {
use std::fs; use std::fs;
use super::*; use super::*;
use crate::test::{setup, LinuxCpuBuilder}; use crate::test::setup;
use oci_spec::runtime::LinuxCpuBuilder;
#[test] #[test]
fn test_set_cpus() { fn test_set_cpus() {
// arrange // arrange
let (tmp, cpus) = setup("test_set_cpus", CGROUP_CPUSET_CPUS); let (tmp, cpus) = setup("test_set_cpus", CGROUP_CPUSET_CPUS);
let cpuset = LinuxCpuBuilder::new().with_cpus("1-3".to_owned()).build(); let cpuset = LinuxCpuBuilder::default()
.cpus("1-3".to_owned())
.build()
.unwrap();
// act // act
CpuSet::apply(&tmp, &cpuset).expect("apply cpuset"); CpuSet::apply(&tmp, &cpuset).expect("apply cpuset");
@ -62,7 +66,10 @@ mod tests {
fn test_set_mems() { fn test_set_mems() {
// arrange // arrange
let (tmp, mems) = setup("test_set_mems", CGROUP_CPUSET_MEMS); let (tmp, mems) = setup("test_set_mems", CGROUP_CPUSET_MEMS);
let cpuset = LinuxCpuBuilder::new().with_mems("1-3".to_owned()).build(); let cpuset = LinuxCpuBuilder::default()
.mems("1-3".to_owned())
.build()
.unwrap();
// act // act
CpuSet::apply(&tmp, &cpuset).expect("apply cpuset"); CpuSet::apply(&tmp, &cpuset).expect("apply cpuset");

View File

@ -21,7 +21,7 @@ impl Controller for Devices {
return Ok(()); return Ok(());
#[cfg(feature = "cgroupsv2_devices")] #[cfg(feature = "cgroupsv2_devices")]
return Self::apply_devices(cgroup_root, &controller_opt.resources.devices); return Self::apply_devices(cgroup_root, controller_opt.resources.devices());
} }
} }

View File

@ -38,14 +38,14 @@ impl Emulator {
pub fn add_rule(&mut self, rule: &LinuxDeviceCgroup) -> Result<()> { pub fn add_rule(&mut self, rule: &LinuxDeviceCgroup) -> Result<()> {
// special case, switch to blacklist or whitelist and clear all existing rules // special case, switch to blacklist or whitelist and clear all existing rules
// NOTE: we ignore other fields when type='a', this is same as cgroup v1 and runc // NOTE: we ignore other fields when type='a', this is same as cgroup v1 and runc
if rule.typ.unwrap_or_default() == LinuxDeviceType::A { if rule.typ().unwrap_or_default() == LinuxDeviceType::A {
self.default_allow = rule.allow; self.default_allow = rule.allow();
self.rules.clear(); self.rules.clear();
return Ok(()); return Ok(());
} }
// empty access match nothing, just discard this rule // empty access match nothing, just discard this rule
if rule.access.is_none() { if rule.access().is_none() {
return Ok(()); return Ok(());
} }

View File

@ -90,15 +90,15 @@ impl Program {
} }
fn add_rule(&mut self, rule: &LinuxDeviceCgroup) -> Result<()> { fn add_rule(&mut self, rule: &LinuxDeviceCgroup) -> Result<()> {
let dev_type = bpf_dev_type(rule.typ.unwrap_or_default())?; let dev_type = bpf_dev_type(rule.typ().unwrap_or_default())?;
let access = bpf_access(rule.access.clone().unwrap_or_default())?; let access = bpf_access(rule.access().clone().unwrap_or_default())?;
let has_access = access let has_access = access
!= (libbpf_sys::BPF_DEVCG_ACC_READ != (libbpf_sys::BPF_DEVCG_ACC_READ
| libbpf_sys::BPF_DEVCG_ACC_WRITE | libbpf_sys::BPF_DEVCG_ACC_WRITE
| libbpf_sys::BPF_DEVCG_ACC_MKNOD); | libbpf_sys::BPF_DEVCG_ACC_MKNOD);
let has_major = rule.major.is_some() && rule.major.unwrap() >= 0; let has_major = rule.major().is_some() && rule.major().unwrap() >= 0;
let has_minor = rule.minor.is_some() && rule.minor.unwrap() >= 0; let has_minor = rule.minor().is_some() && rule.minor().unwrap() >= 0;
// count of instructions of this rule // count of instructions of this rule
let mut instruction_count = 1; // execute dev_type let mut instruction_count = 1; // execute dev_type
@ -151,7 +151,7 @@ impl Program {
self.prog self.prog
.jump_conditional(Cond::NotEquals, Source::Imm) .jump_conditional(Cond::NotEquals, Source::Imm)
.set_dst(4) .set_dst(4)
.set_imm(rule.major.unwrap() as i32) .set_imm(rule.major().unwrap() as i32)
.set_off(next_rule_offset) .set_off(next_rule_offset)
.push(); .push();
} }
@ -162,7 +162,7 @@ impl Program {
self.prog self.prog
.jump_conditional(Cond::NotEquals, Source::Imm) .jump_conditional(Cond::NotEquals, Source::Imm)
.set_dst(5) .set_dst(5)
.set_imm(rule.minor.unwrap() as i32) .set_imm(rule.minor().unwrap() as i32)
.set_off(next_rule_offset) .set_off(next_rule_offset)
.push(); .push();
} }
@ -171,7 +171,7 @@ impl Program {
self.prog self.prog
.mov(Source::Imm, RbpfArch::X32) .mov(Source::Imm, RbpfArch::X32)
.set_dst(0) .set_dst(0)
.set_imm(rule.allow as i32) .set_imm(rule.allow() as i32)
.push(); .push();
self.prog.exit().push(); self.prog.exit().push();
@ -248,6 +248,7 @@ fn bpf_cgroup_dev_ctx(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use oci_spec::runtime::LinuxDeviceCgroupBuilder;
fn build_bpf_program(rules: &Option<Vec<LinuxDeviceCgroup>>) -> Result<Program> { fn build_bpf_program(rules: &Option<Vec<LinuxDeviceCgroup>>) -> Result<Program> {
let mut em = crate::v2::devices::emulator::Emulator::with_default_allow(false); let mut em = crate::v2::devices::emulator::Emulator::with_default_allow(false);
@ -260,13 +261,14 @@ mod tests {
#[test] #[test]
fn test_devices_allow_single() { fn test_devices_allow_single() {
let rules = vec![LinuxDeviceCgroup { let rules = vec![LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(10), .major(10)
minor: Some(20), .minor(20)
access: "r".to_string().into(), .access("r")
}]; .build()
.unwrap()];
let prog = build_bpf_program(&Some(rules)).unwrap(); let prog = build_bpf_program(&Some(rules)).unwrap();
let ty_list = vec![ let ty_list = vec![
@ -333,13 +335,11 @@ mod tests {
#[test] #[test]
fn test_devices_allow_all() { fn test_devices_allow_all() {
let rules = vec![LinuxDeviceCgroup { let rules = vec![LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::A), .typ(LinuxDeviceType::A)
major: None, .build()
minor: None, .unwrap()];
access: None,
}];
let prog = build_bpf_program(&Some(rules)).unwrap(); let prog = build_bpf_program(&Some(rules)).unwrap();
let ty_list = vec![ let ty_list = vec![
@ -371,13 +371,13 @@ mod tests {
#[test] #[test]
fn test_devices_allow_wildcard() { fn test_devices_allow_wildcard() {
let rules = vec![LinuxDeviceCgroup { let rules = vec![LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: None, .minor(20)
minor: Some(20), .access("r")
access: "r".to_string().into(), .build()
}]; .unwrap()];
let prog = build_bpf_program(&Some(rules)).unwrap(); let prog = build_bpf_program(&Some(rules)).unwrap();
let ty_list = vec![ let ty_list = vec![
@ -414,20 +414,20 @@ mod tests {
#[test] #[test]
fn test_devices_allow_and_deny() { fn test_devices_allow_and_deny() {
let rules = vec![ let rules = vec![
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: true, .allow(true)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: None, .minor(20)
minor: Some(20), .access("rw")
access: "rw".to_string().into(), .build()
}, .unwrap(),
LinuxDeviceCgroup { LinuxDeviceCgroupBuilder::default()
allow: false, .allow(false)
typ: Some(LinuxDeviceType::C), .typ(LinuxDeviceType::C)
major: Some(10), .major(10)
minor: None, .access("r")
access: "r".to_string().into(), .build()
}, .unwrap(),
]; ];
let prog = build_bpf_program(&Some(rules)).unwrap(); let prog = build_bpf_program(&Some(rules)).unwrap();

View File

@ -14,7 +14,7 @@ pub struct HugeTlb {}
impl Controller for HugeTlb { impl Controller for HugeTlb {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> {
log::debug!("Apply hugetlb cgroup v2 config"); log::debug!("Apply hugetlb cgroup v2 config");
if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits.as_ref() { if let Some(hugepage_limits) = controller_opt.resources.hugepage_limits().as_ref() {
for hugetlb in hugepage_limits { for hugetlb in hugepage_limits {
Self::apply(cgroup_root, hugetlb) Self::apply(cgroup_root, hugetlb)
.context("failed to apply hugetlb resource restrictions")? .context("failed to apply hugetlb resource restrictions")?
@ -45,7 +45,7 @@ impl StatsProvider for HugeTlb {
impl HugeTlb { impl HugeTlb {
fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> { fn apply(root_path: &Path, hugetlb: &LinuxHugepageLimit) -> Result<()> {
let page_size: String = hugetlb let page_size: String = hugetlb
.page_size .page_size()
.chars() .chars()
.take_while(|c| c.is_digit(10)) .take_while(|c| c.is_digit(10))
.collect(); .collect();
@ -55,8 +55,8 @@ impl HugeTlb {
} }
common::write_cgroup_file( common::write_cgroup_file(
root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)), root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size())),
hugetlb.limit, hugetlb.limit(),
)?; )?;
Ok(()) Ok(())
} }
@ -88,7 +88,7 @@ impl HugeTlb {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxHugepageLimit; use oci_spec::runtime::LinuxHugepageLimitBuilder;
use std::fs::read_to_string; use std::fs::read_to_string;
#[test] #[test]
@ -97,13 +97,14 @@ mod tests {
let tmp = create_temp_dir("test_set_hugetlbv2").expect("create temp directory for test"); let tmp = create_temp_dir("test_set_hugetlbv2").expect("create temp directory for test");
set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size"); set_fixture(&tmp, page_file_name, "0").expect("Set fixture for 2 MB page size");
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimitBuilder::default()
page_size: "2MB".to_owned(), .page_size("2MB")
limit: 16384, .limit(16384)
}; .build()
.unwrap();
HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb"); HugeTlb::apply(&tmp, &hugetlb).expect("apply hugetlb");
let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content"); let content = read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
assert_eq!(hugetlb.limit.to_string(), content); assert_eq!(hugetlb.limit().to_string(), content);
} }
#[test] #[test]
@ -111,10 +112,11 @@ mod tests {
let tmp = create_temp_dir("test_set_hugetlbv2_with_invalid_page_size") let tmp = create_temp_dir("test_set_hugetlbv2_with_invalid_page_size")
.expect("create temp directory for test"); .expect("create temp directory for test");
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimitBuilder::default()
page_size: "3MB".to_owned(), .page_size("3MB")
limit: 16384, .limit(16384)
}; .build()
.unwrap();
let result = HugeTlb::apply(&tmp, &hugetlb); let result = HugeTlb::apply(&tmp, &hugetlb);
assert!( assert!(
@ -125,13 +127,13 @@ mod tests {
quickcheck! { quickcheck! {
fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool { fn property_test_set_hugetlb(hugetlb: LinuxHugepageLimit) -> bool {
let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size); let page_file_name = format!("hugetlb.{:?}.limit_in_bytes", hugetlb.page_size());
let tmp = create_temp_dir("property_test_set_hugetlbv2").expect("create temp directory for test"); let tmp = create_temp_dir("property_test_set_hugetlbv2").expect("create temp directory for test");
set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size"); set_fixture(&tmp, &page_file_name, "0").expect("Set fixture for page size");
let result = HugeTlb::apply(&tmp, &hugetlb); let result = HugeTlb::apply(&tmp, &hugetlb);
let page_size: String = hugetlb let page_size: String = hugetlb
.page_size .page_size()
.chars() .chars()
.take_while(|c| c.is_digit(10)) .take_while(|c| c.is_digit(10))
.collect(); .collect();
@ -140,7 +142,7 @@ mod tests {
if HugeTlb::is_power_of_two(page_size) && page_size != 1 { if HugeTlb::is_power_of_two(page_size) && page_size != 1 {
let content = let content =
read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content"); read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content");
hugetlb.limit.to_string() == content hugetlb.limit().to_string() == content
} else { } else {
result.is_err() result.is_err()
} }

View File

@ -19,7 +19,7 @@ pub struct Io {}
impl Controller for Io { impl Controller for Io {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &Path) -> Result<()> {
log::debug!("Apply io cgroup v2 config"); log::debug!("Apply io cgroup v2 config");
if let Some(io) = &controller_opt.resources.block_io { if let Some(io) = &controller_opt.resources.block_io() {
Self::apply(cgroup_root, io).context("failed to apply io resource restrictions")?; Self::apply(cgroup_root, io).context("failed to apply io resource restrictions")?;
} }
Ok(()) Ok(())
@ -85,20 +85,20 @@ impl Io {
// linux kernel doc: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io // linux kernel doc: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#io
fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> Result<()> { fn apply(root_path: &Path, blkio: &LinuxBlockIo) -> Result<()> {
if let Some(weight_device) = blkio.weight_device.as_ref() { if let Some(weight_device) = blkio.weight_device().as_ref() {
for wd in weight_device { for wd in weight_device {
common::write_cgroup_file( common::write_cgroup_file(
root_path.join(CGROUP_BFQ_IO_WEIGHT), root_path.join(CGROUP_BFQ_IO_WEIGHT),
&format!("{}:{} {}", wd.major, wd.minor, wd.weight.unwrap()), &format!("{}:{} {}", wd.major(), wd.minor(), wd.weight().unwrap()),
)?; )?;
} }
} }
if let Some(leaf_weight) = blkio.leaf_weight { if let Some(leaf_weight) = blkio.leaf_weight() {
if leaf_weight > 0 { if leaf_weight > 0 {
bail!("cannot set leaf_weight with cgroupv2"); bail!("cannot set leaf_weight with cgroupv2");
} }
} }
if let Some(io_weight) = blkio.weight { if let Some(io_weight) = blkio.weight() {
if io_weight > 0 { if io_weight > 0 {
common::write_cgroup_file( common::write_cgroup_file(
root_path.join(CGROUP_IO_WEIGHT), root_path.join(CGROUP_IO_WEIGHT),
@ -107,38 +107,38 @@ impl Io {
} }
} }
if let Some(throttle_read_bps_device) = blkio.throttle_read_bps_device.as_ref() { if let Some(throttle_read_bps_device) = blkio.throttle_read_bps_device().as_ref() {
for trbd in throttle_read_bps_device { for trbd in throttle_read_bps_device {
common::write_cgroup_file( common::write_cgroup_file(
Self::io_max_path(root_path), Self::io_max_path(root_path),
&format!("{}:{} rbps={}", trbd.major, trbd.minor, trbd.rate), &format!("{}:{} rbps={}", trbd.major(), trbd.minor(), trbd.rate()),
)?; )?;
} }
} }
if let Some(throttle_write_bps_device) = blkio.throttle_write_bps_device.as_ref() { if let Some(throttle_write_bps_device) = blkio.throttle_write_bps_device().as_ref() {
for twbd in throttle_write_bps_device { for twbd in throttle_write_bps_device {
common::write_cgroup_file( common::write_cgroup_file(
Self::io_max_path(root_path), Self::io_max_path(root_path),
format!("{}:{} wbps={}", twbd.major, twbd.minor, twbd.rate), format!("{}:{} wbps={}", twbd.major(), twbd.minor(), twbd.rate()),
)?; )?;
} }
} }
if let Some(throttle_read_iops_device) = blkio.throttle_read_iops_device.as_ref() { if let Some(throttle_read_iops_device) = blkio.throttle_read_iops_device().as_ref() {
for trid in throttle_read_iops_device { for trid in throttle_read_iops_device {
common::write_cgroup_file( common::write_cgroup_file(
Self::io_max_path(root_path), Self::io_max_path(root_path),
format!("{}:{} riops={}", trid.major, trid.minor, trid.rate), format!("{}:{} riops={}", trid.major(), trid.minor(), trid.rate()),
)?; )?;
} }
} }
if let Some(throttle_write_iops_device) = blkio.throttle_write_iops_device.as_ref() { if let Some(throttle_write_iops_device) = blkio.throttle_write_iops_device().as_ref() {
for twid in throttle_write_iops_device { for twid in throttle_write_iops_device {
common::write_cgroup_file( common::write_cgroup_file(
Self::io_max_path(root_path), Self::io_max_path(root_path),
format!("{}:{} wiops={}", twid.major, twid.minor, twid.rate), format!("{}:{} wiops={}", twid.major(), twid.minor(), twid.rate()),
)?; )?;
} }
} }
@ -151,70 +151,24 @@ mod test {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture, setup}; use crate::test::{create_temp_dir, set_fixture, setup};
use oci_spec::runtime::{LinuxBlockIo, LinuxThrottleDevice, LinuxWeightDevice}; use oci_spec::runtime::{
LinuxBlockIoBuilder, LinuxThrottleDeviceBuilder, LinuxWeightDeviceBuilder,
};
use std::fs; use std::fs;
struct BlockIoBuilder {
block_io: LinuxBlockIo,
}
impl BlockIoBuilder {
fn new() -> Self {
let block_io = LinuxBlockIo {
weight: Some(0),
leaf_weight: Some(0),
weight_device: vec![].into(),
throttle_read_bps_device: vec![].into(),
throttle_write_bps_device: vec![].into(),
throttle_read_iops_device: vec![].into(),
throttle_write_iops_device: vec![].into(),
};
Self { block_io }
}
fn with_write_weight_device(mut self, throttle: Vec<LinuxWeightDevice>) -> Self {
self.block_io.weight_device = throttle.into();
self
}
fn with_write_io_weight(mut self, iow: u16) -> Self {
self.block_io.weight = Some(iow);
self
}
fn with_read_bps(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_read_bps_device = throttle.into();
self
}
fn with_write_bps(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_write_bps_device = throttle.into();
self
}
fn with_read_iops(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_read_iops_device = throttle.into();
self
}
fn with_write_iops(mut self, throttle: Vec<LinuxThrottleDevice>) -> Self {
self.block_io.throttle_write_iops_device = throttle.into();
self
}
fn build(self) -> LinuxBlockIo {
self.block_io
}
}
#[test] #[test]
fn test_set_io_read_bps() { fn test_set_io_read_bps() {
let (tmp, throttle) = setup("test_set_io_read_bps", "io.max"); let (tmp, throttle) = setup("test_set_io_read_bps", "io.max");
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_read_bps(vec![LinuxThrottleDevice { .throttle_read_bps_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read rbps content")); let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read rbps content"));
@ -226,13 +180,15 @@ mod test {
fn test_set_io_write_bps() { fn test_set_io_write_bps() {
let (tmp, throttle) = setup("test_set_io_write_bps", "io.max"); let (tmp, throttle) = setup("test_set_io_write_bps", "io.max");
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_write_bps(vec![LinuxThrottleDevice { .throttle_write_bps_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read rbps content")); let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read rbps content"));
@ -244,13 +200,15 @@ mod test {
fn test_set_io_read_iops() { fn test_set_io_read_iops() {
let (tmp, throttle) = setup("test_set_io_read_iops", "io.max"); let (tmp, throttle) = setup("test_set_io_read_iops", "io.max");
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_read_iops(vec![LinuxThrottleDevice { .throttle_read_iops_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read riops content")); let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read riops content"));
@ -262,13 +220,15 @@ mod test {
fn test_set_io_write_iops() { fn test_set_io_write_iops() {
let (tmp, throttle) = setup("test_set_io_write_iops", "io.max"); let (tmp, throttle) = setup("test_set_io_write_iops", "io.max");
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_write_iops(vec![LinuxThrottleDevice { .throttle_write_iops_device(vec![LinuxThrottleDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
rate: 102400, .rate(102400u64)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read wiops content")); let content = fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read wiops content"));
@ -279,14 +239,17 @@ mod test {
#[test] #[test]
fn test_set_ioweight_device() { fn test_set_ioweight_device() {
let (tmp, throttle) = setup("test_set_io_weight_device", CGROUP_BFQ_IO_WEIGHT); let (tmp, throttle) = setup("test_set_io_weight_device", CGROUP_BFQ_IO_WEIGHT);
let blkio = BlockIoBuilder::new() let blkio = LinuxBlockIoBuilder::default()
.with_write_weight_device(vec![LinuxWeightDevice { .weight_device(vec![LinuxWeightDeviceBuilder::default()
major: 8, .major(8)
minor: 0, .minor(0)
weight: Some(80), .weight(80u16)
leaf_weight: Some(0), .leaf_weight(0u16)
}]) .build()
.build(); .unwrap()])
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = let content =
fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read bfq_io_weight content")); fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read bfq_io_weight content"));
@ -297,7 +260,11 @@ mod test {
#[test] #[test]
fn test_set_ioweight() { fn test_set_ioweight() {
let (tmp, throttle) = setup("test_set_io_weight", CGROUP_IO_WEIGHT); let (tmp, throttle) = setup("test_set_io_weight", CGROUP_IO_WEIGHT);
let blkio = BlockIoBuilder::new().with_write_io_weight(100).build(); let blkio = LinuxBlockIoBuilder::default()
.weight(100u16)
.build()
.unwrap();
Io::apply(&tmp, &blkio).expect("apply blkio"); Io::apply(&tmp, &blkio).expect("apply blkio");
let content = let content =
fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read bfq_io_weight content")); fs::read_to_string(throttle).unwrap_or_else(|_| panic!("read bfq_io_weight content"));

View File

@ -19,7 +19,7 @@ pub struct Memory {}
impl Controller for Memory { impl Controller for Memory {
fn apply(controller_opt: &ControllerOpt, cgroup_path: &Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_path: &Path) -> Result<()> {
if let Some(memory) = &controller_opt.resources.memory { if let Some(memory) = &controller_opt.resources.memory() {
Self::apply(cgroup_path, memory) Self::apply(cgroup_path, memory)
.context("failed to apply memory resource restrictions")?; .context("failed to apply memory resource restrictions")?;
} }
@ -84,15 +84,15 @@ impl Memory {
fn apply(path: &Path, memory: &LinuxMemory) -> Result<()> { fn apply(path: &Path, memory: &LinuxMemory) -> Result<()> {
// if nothing is set just exit right away // if nothing is set just exit right away
if memory.reservation.is_none() && memory.limit.is_none() && memory.swap.is_none() { if memory.reservation().is_none() && memory.limit().is_none() && memory.swap().is_none() {
return Ok(()); return Ok(());
} }
match memory.limit { match memory.limit() {
Some(limit) if limit < -1 => { Some(limit) if limit < -1 => {
bail!("invalid memory value: {}", limit); bail!("invalid memory value: {}", limit);
} }
Some(limit) => match memory.swap { Some(limit) => match memory.swap() {
Some(swap) if swap < -1 => { Some(swap) if swap < -1 => {
bail!("invalid swap value: {}", swap); bail!("invalid swap value: {}", swap);
} }
@ -125,13 +125,13 @@ impl Memory {
} }
}, },
None => { None => {
if memory.swap.is_some() { if memory.swap().is_some() {
bail!("unable to set swap limit without memory limit"); bail!("unable to set swap limit without memory limit");
} }
} }
}; };
if let Some(reservation) = memory.reservation { if let Some(reservation) = memory.reservation() {
if reservation < -1 { if reservation < -1 {
bail!("invalid memory reservation value: {}", reservation); bail!("invalid memory reservation value: {}", reservation);
} }
@ -146,7 +146,7 @@ impl Memory {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxMemory; use oci_spec::runtime::LinuxMemoryBuilder;
use std::fs::read_to_string; use std::fs::read_to_string;
#[test] #[test]
@ -159,17 +159,15 @@ mod tests {
let limit = 1024; let limit = 1024;
let reservation = 512; let reservation = 512;
let swap = 2048; let swap = 2048;
let memory_limits = &LinuxMemory {
limit: Some(limit), let memory_limits = LinuxMemoryBuilder::default()
reservation: Some(reservation), .limit(limit)
swap: Some(swap), .reservation(reservation)
kernel: None, .swap(swap)
kernel_tcp: None, .build()
swappiness: None, .unwrap();
disable_oom_killer: None,
use_hierarchy: None, Memory::apply(&tmp, &memory_limits).expect("apply memory limits");
};
Memory::apply(&tmp, memory_limits).expect("apply memory limits");
let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit"); let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit");
assert_eq!(limit_content, limit.to_string()); assert_eq!(limit_content, limit.to_string());
@ -190,17 +188,9 @@ mod tests {
set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation"); set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation");
set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit"); set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit");
let memory_limits = &LinuxMemory { let memory_limits = LinuxMemoryBuilder::default().limit(-1).build().unwrap();
limit: Some(-1),
reservation: None, Memory::apply(&tmp, &memory_limits).expect("apply memory limits");
swap: None,
kernel: None,
kernel_tcp: None,
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
Memory::apply(&tmp, memory_limits).expect("apply memory limits");
let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit"); let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit");
assert_eq!(limit_content, "max"); assert_eq!(limit_content, "max");
@ -217,18 +207,9 @@ mod tests {
set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation"); set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation");
set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit"); set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit");
let memory_limits = &LinuxMemory { let memory_limits = LinuxMemoryBuilder::default().swap(512).build().unwrap();
limit: None,
swap: Some(512),
reservation: None,
kernel: None,
kernel_tcp: None,
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
let result = Memory::apply(&tmp, memory_limits); let result = Memory::apply(&tmp, &memory_limits);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -240,18 +221,9 @@ mod tests {
set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation"); set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation");
set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit"); set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit");
let memory_limits = &LinuxMemory { let memory_limits = LinuxMemoryBuilder::default().limit(-2).build().unwrap();
limit: Some(-2),
swap: None,
reservation: None,
kernel: None,
kernel_tcp: None,
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
let result = Memory::apply(&tmp, memory_limits); let result = Memory::apply(&tmp, &memory_limits);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -263,18 +235,13 @@ mod tests {
set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation"); set_fixture(&tmp, CGROUP_MEMORY_LOW, "0").expect("set fixture for memory reservation");
set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit"); set_fixture(&tmp, CGROUP_MEMORY_SWAP, "0").expect("set fixture for swap limit");
let memory_limits = &LinuxMemory { let memory_limits = LinuxMemoryBuilder::default()
limit: Some(512), .limit(512)
swap: Some(-3), .swap(-3)
reservation: None, .build()
kernel: None, .unwrap();
kernel_tcp: None,
swappiness: None,
disable_oom_killer: None,
use_hierarchy: None,
};
let result = Memory::apply(&tmp, memory_limits); let result = Memory::apply(&tmp, &memory_limits);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -290,27 +257,27 @@ mod tests {
// we need to check for expected errors first and foremost or we'll get false negatives // we need to check for expected errors first and foremost or we'll get false negatives
// later // later
if let Some(limit) = linux_memory.limit { if let Some(limit) = linux_memory.limit() {
if limit < -1 { if limit < -1 {
return result.is_err(); return result.is_err();
} }
} }
if let Some(swap) = linux_memory.swap { if let Some(swap) = linux_memory.swap() {
if swap < -1 { if swap < -1 {
return result.is_err(); return result.is_err();
} }
if linux_memory.limit.is_none() { if linux_memory.limit().is_none() {
return result.is_err(); return result.is_err();
} }
if let Some(limit) = linux_memory.limit { if let Some(limit) = linux_memory.limit() {
if limit != -1 && swap != -1 && swap < limit { if limit != -1 && swap != -1 && swap < limit {
return result.is_err(); return result.is_err();
} }
} }
} }
if let Some(reservation) = linux_memory.reservation { if let Some(reservation) = linux_memory.reservation() {
if reservation < -1 { if reservation < -1 {
return result.is_err(); return result.is_err();
} }
@ -318,7 +285,7 @@ mod tests {
// check the limit file is set as expected // check the limit file is set as expected
let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit to string"); let limit_content = read_to_string(tmp.join(CGROUP_MEMORY_MAX)).expect("read memory limit to string");
let limit_check = match linux_memory.limit { let limit_check = match linux_memory.limit() {
Some(limit) if limit == -1 => limit_content == "max", Some(limit) if limit == -1 => limit_content == "max",
Some(limit) => limit_content == limit.to_string(), Some(limit) => limit_content == limit.to_string(),
None => limit_content == "0", None => limit_content == "0",
@ -326,21 +293,21 @@ mod tests {
// check the swap file is set as expected // check the swap file is set as expected
let swap_content = read_to_string(tmp.join(CGROUP_MEMORY_SWAP)).expect("read swap limit to string"); let swap_content = read_to_string(tmp.join(CGROUP_MEMORY_SWAP)).expect("read swap limit to string");
let swap_check = match linux_memory.swap { let swap_check = match linux_memory.swap() {
Some(swap) if swap == -1 => swap_content == "max", Some(swap) if swap == -1 => swap_content == "max",
Some(swap) => { Some(swap) => {
if let Some(limit) = linux_memory.limit { if let Some(limit) = linux_memory.limit() {
if limit == -1 { if limit == -1 {
swap_content == swap.to_string() swap_content == swap.to_string()
} else { } else {
swap_content == (swap - linux_memory.limit.unwrap()).to_string() swap_content == (swap - linux_memory.limit().unwrap()).to_string()
} }
} else { } else {
false false
} }
} }
None => { None => {
match linux_memory.limit { match linux_memory.limit() {
Some(limit) if limit == -1 => swap_content == "max", Some(limit) if limit == -1 => swap_content == "max",
_ => swap_content == "0", _ => swap_content == "0",
} }
@ -350,7 +317,7 @@ mod tests {
// check the resevation file is set as expected // check the resevation file is set as expected
let reservation_content = read_to_string(tmp.join(CGROUP_MEMORY_LOW)).expect("read memory reservation to string"); let reservation_content = read_to_string(tmp.join(CGROUP_MEMORY_LOW)).expect("read memory reservation to string");
let reservation_check = match linux_memory.reservation { let reservation_check = match linux_memory.reservation() {
Some(reservation) if reservation == -1 => reservation_content == "max", Some(reservation) if reservation == -1 => reservation_content == "max",
Some(reservation) => reservation_content == reservation.to_string(), Some(reservation) => reservation_content == reservation.to_string(),
None => reservation_content == "0", None => reservation_content == "0",

View File

@ -15,7 +15,7 @@ pub struct Pids {}
impl Controller for Pids { impl Controller for Pids {
fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> { fn apply(controller_opt: &ControllerOpt, cgroup_root: &std::path::Path) -> Result<()> {
log::debug!("Apply pids cgroup v2 config"); log::debug!("Apply pids cgroup v2 config");
if let Some(pids) = &controller_opt.resources.pids { if let Some(pids) = &controller_opt.resources.pids() {
Self::apply(cgroup_root, pids).context("failed to apply pids resource restrictions")?; Self::apply(cgroup_root, pids).context("failed to apply pids resource restrictions")?;
} }
Ok(()) Ok(())
@ -32,8 +32,8 @@ impl StatsProvider for Pids {
impl Pids { impl Pids {
fn apply(root_path: &Path, pids: &LinuxPids) -> Result<()> { fn apply(root_path: &Path, pids: &LinuxPids) -> Result<()> {
let limit = if pids.limit > 0 { let limit = if pids.limit() > 0 {
pids.limit.to_string() pids.limit().to_string()
} else { } else {
"max".to_string() "max".to_string()
}; };
@ -45,7 +45,7 @@ impl Pids {
mod tests { mod tests {
use super::*; use super::*;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use oci_spec::runtime::LinuxPids; use oci_spec::runtime::LinuxPidsBuilder;
#[test] #[test]
fn test_set_pids() { fn test_set_pids() {
@ -53,12 +53,12 @@ mod tests {
let tmp = create_temp_dir("v2_test_set_pids").expect("create temp directory for test"); let tmp = create_temp_dir("v2_test_set_pids").expect("create temp directory for test");
set_fixture(&tmp, pids_file_name, "1000").expect("Set fixture for 1000 pids"); set_fixture(&tmp, pids_file_name, "1000").expect("Set fixture for 1000 pids");
let pids = LinuxPids { limit: 1000 }; let pids = LinuxPidsBuilder::default().limit(1000).build().unwrap();
Pids::apply(&tmp, &pids).expect("apply pids"); Pids::apply(&tmp, &pids).expect("apply pids");
let content = let content =
std::fs::read_to_string(tmp.join(pids_file_name)).expect("Read pids contents"); std::fs::read_to_string(tmp.join(pids_file_name)).expect("Read pids contents");
assert_eq!(pids.limit.to_string(), content); assert_eq!(pids.limit().to_string(), content);
} }
#[test] #[test]
@ -67,7 +67,7 @@ mod tests {
let tmp = create_temp_dir("v2_test_set_pids_max").expect("create temp directory for test"); let tmp = create_temp_dir("v2_test_set_pids_max").expect("create temp directory for test");
set_fixture(&tmp, pids_file_name, "0").expect("set fixture for 0 pids"); set_fixture(&tmp, pids_file_name, "0").expect("set fixture for 0 pids");
let pids = LinuxPids { limit: 0 }; let pids = LinuxPidsBuilder::default().limit(0).build().unwrap();
Pids::apply(&tmp, &pids).expect("apply pids"); Pids::apply(&tmp, &pids).expect("apply pids");

View File

@ -13,7 +13,7 @@ impl Unified {
cgroup_path: &Path, cgroup_path: &Path,
controllers: Vec<ControllerType>, controllers: Vec<ControllerType>,
) -> Result<()> { ) -> Result<()> {
if let Some(unified) = &controller_opt.resources.unified { if let Some(unified) = &controller_opt.resources.unified() {
log::debug!("Apply unified cgroup config"); log::debug!("Apply unified cgroup config");
for (cgroup_file, value) in unified { for (cgroup_file, value) in unified {
common::write_cgroup_file_str(cgroup_path.join(cgroup_file), value).map_err( common::write_cgroup_file_str(cgroup_path.join(cgroup_file), value).map_err(
@ -45,10 +45,10 @@ impl Unified {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::array::IntoIter; use std::collections::HashMap;
use std::fs; use std::fs;
use oci_spec::runtime::LinuxResources; use oci_spec::runtime::LinuxResourcesBuilder;
use crate::test::{create_temp_dir, set_fixture}; use crate::test::{create_temp_dir, set_fixture};
use crate::v2::controller_type::ControllerType; use crate::v2::controller_type::ControllerType;
@ -62,20 +62,21 @@ mod tests {
let hugetlb_limit_path = set_fixture(&tmp, "hugetlb.1GB.limit_in_bytes", "").unwrap(); let hugetlb_limit_path = set_fixture(&tmp, "hugetlb.1GB.limit_in_bytes", "").unwrap();
let cpu_weight_path = set_fixture(&tmp, "cpu.weight", "").unwrap(); let cpu_weight_path = set_fixture(&tmp, "cpu.weight", "").unwrap();
let resources = LinuxResources { let unified = {
unified: Some( let mut u = HashMap::new();
IntoIter::new([ u.insert(
( "hugetlb.1GB.limit_in_bytes".to_owned(),
"hugetlb.1GB.limit_in_bytes".to_owned(), "72348034".to_owned(),
"72348034".to_owned(), );
), u.insert("cpu.weight".to_owned(), "5000".to_owned());
("cpu.weight".to_owned(), "5000".to_owned()), u
])
.collect(),
),
..Default::default()
}; };
let resources = LinuxResourcesBuilder::default()
.unified(unified)
.build()
.unwrap();
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
resources: &resources, resources: &resources,
freezer_state: None, freezer_state: None,
@ -99,20 +100,21 @@ mod tests {
let tmp = let tmp =
create_temp_dir("test_set_unified_failed_to_write_subsystem_not_enabled").unwrap(); create_temp_dir("test_set_unified_failed_to_write_subsystem_not_enabled").unwrap();
let resources = LinuxResources { let unified = {
unified: Some( let mut u = HashMap::new();
IntoIter::new([ u.insert(
( "hugetlb.1GB.limit_in_bytes".to_owned(),
"hugetlb.1GB.limit_in_bytes".to_owned(), "72348034".to_owned(),
"72348034".to_owned(), );
), u.insert("cpu.weight".to_owned(), "5000".to_owned());
("cpu.weight".to_owned(), "5000".to_owned()), u
])
.collect(),
),
..Default::default()
}; };
let resources = LinuxResourcesBuilder::default()
.unified(unified)
.build()
.unwrap();
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
resources: &resources, resources: &resources,
freezer_state: None, freezer_state: None,
@ -132,20 +134,21 @@ mod tests {
// arrange // arrange
let tmp = create_temp_dir("test_set_unified_failed_to_write_subsystem_enabled").unwrap(); let tmp = create_temp_dir("test_set_unified_failed_to_write_subsystem_enabled").unwrap();
let resources = LinuxResources { let unified = {
unified: Some( let mut u = HashMap::new();
IntoIter::new([ u.insert(
( "hugetlb.1GB.limit_in_bytes".to_owned(),
"hugetlb.1GB.limit_in_bytes".to_owned(), "72348034".to_owned(),
"72348034".to_owned(), );
), u.insert("cpu.weight".to_owned(), "5000".to_owned());
("cpu.weight".to_owned(), "5000".to_owned()), u
])
.collect(),
),
..Default::default()
}; };
let resources = LinuxResourcesBuilder::default()
.unified(unified)
.build()
.unwrap();
let controller_opt = ControllerOpt { let controller_opt = ControllerOpt {
resources: &resources, resources: &resources,
oom_score_adj: None, oom_score_adj: None,

View File

@ -132,24 +132,24 @@ pub fn reset_effective<S: Syscall + ?Sized>(syscall: &S) -> Result<()> {
/// Drop any extra granted capabilities, and reset to defaults which are in oci specification /// Drop any extra granted capabilities, and reset to defaults which are in oci specification
pub fn drop_privileges<S: Syscall + ?Sized>(cs: &LinuxCapabilities, syscall: &S) -> Result<()> { pub fn drop_privileges<S: Syscall + ?Sized>(cs: &LinuxCapabilities, syscall: &S) -> Result<()> {
log::debug!("dropping bounding capabilities to {:?}", cs.bounding); log::debug!("dropping bounding capabilities to {:?}", cs.bounding());
if let Some(bounding) = cs.bounding.as_ref() { if let Some(bounding) = cs.bounding().as_ref() {
syscall.set_capability(CapSet::Bounding, &to_set(bounding))?; syscall.set_capability(CapSet::Bounding, &to_set(bounding))?;
} }
if let Some(effective) = cs.effective.as_ref() { if let Some(effective) = cs.effective().as_ref() {
syscall.set_capability(CapSet::Effective, &to_set(effective))?; syscall.set_capability(CapSet::Effective, &to_set(effective))?;
} }
if let Some(permitted) = cs.permitted.as_ref() { if let Some(permitted) = cs.permitted().as_ref() {
syscall.set_capability(CapSet::Permitted, &to_set(permitted))?; syscall.set_capability(CapSet::Permitted, &to_set(permitted))?;
} }
if let Some(inheritable) = cs.inheritable.as_ref() { if let Some(inheritable) = cs.inheritable().as_ref() {
syscall.set_capability(CapSet::Inheritable, &to_set(inheritable))?; syscall.set_capability(CapSet::Inheritable, &to_set(inheritable))?;
} }
if let Some(ambient) = cs.ambient.as_ref() { if let Some(ambient) = cs.ambient().as_ref() {
// check specifically for ambient, as those might not always be available // check specifically for ambient, as those might not always be available
if let Err(e) = syscall.set_capability(CapSet::Ambient, &to_set(ambient)) { if let Err(e) = syscall.set_capability(CapSet::Ambient, &to_set(ambient)) {
log::error!("failed to set ambient capabilities: {}", e); log::error!("failed to set ambient capabilities: {}", e);
@ -161,6 +161,9 @@ pub fn drop_privileges<S: Syscall + ?Sized>(cs: &LinuxCapabilities, syscall: &S)
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use oci_spec::runtime::LinuxCapabilitiesBuilder;
use std::collections::HashSet;
use super::*; use super::*;
use crate::syscall::test::TestHelperSyscall; use crate::syscall::test::TestHelperSyscall;
@ -555,13 +558,14 @@ mod tests {
let tests = vec![ let tests = vec![
Testcase { Testcase {
name: format!("all LinuxCapabilities fields with caps: {:?}", cps), name: format!("all LinuxCapabilities fields with caps: {:?}", cps),
input: LinuxCapabilities { input: LinuxCapabilitiesBuilder::default()
bounding: cps.clone().into_iter().collect::<Capabilities>().into(), .bounding(cps.clone().into_iter().collect::<Capabilities>())
effective: cps.clone().into_iter().collect::<Capabilities>().into(), .effective(cps.clone().into_iter().collect::<Capabilities>())
inheritable: cps.clone().into_iter().collect::<Capabilities>().into(), .inheritable(cps.clone().into_iter().collect::<Capabilities>())
permitted: cps.clone().into_iter().collect::<Capabilities>().into(), .permitted(cps.clone().into_iter().collect::<Capabilities>())
ambient: cps.clone().into_iter().collect::<Capabilities>().into(), .ambient(cps.clone().into_iter().collect::<Capabilities>())
}, .build()
.unwrap(),
want: vec![ want: vec![
(CapSet::Bounding, cps.clone()), (CapSet::Bounding, cps.clone()),
(CapSet::Effective, cps.clone()), (CapSet::Effective, cps.clone()),
@ -572,29 +576,37 @@ mod tests {
}, },
Testcase { Testcase {
name: format!("partial LinuxCapabilities fields with caps: {:?}", cps), name: format!("partial LinuxCapabilities fields with caps: {:?}", cps),
input: LinuxCapabilities { input: LinuxCapabilitiesBuilder::default()
bounding: cps.clone().into_iter().collect::<Capabilities>().into(), .bounding(cps.clone().into_iter().collect::<Capabilities>())
effective: cps.clone().into_iter().collect::<Capabilities>().into(), .effective(cps.clone().into_iter().collect::<Capabilities>())
inheritable: None, .permitted(cps.clone().into_iter().collect::<Capabilities>())
permitted: cps.clone().into_iter().collect::<Capabilities>().into(), .build()
ambient: None, .unwrap(),
},
want: vec![ want: vec![
(CapSet::Bounding, cps.clone()), (CapSet::Bounding, cps.clone()),
(CapSet::Effective, cps.clone()), (CapSet::Effective, cps.clone()),
(CapSet::Permitted, cps.clone()), (CapSet::Permitted, cps.clone()),
(CapSet::Inheritable, cps.clone()),
(CapSet::Ambient, cps.clone()),
], ],
}, },
Testcase { Testcase {
name: format!("empty LinuxCapabilities fields with caps: {:?}", cps), name: format!("empty LinuxCapabilities fields with caps: {:?}", cps),
input: LinuxCapabilities { input: LinuxCapabilitiesBuilder::default()
bounding: None, .bounding(HashSet::new())
effective: None, .effective(HashSet::new())
inheritable: None, .inheritable(HashSet::new())
permitted: None, .permitted(HashSet::new())
ambient: None, .ambient(HashSet::new())
}, .build()
want: vec![], .unwrap(),
want: vec![
(CapSet::Bounding, cps.clone()),
(CapSet::Effective, cps.clone()),
(CapSet::Permitted, cps.clone()),
(CapSet::Inheritable, cps.clone()),
(CapSet::Ambient, cps),
],
}, },
]; ];

View File

@ -29,7 +29,10 @@ impl Ps {
let spec = oci_spec::runtime::Spec::load(config_absolute_path)?; let spec = oci_spec::runtime::Spec::load(config_absolute_path)?;
log::debug!("spec: {:?}", spec); log::debug!("spec: {:?}", spec);
let cgroups_path = utils::get_cgroup_path( let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path, spec.linux()
.as_ref()
.context("no linux in spec")?
.cgroups_path(),
container.id(), container.id(),
); );
let systemd_cgroup = container let systemd_cgroup = container

View File

@ -48,14 +48,14 @@ impl<'a> ContainerBuilderImpl<'a> {
} }
fn run_container(&mut self) -> Result<()> { fn run_container(&mut self) -> Result<()> {
let linux = self.spec.linux.as_ref().context("no linux in spec")?; let linux = self.spec.linux().as_ref().context("no linux in spec")?;
let cgroups_path = utils::get_cgroup_path(&linux.cgroups_path, &self.container_id); let cgroups_path = utils::get_cgroup_path(linux.cgroups_path(), &self.container_id);
let cmanager = cgroups::common::create_cgroup_manager(&cgroups_path, self.use_systemd)?; let cmanager = cgroups::common::create_cgroup_manager(&cgroups_path, self.use_systemd)?;
let process = self.spec.process.as_ref().context("No process in spec")?; let process = self.spec.process().as_ref().context("No process in spec")?;
if self.init { if self.init {
if let Some(hooks) = self.spec.hooks.as_ref() { if let Some(hooks) = self.spec.hooks().as_ref() {
hooks::run_hooks(hooks.create_runtime.as_ref(), self.container.as_ref())? hooks::run_hooks(hooks.create_runtime().as_ref(), self.container.as_ref())?
} }
} }
@ -78,7 +78,7 @@ impl<'a> ContainerBuilderImpl<'a> {
// is not writeable unless you're an privileged user (if !dumpable is // is not writeable unless you're an privileged user (if !dumpable is
// set). All children inherit their parent's oom_score_adj value on // set). All children inherit their parent's oom_score_adj value on
// fork(2) so this will always be propagated properly. // fork(2) so this will always be propagated properly.
if let Some(oom_score_adj) = process.oom_score_adj { if let Some(oom_score_adj) = process.oom_score_adj() {
log::debug!("Set OOM score to {}", oom_score_adj); log::debug!("Set OOM score to {}", oom_score_adj);
let mut f = fs::File::create("/proc/self/oom_score_adj")?; let mut f = fs::File::create("/proc/self/oom_score_adj")?;
f.write_all(oom_score_adj.to_string().as_bytes())?; f.write_all(oom_score_adj.to_string().as_bytes())?;
@ -92,7 +92,7 @@ impl<'a> ContainerBuilderImpl<'a> {
// going to be switching to a different security context. Thus setting // going to be switching to a different security context. Thus setting
// ourselves to be non-dumpable only breaks things (like rootless // ourselves to be non-dumpable only breaks things (like rootless
// containers), which is the recommendation from the kernel folks. // containers), which is the recommendation from the kernel folks.
if linux.namespaces.is_some() { if linux.namespaces().is_some() {
prctl::set_dumpable(false).unwrap(); prctl::set_dumpable(false).unwrap();
} }
@ -147,8 +147,8 @@ impl<'a> ContainerBuilderImpl<'a> {
let init_pid = receiver_from_intermediate.wait_for_intermediate_ready()?; let init_pid = receiver_from_intermediate.wait_for_intermediate_ready()?;
log::debug!("init pid is {:?}", init_pid); log::debug!("init pid is {:?}", init_pid);
if self.rootless.is_none() && linux.resources.is_some() && self.init { if self.rootless.is_none() && linux.resources().is_some() && self.init {
if let Some(resources) = linux.resources.as_ref() { if let Some(resources) = linux.resources().as_ref() {
apply_cgroups(resources, init_pid, cmanager.as_ref())?; apply_cgroups(resources, init_pid, cmanager.as_ref())?;
} }
} }
@ -215,7 +215,7 @@ mod tests {
sched::{unshare, CloneFlags}, sched::{unshare, CloneFlags},
unistd::{self, getgid, getuid}, unistd::{self, getgid, getuid},
}; };
use oci_spec::runtime::LinuxIdMapping; use oci_spec::runtime::LinuxIdMappingBuilder;
use serial_test::serial; use serial_test::serial;
use crate::process::channel::{intermediate_to_main, main_to_intermediate}; use crate::process::channel::{intermediate_to_main, main_to_intermediate};
@ -225,11 +225,11 @@ mod tests {
#[test] #[test]
#[serial] #[serial]
fn setup_uid_mapping_should_succeed() -> Result<()> { fn setup_uid_mapping_should_succeed() -> Result<()> {
let uid_mapping = LinuxIdMapping { let uid_mapping = LinuxIdMappingBuilder::default()
host_id: u32::from(getuid()), .host_id(getuid())
container_id: 0, .container_id(0u32)
size: 1, .size(1u32)
}; .build()?;
let uid_mappings = vec![uid_mapping]; let uid_mappings = vec![uid_mapping];
let rootless = Rootless { let rootless = Rootless {
uid_mappings: Some(&uid_mappings), uid_mappings: Some(&uid_mappings),
@ -246,9 +246,9 @@ mod tests {
let line = fs::read_to_string(format!("/proc/{}/uid_map", child.as_raw()))?; let line = fs::read_to_string(format!("/proc/{}/uid_map", child.as_raw()))?;
let line_splited = line.split_whitespace(); let line_splited = line.split_whitespace();
for (act, expect) in line_splited.zip([ for (act, expect) in line_splited.zip([
uid_mapping.container_id.to_string().as_str(), uid_mapping.container_id().to_string().as_str(),
uid_mapping.host_id.to_string().as_str(), uid_mapping.host_id().to_string().as_str(),
uid_mapping.size.to_string().as_str(), uid_mapping.size().to_string().as_str(),
]) { ]) {
assert_eq!(act, expect); assert_eq!(act, expect);
} }
@ -271,11 +271,11 @@ mod tests {
#[test] #[test]
#[serial] #[serial]
fn setup_gid_mapping_should_successed() -> Result<()> { fn setup_gid_mapping_should_successed() -> Result<()> {
let gid_mapping = LinuxIdMapping { let gid_mapping = LinuxIdMappingBuilder::default()
host_id: u32::from(getgid()), .host_id(getgid())
container_id: 0, .container_id(0u32)
size: 1, .size(1u32)
}; .build()?;
let gid_mappings = vec![gid_mapping]; let gid_mappings = vec![gid_mapping];
let rootless = Rootless { let rootless = Rootless {
gid_mappings: Some(&gid_mappings), gid_mappings: Some(&gid_mappings),
@ -291,9 +291,9 @@ mod tests {
let line = fs::read_to_string(format!("/proc/{}/gid_map", child.as_raw()))?; let line = fs::read_to_string(format!("/proc/{}/gid_map", child.as_raw()))?;
let line_splited = line.split_whitespace(); let line_splited = line.split_whitespace();
for (act, expect) in line_splited.zip([ for (act, expect) in line_splited.zip([
gid_mapping.container_id.to_string().as_str(), gid_mapping.container_id().to_string().as_str(),
gid_mapping.host_id.to_string().as_str(), gid_mapping.host_id().to_string().as_str(),
gid_mapping.size.to_string().as_str(), gid_mapping.size().to_string().as_str(),
]) { ]) {
assert_eq!(act, expect); assert_eq!(act, expect);
} }

View File

@ -48,7 +48,10 @@ impl Container {
})?; })?;
let cgroups_path = utils::get_cgroup_path( let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path, spec.linux()
.as_ref()
.context("no linux in spec")?
.cgroups_path(),
self.id(), self.id(),
); );
@ -64,8 +67,8 @@ impl Container {
format!("failed to remove cgroup {}", cgroups_path.display()) format!("failed to remove cgroup {}", cgroups_path.display())
})?; })?;
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
hooks::run_hooks(hooks.poststop.as_ref(), Some(self)) hooks::run_hooks(hooks.poststop().as_ref(), Some(self))
.with_context(|| "failed to run post stop hooks")?; .with_context(|| "failed to run post stop hooks")?;
} }
} }

View File

@ -31,7 +31,11 @@ impl Container {
} }
let cgroups_path = utils::get_cgroup_path( let cgroups_path = utils::get_cgroup_path(
&self.spec()?.linux.context("no linux in spec")?.cgroups_path, self.spec()?
.linux()
.as_ref()
.context("no linux in spec")?
.cgroups_path(),
self.id(), self.id(),
); );
let use_systemd = self let use_systemd = self

View File

@ -36,7 +36,10 @@ impl Container {
let spec = self.spec()?; let spec = self.spec()?;
let cgroups_path = utils::get_cgroup_path( let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path, spec.linux()
.as_ref()
.context("no linux in spec")?
.cgroups_path(),
self.id(), self.id(),
); );

View File

@ -38,7 +38,10 @@ impl Container {
let spec = self.spec()?; let spec = self.spec()?;
let cgroups_path = utils::get_cgroup_path( let cgroups_path = utils::get_cgroup_path(
&spec.linux.context("no linux in spec")?.cgroups_path, spec.linux()
.as_ref()
.context("no linux in spec")?
.cgroups_path(),
self.id(), self.id(),
); );

View File

@ -42,11 +42,11 @@ impl Container {
let spec = self let spec = self
.spec() .spec()
.with_context(|| format!("failed to load runtime spec for container {}", self.id()))?; .with_context(|| format!("failed to load runtime spec for container {}", self.id()))?;
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
// While prestart is marked as deprecated in the OCI spec, the docker and integration test still // While prestart is marked as deprecated in the OCI spec, the docker and integration test still
// uses it. // uses it.
#[allow(deprecated)] #[allow(deprecated)]
hooks::run_hooks(hooks.prestart.as_ref(), Some(self)) hooks::run_hooks(hooks.prestart().as_ref(), Some(self))
.with_context(|| "failed to run pre start hooks")?; .with_context(|| "failed to run pre start hooks")?;
} }
@ -60,8 +60,8 @@ impl Container {
// Run post start hooks. It runs after the container process is started. // Run post start hooks. It runs after the container process is started.
// It is called in the runtime namespace. // It is called in the runtime namespace.
if let Some(hooks) = spec.hooks.as_ref() { if let Some(hooks) = spec.hooks().as_ref() {
hooks::run_hooks(hooks.poststart.as_ref(), Some(self)) hooks::run_hooks(hooks.poststart().as_ref(), Some(self))
.with_context(|| "failed to run post start hooks")?; .with_context(|| "failed to run post start hooks")?;
} }

View File

@ -46,12 +46,12 @@ impl<'a> InitContainerBuilder<'a> {
let mut container = self.create_container_state(&container_dir)?; let mut container = self.create_container_state(&container_dir)?;
container container
.set_systemd(self.use_systemd) .set_systemd(self.use_systemd)
.set_annotations(spec.annotations.clone()); .set_annotations(spec.annotations().clone());
unistd::chdir(&*container_dir)?; unistd::chdir(&*container_dir)?;
let notify_path = container_dir.join(NOTIFY_FILE); let notify_path = container_dir.join(NOTIFY_FILE);
// convert path of root file system of the container to absolute path // convert path of root file system of the container to absolute path
let rootfs = fs::canonicalize(&spec.root.as_ref().context("no root in spec")?.path)?; let rootfs = fs::canonicalize(&spec.root().as_ref().context("no root in spec")?.path())?;
// if socket file path is given in commandline options, // if socket file path is given in commandline options,
// get file descriptors of console socket // get file descriptors of console socket
@ -109,15 +109,15 @@ impl<'a> InitContainerBuilder<'a> {
} }
fn validate_spec(spec: &Spec) -> Result<()> { fn validate_spec(spec: &Spec) -> Result<()> {
if !spec.version.starts_with("1.0") { if !spec.version().starts_with("1.0") {
bail!( bail!(
"runtime spec has incompatible version '{}'. Only 1.0.X is supported", "runtime spec has incompatible version '{}'. Only 1.0.X is supported",
spec.version spec.version()
); );
} }
if let Some(process) = &spec.process { if let Some(process) = &spec.process() {
if let Some(profile) = &process.apparmor_profile { if let Some(profile) = &process.apparmor_profile() {
if !apparmor::is_enabled()? { if !apparmor::is_enabled()? {
bail!( bail!(
"apparmor profile {} is specified in runtime spec, \ "apparmor profile {} is specified in runtime spec, \

View File

@ -2,8 +2,9 @@ use anyhow::{bail, Context, Result};
use caps::Capability; use caps::Capability;
use nix::unistd; use nix::unistd;
use oci_spec::runtime::{ use oci_spec::runtime::{
Capabilities as SpecCapabilities, Capability as SpecCapability, LinuxCapabilities, Capabilities as SpecCapabilities, Capability as SpecCapability, LinuxBuilder,
LinuxNamespace, LinuxNamespaceType, Process, Spec, LinuxCapabilities, LinuxCapabilitiesBuilder, LinuxNamespace, LinuxNamespaceBuilder,
LinuxNamespaceType, Process, ProcessBuilder, Spec, SpecBuilder,
}; };
use procfs::process::Namespace; use procfs::process::Namespace;
@ -90,14 +91,16 @@ impl<'a> TenantContainerBuilder<'a> {
pub fn build(self) -> Result<()> { pub fn build(self) -> Result<()> {
let container_dir = self.lookup_container_dir()?; let container_dir = self.lookup_container_dir()?;
let container = self.load_container_state(container_dir.clone())?; let container = self.load_container_state(container_dir.clone())?;
let mut spec = self.load_init_spec(&container_dir)?;
self.adapt_spec_for_tenant(&mut spec, &container)?; let spec = self.load_init_spec(&container_dir)?;
let spec = self.adapt_spec_for_tenant(&spec, &container)?;
log::debug!("{:#?}", spec); log::debug!("{:#?}", spec);
unistd::chdir(&*container_dir)?; unistd::chdir(&*container_dir)?;
let notify_path = Self::setup_notify_listener(&container_dir)?; let notify_path = Self::setup_notify_listener(&container_dir)?;
// convert path of root file system of the container to absolute path // convert path of root file system of the container to absolute path
let rootfs = fs::canonicalize(&spec.root.as_ref().context("no root in spec")?.path)?; let rootfs = fs::canonicalize(&spec.root().as_ref().context("no root in spec")?.path())?;
// if socket file path is given in commandline options, // if socket file path is given in commandline options,
// get file descriptors of console socket // get file descriptors of console socket
@ -156,28 +159,72 @@ impl<'a> TenantContainerBuilder<'a> {
Ok(container) Ok(container)
} }
fn adapt_spec_for_tenant(&self, spec: &mut Spec, container: &Container) -> Result<()> { fn adapt_spec_for_tenant(&self, spec: &Spec, container: &Container) -> Result<Spec> {
if let Some(ref process) = self.process { let process = if let Some(ref process) = self.process {
self.set_process(spec, process)?; self.set_process(process)?
} else { } else {
self.set_working_dir(spec)?; let mut process_builder = ProcessBuilder::default();
self.set_args(spec)?;
self.set_environment(spec)?; process_builder = match self.set_working_dir()? {
self.set_no_new_privileges(spec)?; Some(cwd) => process_builder.cwd(cwd),
self.set_capabilities(spec)?; None => process_builder,
} };
process_builder = process_builder.args(self.set_args()?);
process_builder = process_builder.env(self.set_environment()?);
process_builder = match self.set_no_new_privileges() {
Some(no_new_priv) => process_builder.no_new_privileges(no_new_priv),
None => process_builder,
};
process_builder = match self.set_capabilities(spec)? {
Some(caps) => process_builder.capabilities(caps),
None => process_builder,
};
process_builder.build()?
};
if container.pid().is_none() { if container.pid().is_none() {
bail!("Could not retrieve container init pid"); bail!("Could not retrieve container init pid");
} }
let init_process = procfs::process::Process::new(container.pid().unwrap().as_raw())?; let init_process = procfs::process::Process::new(container.pid().unwrap().as_raw())?;
self.set_namespaces(spec, init_process.namespaces()?)?; let ns = self.set_namespaces(init_process.namespaces()?)?;
let linux = LinuxBuilder::default().namespaces(ns).build()?;
Ok(()) let mut spec_builder = SpecBuilder::default()
.process(process)
.version(spec.version())
.linux(linux);
spec_builder = match spec.root() {
Some(root) => spec_builder.root(root.clone()),
None => spec_builder,
};
spec_builder = match spec.mounts() {
Some(mounts) => spec_builder.mounts(mounts.clone()),
None => spec_builder,
};
spec_builder = match spec.hostname() {
Some(hostname) => spec_builder.hostname(hostname.clone()),
None => spec_builder,
};
spec_builder = match spec.hooks() {
Some(hooks) => spec_builder.hooks(hooks.clone()),
None => spec_builder,
};
spec_builder = match spec.annotations() {
Some(annotations) => spec_builder.annotations(annotations.clone()),
None => spec_builder,
};
let spec = spec_builder.build()?;
Ok(spec)
} }
fn set_process(&self, spec: &mut Spec, process: &Path) -> Result<()> { fn set_process(&self, process: &Path) -> Result<Process> {
if !process.exists() { if !process.exists() {
bail!( bail!(
"Process.json file does not exist at specified path {}", "Process.json file does not exist at specified path {}",
@ -186,12 +233,11 @@ impl<'a> TenantContainerBuilder<'a> {
} }
let process = utils::open(process)?; let process = utils::open(process)?;
let process_spec: Process = serde_json::from_reader(process)?; let process_spec = serde_json::from_reader(process)?;
spec.process = Some(process_spec); Ok(process_spec)
Ok(())
} }
fn set_working_dir(&self, spec: &mut Spec) -> Result<()> { fn set_working_dir(&self) -> Result<Option<PathBuf>> {
if let Some(ref cwd) = self.cwd { if let Some(ref cwd) = self.cwd {
if cwd.is_relative() { if cwd.is_relative() {
bail!( bail!(
@ -199,51 +245,32 @@ impl<'a> TenantContainerBuilder<'a> {
cwd.display() cwd.display()
); );
} }
return Ok(Some(cwd.into()));
spec.process.as_mut().context("no process in spec")?.cwd = cwd.to_path_buf();
} }
Ok(None)
Ok(())
} }
fn set_args(&self, spec: &mut Spec) -> Result<()> { fn set_args(&self) -> Result<Vec<String>> {
if self.args.is_empty() { if self.args.is_empty() {
bail!("Container command was not specified") bail!("Container command was not specified")
} }
spec.process.as_mut().context("no process in spec")?.args = self.args.clone().into(); Ok(self.args.clone())
Ok(())
} }
fn set_environment(&self, spec: &mut Spec) -> Result<()> { fn set_environment(&self) -> Result<Vec<String>> {
spec.process Ok(self
.as_mut()
.context("no process in spec")?
.env .env
.as_mut() .iter()
.context("no env in process spec")? .map(|(k, v)| format!("{}={}", k, v))
.append( .collect())
&mut self
.env
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect(),
);
Ok(())
} }
fn set_no_new_privileges(&self, spec: &mut Spec) -> Result<()> { fn set_no_new_privileges(&self) -> Option<bool> {
if let Some(no_new_privs) = self.no_new_privs { self.no_new_privs
spec.process
.as_mut()
.context("no process in spec")?
.no_new_privileges = no_new_privs.into();
}
Ok(())
} }
fn set_capabilities(&self, spec: &mut Spec) -> Result<()> { fn set_capabilities(&self, spec: &Spec) -> Result<Option<LinuxCapabilities>> {
if !self.capabilities.is_empty() { if !self.capabilities.is_empty() {
let mut caps: Vec<Capability> = Vec::with_capacity(self.capabilities.len()); let mut caps: Vec<Capability> = Vec::with_capacity(self.capabilities.len());
for cap in &self.capabilities { for cap in &self.capabilities {
@ -253,70 +280,84 @@ impl<'a> TenantContainerBuilder<'a> {
let caps: SpecCapabilities = let caps: SpecCapabilities =
caps.iter().map(|c| SpecCapability::from_cap(*c)).collect(); caps.iter().map(|c| SpecCapability::from_cap(*c)).collect();
if let Some(ref mut spec_caps) = spec if let Some(ref spec_caps) = spec
.process .process()
.as_mut() .as_ref()
.context("no process in spec")? .context("no process in spec")?
.capabilities .capabilities()
{ {
spec_caps let mut capabilities_builder = LinuxCapabilitiesBuilder::default();
.ambient capabilities_builder = match spec_caps.ambient() {
.as_mut() Some(ambient) => {
.context("no ambient caps in process spec")? let ambient: SpecCapabilities = ambient.union(&caps).copied().collect();
.extend(&caps); capabilities_builder.ambient(ambient)
spec_caps }
.bounding None => capabilities_builder,
.as_mut() };
.context("no bounding caps in process spec")? capabilities_builder = match spec_caps.bounding() {
.extend(&caps); Some(bounding) => {
spec_caps let bounding: SpecCapabilities = bounding.union(&caps).copied().collect();
.effective capabilities_builder.bounding(bounding)
.as_mut() }
.context("no effective caps in process spec")? None => capabilities_builder,
.extend(&caps); };
spec_caps capabilities_builder = match spec_caps.effective() {
.inheritable Some(effective) => {
.as_mut() let effective: SpecCapabilities = effective.union(&caps).copied().collect();
.context("no inheritable caps in process spec")? capabilities_builder.effective(effective)
.extend(&caps); }
spec_caps None => capabilities_builder,
.permitted };
.as_mut() capabilities_builder = match spec_caps.inheritable() {
.context("no permitted caps in process spec")? Some(inheritable) => {
.extend(&caps); let inheritable: SpecCapabilities =
} else { inheritable.union(&caps).copied().collect();
spec.process capabilities_builder.inheritable(inheritable)
.as_mut() }
.context("no process in spec")? None => capabilities_builder,
.capabilities = Some(LinuxCapabilities { };
ambient: caps.clone().into(), capabilities_builder = match spec_caps.permitted() {
bounding: caps.clone().into(), Some(permitted) => {
effective: caps.clone().into(), let permitted: SpecCapabilities = permitted.union(&caps).copied().collect();
inheritable: caps.clone().into(), capabilities_builder.permitted(permitted)
permitted: caps.into(), }
}) None => capabilities_builder,
};
let c = capabilities_builder.build()?;
return Ok(Some(c));
} }
return Ok(Some(
LinuxCapabilitiesBuilder::default()
.bounding(caps.clone())
.effective(caps.clone())
.inheritable(caps.clone())
.permitted(caps.clone())
.ambient(caps)
.build()?,
));
} }
Ok(()) Ok(None)
} }
fn set_namespaces(&self, spec: &mut Spec, init_namespaces: Vec<Namespace>) -> Result<()> { fn set_namespaces(&self, init_namespaces: Vec<Namespace>) -> Result<Vec<LinuxNamespace>> {
let mut tenant_namespaces = Vec::with_capacity(init_namespaces.len()); let mut tenant_namespaces = Vec::with_capacity(init_namespaces.len());
for ns_type in NAMESPACE_TYPES.iter().copied() { for ns_type in NAMESPACE_TYPES.iter().copied() {
if let Some(init_ns) = init_namespaces.iter().find(|n| n.ns_type.eq(ns_type)) { if let Some(init_ns) = init_namespaces.iter().find(|n| n.ns_type.eq(ns_type)) {
let tenant_ns = LinuxNamespaceType::try_from(ns_type)?; let tenant_ns = LinuxNamespaceType::try_from(ns_type)?;
tenant_namespaces.push(LinuxNamespace { tenant_namespaces.push(
typ: tenant_ns, LinuxNamespaceBuilder::default()
path: Some(init_ns.path.clone()), .typ(tenant_ns)
}) .path(init_ns.path.clone())
.build()?,
)
} }
} }
let mut linux = &mut spec.linux.as_mut().context("no linux in spec")?; Ok(tenant_namespaces)
linux.namespaces = tenant_namespaces.into();
Ok(())
} }
fn should_use_systemd(&self, container: &Container) -> bool { fn should_use_systemd(&self, container: &Container) -> bool {

View File

@ -27,21 +27,21 @@ pub fn run_hooks(hooks: Option<&Vec<Hook>>, container: Option<&Container>) -> Re
if let Some(hooks) = hooks { if let Some(hooks) = hooks {
for hook in hooks { for hook in hooks {
let mut hook_command = process::Command::new(&hook.path); let mut hook_command = process::Command::new(&hook.path());
// Based on OCI spec, the first arguement of the args vector is the // Based on OCI spec, the first arguement of the args vector is the
// arg0, which can be different from the path. For example, path // arg0, which can be different from the path. For example, path
// may be "/usr/bin/true" and arg0 is set to "true". However, rust // may be "/usr/bin/true" and arg0 is set to "true". However, rust
// command differenciates arg0 from args, where rust command arg // command differenciates arg0 from args, where rust command arg
// doesn't include arg0. So we have to make the split arg0 from the // doesn't include arg0. So we have to make the split arg0 from the
// rest of args. // rest of args.
if let Some((arg0, args)) = hook.args.as_ref().map(|a| a.split_first()).flatten() { if let Some((arg0, args)) = hook.args().as_ref().map(|a| a.split_first()).flatten() {
log::debug!("run_hooks arg0: {:?}, args: {:?}", arg0, args); log::debug!("run_hooks arg0: {:?}, args: {:?}", arg0, args);
hook_command.arg0(arg0).args(args) hook_command.arg0(arg0).args(args)
} else { } else {
hook_command.arg0(&hook.path.as_path().display().to_string()) hook_command.arg0(&hook.path().as_path().display().to_string())
}; };
let envs: HashMap<String, String> = if let Some(env) = hook.env.as_ref() { let envs: HashMap<String, String> = if let Some(env) = hook.env().as_ref() {
utils::parse_env(env) utils::parse_env(env)
} else { } else {
HashMap::new() HashMap::new()
@ -78,7 +78,7 @@ pub fn run_hooks(hooks: Option<&Vec<Hook>>, container: Option<&Container>) -> Re
} }
} }
let res = if let Some(timeout_sec) = hook.timeout { let res = if let Some(timeout_sec) = hook.timeout() {
// Rust does not make it easy to handle executing a command and // Rust does not make it easy to handle executing a command and
// timeout. Here we decided to wait for the command in a // timeout. Here we decided to wait for the command in a
// different thread, so the main thread is not blocked. We use a // different thread, so the main thread is not blocked. We use a
@ -137,8 +137,9 @@ pub fn run_hooks(hooks: Option<&Vec<Hook>>, container: Option<&Container>) -> Re
mod test { mod test {
use super::*; use super::*;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use oci_spec::runtime::HookBuilder;
use serial_test::serial; use serial_test::serial;
use std::{env, fs, path::PathBuf}; use std::{env, fs};
fn is_command_in_path(program: &str) -> bool { fn is_command_in_path(program: &str) -> bool {
if let Ok(path) = env::var("PATH") { if let Ok(path) = env::var("PATH") {
@ -170,12 +171,8 @@ mod test {
{ {
assert!(is_command_in_path("true"), "The true was not found."); assert!(is_command_in_path("true"), "The true was not found.");
let default_container: Container = Default::default(); let default_container: Container = Default::default();
let hook = Hook {
path: PathBuf::from("true"), let hook = HookBuilder::default().path("true").build()?;
args: None,
env: None,
timeout: None,
};
let hooks = Some(vec![hook]); let hooks = Some(vec![hook]);
run_hooks(hooks.as_ref(), Some(&default_container)).context("Failed true")?; run_hooks(hooks.as_ref(), Some(&default_container)).context("Failed true")?;
} }
@ -187,16 +184,15 @@ mod test {
); );
// Use `printenv` to make sure the environment is set correctly. // Use `printenv` to make sure the environment is set correctly.
let default_container: Container = Default::default(); let default_container: Container = Default::default();
let hook = Hook { let hook = HookBuilder::default()
path: PathBuf::from("bash"), .path("bash")
args: Some(vec![ .args(vec![
String::from("bash"), String::from("bash"),
String::from("-c"), String::from("-c"),
String::from("printenv key > /dev/null"), String::from("printenv key > /dev/null"),
]), ])
env: Some(vec![String::from("key=value")]), .env(vec![String::from("key=value")])
timeout: None, .build()?;
};
let hooks = Some(vec![hook]); let hooks = Some(vec![hook]);
run_hooks(hooks.as_ref(), Some(&default_container)).context("Failed printenv test")?; run_hooks(hooks.as_ref(), Some(&default_container)).context("Failed printenv test")?;
} }
@ -211,16 +207,15 @@ mod test {
fn test_run_hook_timeout() -> Result<()> { fn test_run_hook_timeout() -> Result<()> {
let default_container: Container = Default::default(); let default_container: Container = Default::default();
// We use `tail -f /dev/null` here to simulate a hook command that hangs. // We use `tail -f /dev/null` here to simulate a hook command that hangs.
let hook = Hook { let hook = HookBuilder::default()
path: PathBuf::from("tail"), .path("tail")
args: Some(vec![ .args(vec![
String::from("tail"), String::from("tail"),
String::from("-f"), String::from("-f"),
String::from("/dev/null"), String::from("/dev/null"),
]), ])
env: None, .timeout(1)
timeout: Some(1), .build()?;
};
let hooks = Some(vec![hook]); let hooks = Some(vec![hook]);
match run_hooks(hooks.as_ref(), Some(&default_container)) { match run_hooks(hooks.as_ref(), Some(&default_container)) {
Ok(_) => { Ok(_) => {

View File

@ -37,7 +37,7 @@ impl From<Option<&Vec<LinuxNamespace>>> for Namespaces {
let namespace_map: collections::HashMap<CloneFlags, LinuxNamespace> = namespaces let namespace_map: collections::HashMap<CloneFlags, LinuxNamespace> = namespaces
.unwrap_or(&vec![]) .unwrap_or(&vec![])
.iter() .iter()
.map(|ns| (get_clone_flag(ns.typ), ns.clone())) .map(|ns| (get_clone_flag(ns.typ()), ns.clone()))
.collect(); .collect();
Namespaces { Namespaces {
@ -63,14 +63,14 @@ impl Namespaces {
pub fn unshare_or_setns(&self, namespace: &LinuxNamespace) -> Result<()> { pub fn unshare_or_setns(&self, namespace: &LinuxNamespace) -> Result<()> {
log::debug!("unshare or setns: {:?}", namespace); log::debug!("unshare or setns: {:?}", namespace);
if namespace.path.is_none() { if namespace.path().is_none() {
self.command.unshare(get_clone_flag(namespace.typ))?; self.command.unshare(get_clone_flag(namespace.typ()))?;
} else { } else {
let ns_path = namespace.path.as_ref().unwrap(); let ns_path = namespace.path().as_ref().unwrap();
let fd = fcntl::open(ns_path, fcntl::OFlag::empty(), stat::Mode::empty()) let fd = fcntl::open(ns_path, fcntl::OFlag::empty(), stat::Mode::empty())
.with_context(|| format!("Failed to open namespace fd: {:?}", ns_path))?; .with_context(|| format!("Failed to open namespace fd: {:?}", ns_path))?;
self.command self.command
.set_ns(fd, get_clone_flag(namespace.typ)) .set_ns(fd, get_clone_flag(namespace.typ()))
.with_context(|| "Failed to set namespace")?; .with_context(|| "Failed to set namespace")?;
unistd::close(fd).with_context(|| "Failed to close namespace fd")?; unistd::close(fd).with_context(|| "Failed to close namespace fd")?;
} }
@ -87,31 +87,33 @@ impl Namespaces {
mod tests { mod tests {
use super::*; use super::*;
use crate::syscall::test::TestHelperSyscall; use crate::syscall::test::TestHelperSyscall;
use oci_spec::runtime::LinuxNamespaceType; use oci_spec::runtime::{LinuxNamespaceBuilder, LinuxNamespaceType};
use serial_test::serial; use serial_test::serial;
fn gen_sample_linux_namespaces() -> Vec<LinuxNamespace> { fn gen_sample_linux_namespaces() -> Vec<LinuxNamespace> {
vec![ vec![
LinuxNamespace { LinuxNamespaceBuilder::default()
typ: LinuxNamespaceType::Mount, .typ(LinuxNamespaceType::Mount)
path: Some("/dev/null".into()), .path("/dev/null")
}, .build()
LinuxNamespace { .unwrap(),
typ: LinuxNamespaceType::Network, LinuxNamespaceBuilder::default()
path: Some("/dev/null".into()), .typ(LinuxNamespaceType::Network)
}, .path("/dev/null")
LinuxNamespace { .build()
typ: LinuxNamespaceType::Pid, .unwrap(),
path: None, LinuxNamespaceBuilder::default()
}, .typ(LinuxNamespaceType::Pid)
LinuxNamespace { .build()
typ: LinuxNamespaceType::User, .unwrap(),
path: None, LinuxNamespaceBuilder::default()
}, .typ(LinuxNamespaceType::User)
LinuxNamespace { .build()
typ: LinuxNamespaceType::Ipc, .unwrap(),
path: None, LinuxNamespaceBuilder::default()
}, .typ(LinuxNamespaceType::Ipc)
.build()
.unwrap(),
] ]
} }

View File

@ -164,13 +164,13 @@ pub fn container_init(
) -> Result<()> { ) -> Result<()> {
let command = args.syscall; let command = args.syscall;
let spec = &args.spec; let spec = &args.spec;
let linux = spec.linux.as_ref().context("no linux in spec")?; let linux = spec.linux().as_ref().context("no linux in spec")?;
let proc = spec.process.as_ref().context("no process in spec")?; let proc = spec.process().as_ref().context("no process in spec")?;
let mut envs: Vec<String> = proc.env.as_ref().unwrap_or(&vec![]).clone(); let mut envs: Vec<String> = proc.env().as_ref().unwrap_or(&vec![]).clone();
let rootfs = &args.rootfs; let rootfs = &args.rootfs;
let hooks = spec.hooks.as_ref(); let hooks = spec.hooks().as_ref();
let container = args.container.as_ref(); let container = args.container.as_ref();
let namespaces = Namespaces::from(linux.namespaces.as_ref()); let namespaces = Namespaces::from(linux.namespaces().as_ref());
// set up tty if specified // set up tty if specified
if let Some(csocketfd) = args.console_socket { if let Some(csocketfd) = args.console_socket {
@ -196,14 +196,14 @@ pub fn container_init(
// Only set the host name if entering into a new uts namespace // Only set the host name if entering into a new uts namespace
if let Some(uts_namespace) = namespaces.get(LinuxNamespaceType::Uts) { if let Some(uts_namespace) = namespaces.get(LinuxNamespaceType::Uts) {
if uts_namespace.path.is_none() { if uts_namespace.path().is_none() {
if let Some(hostname) = spec.hostname.as_ref() { if let Some(hostname) = spec.hostname().as_ref() {
command.set_hostname(hostname)?; command.set_hostname(hostname)?;
} }
} }
} }
if let Some(true) = proc.no_new_privileges { if let Some(true) = proc.no_new_privileges() {
let _ = prctl::set_no_new_privileges(true); let _ = prctl::set_no_new_privileges(true);
} }
@ -211,7 +211,7 @@ pub fn container_init(
// create_container hook needs to be called after the namespace setup, but // create_container hook needs to be called after the namespace setup, but
// before pivot_root is called. This runs in the container namespaces. // before pivot_root is called. This runs in the container namespaces.
if let Some(hooks) = hooks { if let Some(hooks) = hooks {
hooks::run_hooks(hooks.create_container.as_ref(), container) hooks::run_hooks(hooks.create_container().as_ref(), container)
.context("Failed to run create container hooks")?; .context("Failed to run create container hooks")?;
} }
@ -237,18 +237,18 @@ pub fn container_init(
rootfs::adjust_root_mount_propagation(linux) rootfs::adjust_root_mount_propagation(linux)
.context("Failed to set propagation type of root mount")?; .context("Failed to set propagation type of root mount")?;
if let Some(kernel_params) = &linux.sysctl { if let Some(kernel_params) = &linux.sysctl() {
sysctl(kernel_params) sysctl(kernel_params)
.with_context(|| format!("Failed to sysctl: {:?}", kernel_params))?; .with_context(|| format!("Failed to sysctl: {:?}", kernel_params))?;
} }
} }
if let Some(profile) = &proc.apparmor_profile { if let Some(profile) = &proc.apparmor_profile() {
apparmor::apply_profile(profile) apparmor::apply_profile(profile)
.with_context(|| format!("failed to apply apparmor profile {}", profile))?; .with_context(|| format!("failed to apply apparmor profile {}", profile))?;
} }
if let Some(true) = spec.root.as_ref().map(|r| r.readonly.unwrap_or(false)) { if let Some(true) = spec.root().as_ref().map(|r| r.readonly().unwrap_or(false)) {
nix_mount( nix_mount(
None::<&str>, None::<&str>,
"/", "/",
@ -258,51 +258,54 @@ pub fn container_init(
)? )?
} }
if let Some(paths) = &linux.readonly_paths { if let Some(paths) = &linux.readonly_paths() {
// mount readonly path // mount readonly path
for path in paths { for path in paths {
readonly_path(path).context("Failed to set read only path")?; readonly_path(path).context("Failed to set read only path")?;
} }
} }
if let Some(paths) = &linux.masked_paths { if let Some(paths) = &linux.masked_paths() {
// mount masked path // mount masked path
for path in paths { for path in paths {
masked_path(path, &linux.mount_label).context("Failed to set masked path")?; masked_path(path, linux.mount_label()).context("Failed to set masked path")?;
} }
} }
let cwd = format!("{}", proc.cwd.display()); let cwd = format!("{}", proc.cwd().display());
let do_chdir = if cwd.is_empty() { let do_chdir = if cwd.is_empty() {
false false
} else { } else {
// This chdir must run before setting up the user. // This chdir must run before setting up the user.
// This may allow the user running youki to access directories // This may allow the user running youki to access directories
// that the container user cannot access. // that the container user cannot access.
match unistd::chdir(&*proc.cwd) { match unistd::chdir(&*proc.cwd()) {
Ok(_) => false, Ok(_) => false,
Err(nix::Error::EPERM) => true, Err(nix::Error::EPERM) => true,
Err(e) => bail!("Failed to chdir: {}", e), Err(e) => bail!("Failed to chdir: {}", e),
} }
}; };
set_supplementary_gids(&proc.user, &args.rootless) set_supplementary_gids(proc.user(), &args.rootless)
.context("failed to set supplementary gids")?; .context("failed to set supplementary gids")?;
command command
.set_id(Uid::from_raw(proc.user.uid), Gid::from_raw(proc.user.gid)) .set_id(
Uid::from_raw(proc.user().uid()),
Gid::from_raw(proc.user().gid()),
)
.context("Failed to configure uid and gid")?; .context("Failed to configure uid and gid")?;
// Without no new privileges, seccomp is a privileged operation. We have to // Without no new privileges, seccomp is a privileged operation. We have to
// do this before dropping capabilities. Otherwise, we should do it later, // do this before dropping capabilities. Otherwise, we should do it later,
// as close to exec as possible. // as close to exec as possible.
if linux.seccomp.is_some() && proc.no_new_privileges.is_none() { if linux.seccomp().is_some() && proc.no_new_privileges().is_none() {
seccomp::initialize_seccomp(linux.seccomp.as_ref().unwrap()) seccomp::initialize_seccomp(linux.seccomp().as_ref().unwrap())
.context("Failed to execute seccomp")?; .context("Failed to execute seccomp")?;
} }
capabilities::reset_effective(command).context("Failed to reset effective capabilities")?; capabilities::reset_effective(command).context("Failed to reset effective capabilities")?;
if let Some(caps) = &proc.capabilities { if let Some(caps) = &proc.capabilities() {
capabilities::drop_privileges(caps, command).context("Failed to drop capabilities")?; capabilities::drop_privileges(caps, command).context("Failed to drop capabilities")?;
} }
@ -351,8 +354,8 @@ pub fn container_init(
// change directory to process.cwd if process.cwd is not empty // change directory to process.cwd if process.cwd is not empty
if do_chdir { if do_chdir {
unistd::chdir(&*proc.cwd) unistd::chdir(&*proc.cwd())
.with_context(|| format!("Failed to chdir {}", proc.cwd.display()))?; .with_context(|| format!("Failed to chdir {}", proc.cwd().display()))?;
} }
// Reset the process env based on oci spec. // Reset the process env based on oci spec.
@ -375,18 +378,18 @@ pub fn container_init(
// before pivot_root is called. This runs in the container namespaces. // before pivot_root is called. This runs in the container namespaces.
if args.init { if args.init {
if let Some(hooks) = hooks { if let Some(hooks) = hooks {
hooks::run_hooks(hooks.start_container.as_ref(), container)? hooks::run_hooks(hooks.start_container().as_ref(), container)?
} }
} }
if linux.seccomp.is_some() && proc.no_new_privileges.is_some() { if linux.seccomp().is_some() && proc.no_new_privileges().is_some() {
// Initialize seccomp profile right before we are ready to execute the // Initialize seccomp profile right before we are ready to execute the
// payload. The notify socket will still need network related syscalls. // payload. The notify socket will still need network related syscalls.
seccomp::initialize_seccomp(linux.seccomp.as_ref().unwrap()) seccomp::initialize_seccomp(linux.seccomp().as_ref().unwrap())
.context("Failed to execute seccomp")?; .context("Failed to execute seccomp")?;
} }
if let Some(args) = proc.args.as_ref() { if let Some(args) = proc.args().as_ref() {
utils::do_exec(&args[0], args)?; utils::do_exec(&args[0], args)?;
} else { } else {
bail!("On non-Windows, at least one process arg entry is required.") bail!("On non-Windows, at least one process arg entry is required.")
@ -422,7 +425,7 @@ pub fn container_init(
// Privileged user starting a normal container: Just add the supplementary groups. // Privileged user starting a normal container: Just add the supplementary groups.
// //
fn set_supplementary_gids(user: &User, rootless: &Option<Rootless>) -> Result<()> { fn set_supplementary_gids(user: &User, rootless: &Option<Rootless>) -> Result<()> {
if let Some(additional_gids) = &user.additional_gids { if let Some(additional_gids) = &user.additional_gids() {
if additional_gids.is_empty() { if additional_gids.is_empty() {
return Ok(()); return Ok(());
} }

View File

@ -13,8 +13,8 @@ pub fn container_intermediate(
) -> Result<()> { ) -> Result<()> {
let command = &args.syscall; let command = &args.syscall;
let spec = &args.spec; let spec = &args.spec;
let linux = spec.linux.as_ref().context("no linux in spec")?; let linux = spec.linux().as_ref().context("no linux in spec")?;
let namespaces = Namespaces::from(linux.namespaces.as_ref()); let namespaces = Namespaces::from(linux.namespaces().as_ref());
// if new user is specified in specification, this will be true and new // if new user is specified in specification, this will be true and new
// namespace will be created, check // namespace will be created, check
@ -24,7 +24,7 @@ pub fn container_intermediate(
namespaces namespaces
.unshare_or_setns(user_namespace) .unshare_or_setns(user_namespace)
.with_context(|| format!("Failed to enter pid namespace: {:?}", user_namespace))?; .with_context(|| format!("Failed to enter pid namespace: {:?}", user_namespace))?;
if user_namespace.path.is_none() { if user_namespace.path().is_none() {
log::debug!("creating new user namespace"); log::debug!("creating new user namespace");
// child needs to be dumpable, otherwise the non root parent is not // child needs to be dumpable, otherwise the non root parent is not
// allowed to write the uid/gid maps // allowed to write the uid/gid maps
@ -46,8 +46,8 @@ pub fn container_intermediate(
} }
// set limits and namespaces to the process // set limits and namespaces to the process
let proc = spec.process.as_ref().context("no process in spec")?; let proc = spec.process().as_ref().context("no process in spec")?;
if let Some(rlimits) = proc.rlimits.as_ref() { if let Some(rlimits) = proc.rlimits().as_ref() {
for rlimit in rlimits.iter() { for rlimit in rlimits.iter() {
command.set_rlimit(rlimit).context("failed to set rlimit")?; command.set_rlimit(rlimit).context("failed to set rlimit")?;
} }

View File

@ -12,7 +12,7 @@ use nix::sys::stat::{Mode, SFlag};
use nix::unistd::{chown, close}; use nix::unistd::{chown, close};
use nix::unistd::{Gid, Uid}; use nix::unistd::{Gid, Uid};
use nix::NixPath; use nix::NixPath;
use oci_spec::runtime::{Linux, LinuxDevice, LinuxDeviceType, Mount, Spec}; use oci_spec::runtime::{Linux, LinuxDevice, LinuxDeviceBuilder, LinuxDeviceType, Mount, Spec};
use procfs::process::{MountInfo, MountOptFields, Process}; use procfs::process::{MountInfo, MountOptFields, Process};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::fs::{canonicalize, create_dir_all, remove_file}; use std::fs::{canonicalize, create_dir_all, remove_file};
@ -22,8 +22,8 @@ use std::path::{Path, PathBuf};
pub fn prepare_rootfs(spec: &Spec, rootfs: &Path, bind_devices: bool) -> Result<()> { pub fn prepare_rootfs(spec: &Spec, rootfs: &Path, bind_devices: bool) -> Result<()> {
log::debug!("Prepare rootfs: {:?}", rootfs); log::debug!("Prepare rootfs: {:?}", rootfs);
let mut flags = MsFlags::MS_REC; let mut flags = MsFlags::MS_REC;
let linux = spec.linux.as_ref().context("no linux in spec")?; let linux = spec.linux().as_ref().context("no linux in spec")?;
if let Some(roofs_propagation) = linux.rootfs_propagation.as_ref() { if let Some(roofs_propagation) = linux.rootfs_propagation().as_ref() {
match roofs_propagation.as_str() { match roofs_propagation.as_str() {
"shared" => flags |= MsFlags::MS_SHARED, "shared" => flags |= MsFlags::MS_SHARED,
"private" => flags |= MsFlags::MS_PRIVATE, "private" => flags |= MsFlags::MS_PRIVATE,
@ -49,15 +49,15 @@ pub fn prepare_rootfs(spec: &Spec, rootfs: &Path, bind_devices: bool) -> Result<
None::<&str>, None::<&str>,
)?; )?;
if let Some(mounts) = spec.mounts.as_ref() { if let Some(mounts) = spec.mounts().as_ref() {
for mount in mounts.iter() { for mount in mounts.iter() {
log::debug!("Mount... {:?}", mount); log::debug!("Mount... {:?}", mount);
let (flags, data) = parse_mount(mount); let (flags, data) = parse_mount(mount);
let mount_label = linux.mount_label.as_ref(); let mount_label = linux.mount_label().as_ref();
if mount.typ == Some("cgroup".to_string()) { if *mount.typ() == Some("cgroup".to_string()) {
// skip // skip
log::warn!("A feature of cgroup is unimplemented."); log::warn!("A feature of cgroup is unimplemented.");
} else if mount.destination == PathBuf::from("/dev") { } else if *mount.destination() == PathBuf::from("/dev") {
mount_to_container( mount_to_container(
mount, mount,
rootfs, rootfs,
@ -74,7 +74,7 @@ pub fn prepare_rootfs(spec: &Spec, rootfs: &Path, bind_devices: bool) -> Result<
} }
setup_default_symlinks(rootfs).context("Failed to setup default symlinks")?; setup_default_symlinks(rootfs).context("Failed to setup default symlinks")?;
if let Some(added_devices) = linux.devices.as_ref() { if let Some(added_devices) = linux.devices().as_ref() {
create_devices( create_devices(
rootfs, rootfs,
default_devices().iter().chain(added_devices), default_devices().iter().chain(added_devices),
@ -119,60 +119,54 @@ fn setup_default_symlinks(rootfs: &Path) -> Result<()> {
pub fn default_devices() -> Vec<LinuxDevice> { pub fn default_devices() -> Vec<LinuxDevice> {
vec![ vec![
LinuxDevice { LinuxDeviceBuilder::default()
path: PathBuf::from("/dev/null"), .path(PathBuf::from("/dev/null"))
typ: LinuxDeviceType::C, .typ(LinuxDeviceType::C)
major: 1, .major(1)
minor: 3, .minor(3)
file_mode: Some(0o066), .file_mode(0o066u32)
uid: None, .build()
gid: None, .unwrap(),
}, LinuxDeviceBuilder::default()
LinuxDevice { .path(PathBuf::from("/dev/zero"))
path: PathBuf::from("/dev/zero"), .typ(LinuxDeviceType::C)
typ: LinuxDeviceType::C, .major(1)
major: 1, .minor(5)
minor: 5, .file_mode(0o066u32)
file_mode: Some(0o066), .build()
uid: None, .unwrap(),
gid: None, LinuxDeviceBuilder::default()
}, .path(PathBuf::from("/dev/full"))
LinuxDevice { .typ(LinuxDeviceType::C)
path: PathBuf::from("/dev/full"), .major(1)
typ: LinuxDeviceType::C, .minor(7)
major: 1, .file_mode(0o066u32)
minor: 7, .build()
file_mode: Some(0o066), .unwrap(),
uid: None, LinuxDeviceBuilder::default()
gid: None, .path(PathBuf::from("/dev/tty"))
}, .typ(LinuxDeviceType::C)
LinuxDevice { .major(5)
path: PathBuf::from("/dev/tty"), .minor(0)
typ: LinuxDeviceType::C, .file_mode(0o066u32)
major: 5, .build()
minor: 0, .unwrap(),
file_mode: Some(0o066), LinuxDeviceBuilder::default()
uid: None, .path(PathBuf::from("/dev/urandom"))
gid: None, .typ(LinuxDeviceType::C)
}, .major(1)
LinuxDevice { .minor(9)
path: PathBuf::from("/dev/urandom"), .file_mode(0o066u32)
typ: LinuxDeviceType::C, .build()
major: 1, .unwrap(),
minor: 9, LinuxDeviceBuilder::default()
file_mode: Some(0o066), .path(PathBuf::from("/dev/random"))
uid: None, .typ(LinuxDeviceType::C)
gid: None, .major(1)
}, .minor(8)
LinuxDevice { .file_mode(0o066u32)
path: PathBuf::from("/dev/random"), .build()
typ: LinuxDeviceType::C, .unwrap(),
major: 1,
minor: 8,
file_mode: Some(0o066),
uid: None,
gid: None,
},
] ]
} }
@ -184,8 +178,8 @@ where
if bind { if bind {
let _ = devices let _ = devices
.map(|dev| { .map(|dev| {
if !dev.path.starts_with("/dev") { if !dev.path().starts_with("/dev") {
panic!("{} is not a valid device path", dev.path.display()); panic!("{} is not a valid device path", dev.path().display());
} }
bind_dev(rootfs, dev) bind_dev(rootfs, dev)
@ -194,8 +188,8 @@ where
} else { } else {
devices devices
.map(|dev| { .map(|dev| {
if !dev.path.starts_with("/dev") { if !dev.path().starts_with("/dev") {
panic!("{} is not a valid device path", dev.path.display()); panic!("{} is not a valid device path", dev.path().display());
} }
mknod_dev(rootfs, dev) mknod_dev(rootfs, dev)
@ -208,7 +202,7 @@ where
} }
fn bind_dev(rootfs: &Path, dev: &LinuxDevice) -> Result<()> { fn bind_dev(rootfs: &Path, dev: &LinuxDevice) -> Result<()> {
let full_container_path = rootfs.join(dev.path.as_in_container()?); let full_container_path = rootfs.join(dev.path().as_in_container()?);
let fd = open( let fd = open(
&full_container_path, &full_container_path,
@ -217,7 +211,7 @@ fn bind_dev(rootfs: &Path, dev: &LinuxDevice) -> Result<()> {
)?; )?;
close(fd)?; close(fd)?;
nix_mount( nix_mount(
Some(&dev.path), Some(dev.path()),
&full_container_path, &full_container_path,
Some("bind"), Some("bind"),
MsFlags::MS_BIND, MsFlags::MS_BIND,
@ -244,17 +238,17 @@ fn mknod_dev(rootfs: &Path, dev: &LinuxDevice) -> Result<()> {
| ((major & !0xfff) << 32)) as u64 | ((major & !0xfff) << 32)) as u64
} }
let full_container_path = rootfs.join(dev.path.as_in_container()?); let full_container_path = rootfs.join(dev.path().as_in_container()?);
mknod( mknod(
&full_container_path, &full_container_path,
to_sflag(dev.typ), to_sflag(dev.typ()),
Mode::from_bits_truncate(dev.file_mode.unwrap_or(0)), Mode::from_bits_truncate(dev.file_mode().unwrap_or(0)),
makedev(dev.major, dev.minor), makedev(dev.major(), dev.minor()),
)?; )?;
chown( chown(
&full_container_path, &full_container_path,
dev.uid.map(Uid::from_raw), dev.uid().map(Uid::from_raw),
dev.gid.map(Gid::from_raw), dev.gid().map(Gid::from_raw),
)?; )?;
Ok(()) Ok(())
@ -267,7 +261,7 @@ fn mount_to_container(
data: &str, data: &str,
label: Option<&String>, label: Option<&String>,
) -> Result<()> { ) -> Result<()> {
let typ = m.typ.as_deref(); let typ = m.typ().as_deref();
let d = if let Some(l) = label { let d = if let Some(l) = label {
if typ != Some("proc") && typ != Some("sysfs") { if typ != Some("proc") && typ != Some("sysfs") {
if data.is_empty() { if data.is_empty() {
@ -284,10 +278,10 @@ fn mount_to_container(
let dest_for_host = format!( let dest_for_host = format!(
"{}{}", "{}{}",
rootfs.to_string_lossy().into_owned(), rootfs.to_string_lossy().into_owned(),
m.destination.display() m.destination().display()
); );
let dest = Path::new(&dest_for_host); let dest = Path::new(&dest_for_host);
let source = m.source.as_ref().context("no source in mount spec")?; let source = m.source().as_ref().context("no source in mount spec")?;
let src = if typ == Some("bind") { let src = if typ == Some("bind") {
let src = canonicalize(source)?; let src = canonicalize(source)?;
let dir = if src.is_file() { let dir = if src.is_file() {
@ -313,7 +307,7 @@ fn mount_to_container(
if let Err(errno) = nix_mount(Some(&*src), dest, typ, flags, Some(&*d)) { if let Err(errno) = nix_mount(Some(&*src), dest, typ, flags, Some(&*d)) {
if !matches!(errno, Errno::EINVAL) { if !matches!(errno, Errno::EINVAL) {
bail!("mount of {:?} failed", m.destination); bail!("mount of {:?} failed", m.destination());
} }
nix_mount(Some(&*src), dest, typ, flags, Some(data))?; nix_mount(Some(&*src), dest, typ, flags, Some(data))?;
} }
@ -342,7 +336,7 @@ fn mount_to_container(
fn parse_mount(m: &Mount) -> (MsFlags, String) { fn parse_mount(m: &Mount) -> (MsFlags, String) {
let mut flags = MsFlags::empty(); let mut flags = MsFlags::empty();
let mut data = Vec::new(); let mut data = Vec::new();
if let Some(options) = &m.options { if let Some(options) = &m.options() {
for s in options { for s in options {
if let Some((is_clear, flag)) = match s.as_str() { if let Some((is_clear, flag)) = match s.as_str() {
"defaults" => Some((false, MsFlags::empty())), "defaults" => Some((false, MsFlags::empty())),
@ -430,7 +424,7 @@ fn make_parent_mount_private(rootfs: &Path) -> Result<()> {
/// Change propagation type of rootfs as specified in spec. /// Change propagation type of rootfs as specified in spec.
pub fn adjust_root_mount_propagation(linux: &Linux) -> Result<()> { pub fn adjust_root_mount_propagation(linux: &Linux) -> Result<()> {
let rootfs_propagation = linux.rootfs_propagation.as_deref(); let rootfs_propagation = linux.rootfs_propagation().as_deref();
let flags = match rootfs_propagation { let flags = match rootfs_propagation {
Some("shared") => Some(MsFlags::MS_SHARED), Some("shared") => Some(MsFlags::MS_SHARED),
Some("unbindable") => Some(MsFlags::MS_UNBINDABLE), Some("unbindable") => Some(MsFlags::MS_UNBINDABLE),

View File

@ -24,8 +24,8 @@ pub struct Rootless<'a> {
impl<'a> Rootless<'a> { impl<'a> Rootless<'a> {
pub fn new(spec: &'a Spec) -> Result<Option<Rootless<'a>>> { pub fn new(spec: &'a Spec) -> Result<Option<Rootless<'a>>> {
let linux = spec.linux.as_ref().context("no linux in spec")?; let linux = spec.linux().as_ref().context("no linux in spec")?;
let namespaces = Namespaces::from(linux.namespaces.as_ref()); let namespaces = Namespaces::from(linux.namespaces().as_ref());
let user_namespace = namespaces.get(LinuxNamespaceType::User); let user_namespace = namespaces.get(LinuxNamespaceType::User);
// If conditions requires us to use rootless, we must either create a new // If conditions requires us to use rootless, we must either create a new
@ -34,7 +34,7 @@ impl<'a> Rootless<'a> {
bail!("rootless container requires valid user namespace definition"); bail!("rootless container requires valid user namespace definition");
} }
if user_namespace.is_some() && user_namespace.unwrap().path.is_none() { if user_namespace.is_some() && user_namespace.unwrap().path().is_none() {
log::debug!("rootless container should be created"); log::debug!("rootless container should be created");
log::warn!( log::warn!(
"resource constraints and multi id mapping is unimplemented for rootless containers" "resource constraints and multi id mapping is unimplemented for rootless containers"
@ -83,13 +83,13 @@ impl<'a> Rootless<'a> {
impl<'a> From<&'a Linux> for Rootless<'a> { impl<'a> From<&'a Linux> for Rootless<'a> {
fn from(linux: &'a Linux) -> Self { fn from(linux: &'a Linux) -> Self {
let namespaces = Namespaces::from(linux.namespaces.as_ref()); let namespaces = Namespaces::from(linux.namespaces().as_ref());
let user_namespace = namespaces.get(LinuxNamespaceType::User); let user_namespace = namespaces.get(LinuxNamespaceType::User);
Self { Self {
newuidmap: None, newuidmap: None,
newgidmap: None, newgidmap: None,
uid_mappings: linux.uid_mappings.as_ref(), uid_mappings: linux.uid_mappings().as_ref(),
gid_mappings: linux.gid_mappings.as_ref(), gid_mappings: linux.gid_mappings().as_ref(),
user_namespace: user_namespace.cloned(), user_namespace: user_namespace.cloned(),
privileged: nix::unistd::geteuid().is_root(), privileged: nix::unistd::geteuid().is_root(),
} }
@ -112,18 +112,18 @@ pub fn rootless_required() -> bool {
/// Validates that the spec contains the required information for /// Validates that the spec contains the required information for
/// running in rootless mode /// running in rootless mode
fn validate(spec: &Spec) -> Result<()> { fn validate(spec: &Spec) -> Result<()> {
let linux = spec.linux.as_ref().context("no linux in spec")?; let linux = spec.linux().as_ref().context("no linux in spec")?;
let namespaces = Namespaces::from(linux.namespaces.as_ref()); let namespaces = Namespaces::from(linux.namespaces().as_ref());
if namespaces.get(LinuxNamespaceType::User).is_none() { if namespaces.get(LinuxNamespaceType::User).is_none() {
bail!("rootless containers require the specification of a user namespace"); bail!("rootless containers require the specification of a user namespace");
} }
let gid_mappings = linux let gid_mappings = linux
.gid_mappings .gid_mappings()
.as_ref() .as_ref()
.context("rootless containers require gidMappings in spec")?; .context("rootless containers require gidMappings in spec")?;
let uid_mappings = linux let uid_mappings = linux
.uid_mappings .uid_mappings()
.as_ref() .as_ref()
.context("rootless containers require uidMappings in spec")?; .context("rootless containers require uidMappings in spec")?;
@ -136,13 +136,13 @@ fn validate(spec: &Spec) -> Result<()> {
} }
validate_mounts( validate_mounts(
spec.mounts.as_ref().context("no mounts in spec")?, spec.mounts().as_ref().context("no mounts in spec")?,
uid_mappings, uid_mappings,
gid_mappings, gid_mappings,
)?; )?;
if let Some(process) = &spec.process { if let Some(process) = &spec.process() {
if let Some(additional_gids) = &process.user.additional_gids { if let Some(additional_gids) = &process.user().additional_gids() {
let privileged = nix::unistd::geteuid().is_root(); let privileged = nix::unistd::geteuid().is_root();
match (privileged, additional_gids.is_empty()) { match (privileged, additional_gids.is_empty()) {
@ -174,7 +174,7 @@ fn validate_mounts(
gid_mappings: &[LinuxIdMapping], gid_mappings: &[LinuxIdMapping],
) -> Result<()> { ) -> Result<()> {
for mount in mounts { for mount in mounts {
if let Some(options) = &mount.options { if let Some(options) = &mount.options() {
for opt in options { for opt in options {
if opt.starts_with("uid=") && !is_id_mapped(opt[4..].parse()?, uid_mappings) { if opt.starts_with("uid=") && !is_id_mapped(opt[4..].parse()?, uid_mappings) {
bail!("Mount {:?} specifies option {} which is not mapped inside the rootless container", mount, opt); bail!("Mount {:?} specifies option {} which is not mapped inside the rootless container", mount, opt);
@ -193,13 +193,13 @@ fn validate_mounts(
fn is_id_mapped(id: u32, mappings: &[LinuxIdMapping]) -> bool { fn is_id_mapped(id: u32, mappings: &[LinuxIdMapping]) -> bool {
mappings mappings
.iter() .iter()
.any(|m| id >= m.container_id && id <= m.container_id + m.size) .any(|m| id >= m.container_id() && id <= m.container_id() + m.size())
} }
/// Looks up the location of the newuidmap and newgidmap binaries which /// Looks up the location of the newuidmap and newgidmap binaries which
/// are required to write multiple user/group mappings /// are required to write multiple user/group mappings
pub fn lookup_map_binaries(spec: &Linux) -> Result<Option<(PathBuf, PathBuf)>> { pub fn lookup_map_binaries(spec: &Linux) -> Result<Option<(PathBuf, PathBuf)>> {
if let Some(uid_mappings) = spec.uid_mappings.as_ref() { if let Some(uid_mappings) = spec.uid_mappings().as_ref() {
if uid_mappings.len() == 1 && uid_mappings.len() == 1 { if uid_mappings.len() == 1 && uid_mappings.len() == 1 {
return Ok(None); return Ok(None);
} }
@ -231,7 +231,7 @@ fn write_id_mapping(
) -> Result<()> { ) -> Result<()> {
let mappings: Vec<String> = mappings let mappings: Vec<String> = mappings
.iter() .iter()
.map(|m| format!("{} {} {}", m.container_id, m.host_id, m.size)) .map(|m| format!("{} {} {}", m.container_id(), m.host_id(), m.size()))
.collect(); .collect();
log::debug!("Write ID mapping: {:?}", mappings); log::debug!("Write ID mapping: {:?}", mappings);
if mappings.len() == 1 { if mappings.len() == 1 {

View File

@ -201,16 +201,16 @@ fn translate_arch(arch: &Arch) -> scmp_arch {
} }
pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<()> { pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<()> {
if seccomp.flags.is_some() { if seccomp.flags().is_some() {
// runc did not support this, so let's skip it for now. // runc did not support this, so let's skip it for now.
bail!("seccomp flags are not yet supported"); bail!("seccomp flags are not yet supported");
} }
// TODO: fix default action error number. The spec repo doesn't have it yet. // TODO: fix default action error number. The spec repo doesn't have it yet.
let default_action = translate_action(&seccomp.default_action, None); let default_action = translate_action(&seccomp.default_action(), None);
let mut ctx = FilterContext::default(default_action)?; let mut ctx = FilterContext::default(default_action)?;
if let Some(architectures) = seccomp.architectures.as_ref() { if let Some(architectures) = seccomp.architectures().as_ref() {
for arch in architectures { for arch in architectures {
let arch_token = translate_arch(arch); let arch_token = translate_arch(arch);
ctx.add_arch(arch_token as u32) ctx.add_arch(arch_token as u32)
@ -233,9 +233,9 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<()> {
); );
} }
if let Some(syscalls) = seccomp.syscalls.as_ref() { if let Some(syscalls) = seccomp.syscalls().as_ref() {
for syscall in syscalls { for syscall in syscalls {
let action = translate_action(&syscall.action, syscall.errno_ret); let action = translate_action(&syscall.action(), syscall.errno_ret());
if action == default_action { if action == default_action {
// When the action is the same as the default action, the rule is redundent. We can // When the action is the same as the default action, the rule is redundent. We can
// skip this here to avoid failing when we add the rules. // skip this here to avoid failing when we add the rules.
@ -246,7 +246,7 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<()> {
continue; continue;
} }
for name in &syscall.names { for name in syscall.names().iter() {
let syscall_number = match translate_syscall(name) { let syscall_number = match translate_syscall(name) {
Ok(x) => x, Ok(x) => x,
Err(_) => { Err(_) => {
@ -262,14 +262,14 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<()> {
// Not clear why but if there are multiple arg attached to one // Not clear why but if there are multiple arg attached to one
// syscall rule, we have to add them seperatly. add_rule will // syscall rule, we have to add them seperatly. add_rule will
// return EINVAL. runc does the same but doesn't explain why. // return EINVAL. runc does the same but doesn't explain why.
match syscall.args.as_ref() { match syscall.args().as_ref() {
Some(args) => { Some(args) => {
for arg in args { for arg in args {
let mut rule = Rule::new(action, syscall_number); let mut rule = Rule::new(action, syscall_number);
let cmp = Compare::new(arg.index as u32) let cmp = Compare::new(arg.index() as u32)
.op(translate_op(&arg.op)) .op(translate_op(&arg.op()))
.datum_a(arg.value) .datum_a(arg.value())
.datum_b(arg.value_two.unwrap_or(0)) .datum_b(arg.value_two().unwrap_or(0))
.build() .build()
.context("Failed to build a seccomp compare rule")?; .context("Failed to build a seccomp compare rule")?;
rule.add_comparator(cmp); rule.add_comparator(cmp);
@ -310,7 +310,8 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use mio::unix::pipe; use mio::unix::pipe;
use nix::sys::wait; use nix::sys::wait;
use oci_spec::runtime::{Arch, LinuxSeccomp, LinuxSyscall}; use oci_spec::runtime::Arch;
use oci_spec::runtime::{LinuxSeccompBuilder, LinuxSyscallBuilder};
use serial_test::serial; use serial_test::serial;
use std::io::Read; use std::io::Read;
use std::io::Write; use std::io::Write;
@ -332,17 +333,16 @@ mod tests {
// we can make sure that getcwd failed because of seccomp rule. // we can make sure that getcwd failed because of seccomp rule.
let expect_error = libc::EAGAIN; let expect_error = libc::EAGAIN;
let seccomp_profile = LinuxSeccomp { let syscall = LinuxSyscallBuilder::default()
default_action: LinuxSeccompAction::ScmpActAllow, .names(vec![String::from("getcwd")])
architectures: Some(vec![Arch::ScmpArchNative]), .action(LinuxSeccompAction::ScmpActErrno)
flags: None, .errno_ret(expect_error as u32)
syscalls: Some(vec![LinuxSyscall { .build()?;
names: vec![String::from("getcwd")], let seccomp_profile = LinuxSeccompBuilder::default()
action: LinuxSeccompAction::ScmpActErrno, .default_action(LinuxSeccompAction::ScmpActAllow)
errno_ret: Some(expect_error as u32), .architectures(vec![Arch::ScmpArchNative])
args: None, .syscalls(vec![syscall])
}]), .build()?;
};
// Since Rust cargo test uses a single process to execute all tests, it // Since Rust cargo test uses a single process to execute all tests, it
// is a good idea to fork a child process to test the seccomp profile, // is a good idea to fork a child process to test the seccomp profile,
@ -391,7 +391,7 @@ mod tests {
.context("Failed to load test spec for seccomp")?; .context("Failed to load test spec for seccomp")?;
// We know linux and seccomp exist, so let's just unwrap. // We know linux and seccomp exist, so let's just unwrap.
let seccomp_profile = spec.linux.unwrap().seccomp.unwrap(); let seccomp_profile = spec.linux().as_ref().unwrap().seccomp().as_ref().unwrap();
match unsafe { nix::unistd::fork()? } { match unsafe { nix::unistd::fork()? } {
nix::unistd::ForkResult::Parent { child } => { nix::unistd::ForkResult::Parent { child } => {
let status = wait::waitpid(child, None)?; let status = wait::waitpid(child, None)?;
@ -409,7 +409,7 @@ mod tests {
} }
nix::unistd::ForkResult::Child => { nix::unistd::ForkResult::Child => {
let _ = prctl::set_no_new_privileges(true); let _ = prctl::set_no_new_privileges(true);
let ret = initialize_seccomp(&seccomp_profile); let ret = initialize_seccomp(seccomp_profile);
let exit_code = if ret.is_ok() { 0 } else { -1 }; let exit_code = if ret.is_ok() { 0 } else { -1 };
std::process::exit(exit_code); std::process::exit(exit_code);
} }

View File

@ -162,12 +162,12 @@ impl Syscall for LinuxSyscall {
/// Sets resource limit for process /// Sets resource limit for process
fn set_rlimit(&self, rlimit: &LinuxRlimit) -> Result<()> { fn set_rlimit(&self, rlimit: &LinuxRlimit) -> Result<()> {
let rlim = &libc::rlimit { let rlim = &libc::rlimit {
rlim_cur: rlimit.soft, rlim_cur: rlimit.soft(),
rlim_max: rlimit.hard, rlim_max: rlimit.hard(),
}; };
let res = unsafe { libc::setrlimit(rlimit.typ as u32, rlim) }; let res = unsafe { libc::setrlimit(rlimit.typ() as u32, rlim) };
if let Err(e) = Errno::result(res).map(drop) { if let Err(e) = Errno::result(res).map(drop) {
bail!("Failed to set {:?}. {:?}", rlimit.typ, e) bail!("Failed to set {:?}. {:?}", rlimit.typ(), e)
} }
Ok(()) Ok(())
} }