1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-04-27 20:35:05 +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
act_err_num=0
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
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
fi
done

View File

@ -6,6 +6,8 @@ pub enum ControllerType {
Pids,
Memory,
Blkio,
NetworkPriority,
NetworkClassifier,
}
impl ToString for ControllerType {
@ -16,6 +18,8 @@ impl ToString for ControllerType {
Self::Pids => "pids".into(),
Self::Memory => "memory".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 {
fn apply(linux_resources: &LinuxResources, cgroup_root: &Path, pid: Pid) -> Result<()> {
log::debug!("Apply Devices cgroup config");
create_dir_all(&cgroup_root)?;
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 regex::Regex;
use crate::{cgroups::Controller, spec::{ LinuxHugepageLimit, LinuxResources}};
use crate::{
cgroups::Controller,
spec::{LinuxHugepageLimit, LinuxResources},
};
pub struct 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)?;
for hugetlb in &linux_resources.hugepage_limits {
@ -32,18 +44,21 @@ impl Hugetlb {
match caps {
None => return Err(anyhow!("page size must be in the format [0-9]+[KMG]B")),
Some(caps) => {
let page_size:u64 = caps["pagesize"].parse()?;
let page_size: u64 = caps["pagesize"].parse()?;
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(())
}
fn write_file(file_path: &Path, data: &str) -> anyhow::Result<()> {
fn write_file(file_path: &Path, data: &str) -> anyhow::Result<()> {
fs::OpenOptions::new()
.create(false)
.write(true)
@ -55,7 +70,7 @@ impl Hugetlb {
}
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 {
page_size: "2MB".to_owned(),
limit: 16384,
};
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);
}
#[test]
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 {
page_size: "3MB".to_owned(),
@ -108,6 +124,9 @@ mod tests {
};
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 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] = &[
ControllerType::Devices,
@ -15,6 +19,8 @@ const CONTROLLERS: &[ControllerType] = &[
ControllerType::Memory,
ControllerType::Pids,
ControllerType::Blkio,
ControllerType::NetworkPriority,
ControllerType::NetworkClassifier,
];
pub struct Manager {
@ -42,6 +48,8 @@ impl Manager {
"memory" => Memory::apply(linux_resources, &subsys.1, pid)?,
"pids" => Pids::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,
}
}
@ -61,10 +69,22 @@ impl Manager {
}
fn get_subsystem_path(cgroup_path: &Path, subsystem: &str) -> anyhow::Result<PathBuf> {
log::debug!("Get path for subsystem: {}", subsystem);
let mount = Process::myself()?
.mountinfo()?
.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();
let cgroup = Process::myself()?

View File

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

View File

@ -5,6 +5,8 @@ mod hugetlb;
mod blkio;
mod manager;
mod memory;
mod network_classifier;
mod network_priority;
mod pids;
pub use controller::Controller;
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)]
mod tests {
use std::path::PathBuf;
use super::*;
use crate::spec::LinuxPids;
@ -77,7 +75,7 @@ mod tests {
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))?;
Ok(std::env::temp_dir().join(test_name))
}
@ -85,7 +83,7 @@ mod tests {
#[test]
fn test_set_pids() {
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");
let pids = LinuxPids { limit: 1000 };
@ -99,7 +97,7 @@ mod tests {
#[test]
fn test_set_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");
let pids = LinuxPids { limit: 0 };