1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-10 01:26:14 +02:00

Merge pull request #34 from tsturzl/main

cgroup v1 networking
This commit is contained in:
utam0k 2021-05-26 09:41:14 +09:00 committed by GitHub
commit e264d20d8b
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 282 additions and 27 deletions

View File

@ -45,10 +45,10 @@ jobs:
expect_err_num=8 expect_err_num=8
act_err_num=0 act_err_num=0
cd $(go env GOPATH)/src/github.com/opencontainers/runtime-tools cd $(go env GOPATH)/src/github.com/opencontainers/runtime-tools
test_cases=("default/default.t" "linux_cgroups_devices/linux_cgroups_devices.t" "linux_cgroups_hugetlb/linux_cgroups_hugetlb.t" "linux_cgroups_pids/linux_cgroups_pids.t", "linux_cgroups_memory/linux_cgroups_memory.t") test_cases=("default/default.t" "linux_cgroups_devices/linux_cgroups_devices.t" "linux_cgroups_hugetlb/linux_cgroups_hugetlb.t" "linux_cgroups_pids/linux_cgroups_pids.t", "linux_cgroups_memory/linux_cgroups_memory.t", "linux_cgroups_network/linux_cgroups_network.t")
for case in "${test_cases[@]}"; do for case in "${test_cases[@]}"; do
title="Running $case" title="Running $case"
if [ 0 -ne $(sudo RUNTIME=$GITHUB_WORKSPACE/target/x86_64-unknown-linux-gnu/debug/youki ./validation/$case | grep "not ok" | wc -l) ]; then if [ 0 -ne $(sudo RUST_BACKTRACE=1 RUNTIME=$GITHUB_WORKSPACE/target/x86_64-unknown-linux-gnu/debug/youki ./validation/$case | grep "not ok" | wc -l) ]; then
exit 1 exit 1
fi fi
done done

View File

@ -6,6 +6,8 @@ pub enum ControllerType {
Pids, Pids,
Memory, Memory,
Blkio, Blkio,
NetworkPriority,
NetworkClassifier,
} }
impl ToString for ControllerType { impl ToString for ControllerType {
@ -16,6 +18,8 @@ impl ToString for ControllerType {
Self::Pids => "pids".into(), Self::Pids => "pids".into(),
Self::Memory => "memory".into(), Self::Memory => "memory".into(),
Self::Blkio => "blkio".into(), Self::Blkio => "blkio".into(),
Self::NetworkPriority => "net_prio".into(),
Self::NetworkClassifier => "net_cls".into(),
} }
} }
} }

View File

@ -37,6 +37,7 @@ pub struct Devices {}
impl Controller for Devices { impl Controller for Devices {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> { fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply Devices cgroup config");
create_dir_all(&cgroup_root)?; create_dir_all(&cgroup_root)?;
for d in &linux_resources.devices { for d in &linux_resources.devices {

View File

@ -1,14 +1,26 @@
use std::{fs::{self, OpenOptions}, io::Write, path::Path}; use std::{
fs::{self, OpenOptions},
io::Write,
path::Path,
};
use regex::Regex;
use anyhow::anyhow; use anyhow::anyhow;
use regex::Regex;
use crate::{cgroups::Controller, spec::{ LinuxHugepageLimit, LinuxResources}}; use crate::{
cgroups::Controller,
spec::{LinuxHugepageLimit, LinuxResources},
};
pub struct Hugetlb {} pub struct Hugetlb {}
impl Controller for Hugetlb { impl Controller for Hugetlb {
fn apply(linux_resources: &LinuxResources, cgroup_root: &std::path::Path, pid: nix::unistd::Pid) -> anyhow::Result<()> { fn apply(
linux_resources: &LinuxResources,
cgroup_root: &std::path::Path,
pid: nix::unistd::Pid,
) -> anyhow::Result<()> {
log::debug!("Apply Hugetlb cgroup config");
fs::create_dir_all(cgroup_root)?; fs::create_dir_all(cgroup_root)?;
for hugetlb in &linux_resources.hugepage_limits { for hugetlb in &linux_resources.hugepage_limits {
@ -32,18 +44,21 @@ impl Hugetlb {
match caps { match caps {
None => return Err(anyhow!("page size must be in the format [0-9]+[KMG]B")), None => return Err(anyhow!("page size must be in the format [0-9]+[KMG]B")),
Some(caps) => { Some(caps) => {
let page_size:u64 = caps["pagesize"].parse()?; let page_size: u64 = caps["pagesize"].parse()?;
if !Self::is_power_of_two(page_size) { if !Self::is_power_of_two(page_size) {
return Err(anyhow!("page size must be in the format of 2^(integer)")) return Err(anyhow!("page size must be in the format of 2^(integer)"));
} }
} }
} }
Self::write_file(&root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)), &hugetlb.limit.to_string())?; Self::write_file(
&root_path.join(format!("hugetlb.{}.limit_in_bytes", hugetlb.page_size)),
&hugetlb.limit.to_string(),
)?;
Ok(()) Ok(())
} }
fn write_file(file_path: &Path, data: &str) -> anyhow::Result<()> { fn write_file(file_path: &Path, data: &str) -> anyhow::Result<()> {
fs::OpenOptions::new() fs::OpenOptions::new()
.create(false) .create(false)
.write(true) .write(true)
@ -55,7 +70,7 @@ impl Hugetlb {
} }
fn is_power_of_two(number: u64) -> bool { fn is_power_of_two(number: u64) -> bool {
(number != 0) && (number & (number -1)) == 0 (number != 0) && (number & (number - 1)) == 0
} }
} }
@ -91,16 +106,17 @@ mod tests {
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimit {
page_size: "2MB".to_owned(), page_size: "2MB".to_owned(),
limit: 16384, limit: 16384,
}; };
Hugetlb::apply(&tmp, &hugetlb).expect("apply hugetlb"); Hugetlb::apply(&tmp, &hugetlb).expect("apply hugetlb");
let content = std::fs::read_to_string(tmp.join(page_file_name)).expect("Read hugetlb file content"); let content =
std::fs::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]
fn test_set_hugetlb_with_invalid_page_size() { fn 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"); let tmp = create_temp_dir("test_set_hugetlb_with_invalid_page_size")
.expect("create temp directory for test");
let hugetlb = LinuxHugepageLimit { let hugetlb = LinuxHugepageLimit {
page_size: "3MB".to_owned(), page_size: "3MB".to_owned(),
@ -108,6 +124,9 @@ mod tests {
}; };
let result = Hugetlb::apply(&tmp, &hugetlb); let result = Hugetlb::apply(&tmp, &hugetlb);
assert!(result.is_err(), "page size that is not a power of two should be an error"); assert!(
result.is_err(),
"page size that is not a power of two should be an error"
);
} }
} }

View File

@ -7,7 +7,11 @@ use procfs::process::Process;
use crate::{cgroups::ControllerType, spec::LinuxResources, utils::PathBufExt}; use crate::{cgroups::ControllerType, spec::LinuxResources, utils::PathBufExt};
use super::{devices::Devices, hugetlb::Hugetlb, memory::Memory, pids::Pids, blkio::Blkio, Controller}; use super::{
blkio::Blkio, devices::Devices, hugetlb::Hugetlb, memory::Memory,
network_classifier::NetworkClassifier, network_priority::NetworkPriority, pids::Pids,
Controller,
};
const CONTROLLERS: &[ControllerType] = &[ const CONTROLLERS: &[ControllerType] = &[
ControllerType::Devices, ControllerType::Devices,
@ -15,6 +19,8 @@ const CONTROLLERS: &[ControllerType] = &[
ControllerType::Memory, ControllerType::Memory,
ControllerType::Pids, ControllerType::Pids,
ControllerType::Blkio, ControllerType::Blkio,
ControllerType::NetworkPriority,
ControllerType::NetworkClassifier,
]; ];
pub struct Manager { pub struct Manager {
@ -42,6 +48,8 @@ impl Manager {
"memory" => Memory::apply(linux_resources, &subsys.1, pid)?, "memory" => Memory::apply(linux_resources, &subsys.1, pid)?,
"pids" => Pids::apply(linux_resources, &subsys.1, pid)?, "pids" => Pids::apply(linux_resources, &subsys.1, pid)?,
"blkio" => Blkio::apply(linux_resources, &subsys.1, pid)?, "blkio" => Blkio::apply(linux_resources, &subsys.1, pid)?,
"net_prio" => NetworkPriority::apply(linux_resources, &subsys.1, pid)?,
"net_cls" => NetworkClassifier::apply(linux_resources, &subsys.1, pid)?,
_ => continue, _ => continue,
} }
} }
@ -61,10 +69,22 @@ impl Manager {
} }
fn get_subsystem_path(cgroup_path: &Path, subsystem: &str) -> anyhow::Result<PathBuf> { fn get_subsystem_path(cgroup_path: &Path, subsystem: &str) -> anyhow::Result<PathBuf> {
log::debug!("Get path for subsystem: {}", subsystem);
let mount = Process::myself()? let mount = Process::myself()?
.mountinfo()? .mountinfo()?
.into_iter() .into_iter()
.find(|m| m.fs_type == "cgroup" && m.mount_point.ends_with(subsystem)) .find(|m| {
if m.fs_type == "cgroup" {
// Some systems mount net_prio and net_cls in the same directory
// other systems mount them in their own diretories. This
// should handle both cases.
if subsystem == "net_cls" || subsystem == "net_prio" {
return m.mount_point.ends_with("net_cls,net_prio")
|| m.mount_point.ends_with("net_prio,net_cls");
}
}
m.mount_point.ends_with(subsystem)
})
.unwrap(); .unwrap();
let cgroup = Process::myself()? let cgroup = Process::myself()?

View File

@ -27,10 +27,7 @@ pub struct Memory {}
impl Controller for Memory { impl Controller for Memory {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> { fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
log::info!( log::debug!("Apply Memory cgroup config");
"Memory controller path: {}",
cgroup_root.to_str().unwrap_or("")
);
create_dir_all(&cgroup_root)?; create_dir_all(&cgroup_root)?;
if let Some(memory) = &linux_resources.memory { if let Some(memory) = &linux_resources.memory {

View File

@ -5,6 +5,8 @@ mod hugetlb;
mod blkio; mod blkio;
mod manager; mod manager;
mod memory; mod memory;
mod network_classifier;
mod network_priority;
mod pids; mod pids;
pub use controller::Controller; pub use controller::Controller;
pub use controller_type::ControllerType; pub use controller_type::ControllerType;

View File

@ -0,0 +1,98 @@
use std::io::Write;
use std::{
fs::{create_dir_all, OpenOptions},
path::Path,
};
use anyhow::Result;
use nix::unistd::Pid;
use crate::{
cgroups::Controller,
spec::{LinuxNetwork, LinuxResources},
};
pub struct NetworkClassifier {}
impl Controller for NetworkClassifier {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply NetworkClassifier cgroup config");
create_dir_all(&cgroup_root)?;
if let Some(network) = linux_resources.network.as_ref() {
Self::apply(cgroup_root, network)?;
OpenOptions::new()
.create(false)
.write(true)
.truncate(true)
.open(cgroup_root.join("cgroup.procs"))?
.write_all(pid.to_string().as_bytes())?;
}
Ok(())
}
}
impl NetworkClassifier {
fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> {
if let Some(class_id) = network.class_id {
Self::write_file(&root_path.join("net_cls.classid"), &class_id.to_string())?;
}
Ok(())
}
fn write_file(file_path: &Path, data: &str) -> Result<()> {
OpenOptions::new()
.create(false)
.write(true)
.truncate(true)
.open(file_path)?
.write_all(data.as_bytes())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
fn set_fixture(temp_dir: &std::path::Path, filename: &str, val: &str) -> Result<()> {
std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(temp_dir.join(filename))?
.write_all(val.as_bytes())?;
Ok(())
}
fn create_temp_dir(test_name: &str) -> Result<PathBuf> {
std::fs::create_dir_all(std::env::temp_dir().join(test_name))?;
Ok(std::env::temp_dir().join(test_name))
}
#[test]
fn test_apply_network_classifier() {
let tmp = create_temp_dir("test_apply_network_classifier")
.expect("create temp directory for test");
set_fixture(&tmp, "net_cls.classid", "0").expect("set fixture for classID");
let id = 0x100001;
let network = LinuxNetwork {
class_id: Some(id),
priorities: vec![],
};
NetworkClassifier::apply(&tmp, &network).expect("apply network classID");
let content =
std::fs::read_to_string(tmp.join("net_cls.classid")).expect("Read classID contents");
assert_eq!(id.to_string(), content);
}
}

View File

@ -0,0 +1,116 @@
use std::io::Write;
use std::{
fs::{create_dir_all, OpenOptions},
path::Path,
};
use anyhow::Result;
use nix::unistd::Pid;
use crate::{
cgroups::Controller,
spec::{LinuxInterfacePriority, LinuxNetwork, LinuxResources},
};
impl ToString for LinuxInterfacePriority {
fn to_string(&self) -> String {
format!("{} {}\n", self.name, self.priority)
}
}
pub struct NetworkPriority {}
impl Controller for NetworkPriority {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply NetworkPriority cgroup config");
create_dir_all(&cgroup_root)?;
if let Some(network) = linux_resources.network.as_ref() {
Self::apply(cgroup_root, network)?;
OpenOptions::new()
.create(false)
.write(true)
.truncate(true)
.open(cgroup_root.join("cgroup.procs"))?
.write_all(pid.to_string().as_bytes())?;
}
Ok(())
}
}
impl NetworkPriority {
fn apply(root_path: &Path, network: &LinuxNetwork) -> Result<()> {
let priorities: String = network.priorities.iter().map(|p| p.to_string()).collect();
Self::write_file(&root_path.join("net_prio.ifpriomap"), &priorities.trim())?;
Ok(())
}
fn write_file(file_path: &Path, data: &str) -> Result<()> {
OpenOptions::new()
.create(false)
.write(true)
.truncate(true)
.open(file_path)?
.write_all(data.as_bytes())?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
fn set_fixture(temp_dir: &std::path::Path, filename: &str, val: &str) -> Result<()> {
std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(temp_dir.join(filename))?
.write_all(val.as_bytes())?;
Ok(())
}
fn create_temp_dir(test_name: &str) -> Result<PathBuf> {
std::fs::create_dir_all(std::env::temp_dir().join(test_name))?;
Ok(std::env::temp_dir().join(test_name))
}
#[test]
fn test_apply_network_priorites() {
let tmp = create_temp_dir("test_apply_network_priorites")
.expect("create temp directory for test");
set_fixture(&tmp, "net_prio.ifpriomap", "").expect("set fixture for priority map");
let priorities = vec![
LinuxInterfacePriority {
name: "a".to_owned(),
priority: 1,
},
LinuxInterfacePriority {
name: "b".to_owned(),
priority: 2,
},
];
let priorities_string = priorities
.clone()
.iter()
.map(|p| p.to_string())
.collect::<String>();
let network = LinuxNetwork {
class_id: None,
priorities,
};
NetworkPriority::apply(&tmp, &network).expect("apply network priorities");
let content =
std::fs::read_to_string(tmp.join("net_prio.ifpriomap")).expect("Read classID contents");
assert_eq!(priorities_string.trim(), content);
}
}

View File

@ -61,8 +61,6 @@ impl Pids {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf;
use super::*; use super::*;
use crate::spec::LinuxPids; use crate::spec::LinuxPids;
@ -77,7 +75,7 @@ mod tests {
Ok(()) Ok(())
} }
fn create_temp_dir(test_name: &str) -> Result<PathBuf> { fn create_temp_dir(test_name: &str) -> Result<std::path::PathBuf> {
std::fs::create_dir_all(std::env::temp_dir().join(test_name))?; std::fs::create_dir_all(std::env::temp_dir().join(test_name))?;
Ok(std::env::temp_dir().join(test_name)) Ok(std::env::temp_dir().join(test_name))
} }
@ -85,7 +83,7 @@ mod tests {
#[test] #[test]
fn test_set_pids() { fn test_set_pids() {
let pids_file_name = "pids.max"; let pids_file_name = "pids.max";
let tmp = create_temp_dir("pids").expect("create temp directory for test"); let tmp = create_temp_dir("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 = LinuxPids { limit: 1000 };
@ -99,7 +97,7 @@ mod tests {
#[test] #[test]
fn test_set_pids_max() { fn test_set_pids_max() {
let pids_file_name = "pids.max"; let pids_file_name = "pids.max";
let tmp = create_temp_dir("pids").expect("create temp directory for test"); let tmp = create_temp_dir("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 = LinuxPids { limit: 0 };