1
0
mirror of https://github.com/containers/youki synced 2024-11-23 09:21:57 +01:00

Systemd manager updates

- Use systemd client to find systemd cgroup root
- Add error context
- Manager debug impl
- Comments
- Set default slice name for rootless and rootfull containers
This commit is contained in:
Furisto 2021-11-24 19:11:00 +01:00
parent c6b91abf35
commit 64fd60dda3
2 changed files with 128 additions and 44 deletions

@ -190,13 +190,16 @@ pub fn create_cgroup_manager<P: Into<PathBuf>>(
}
let use_system = nix::unistd::geteuid().is_root();
log::info!("systemd cgroup manager with system bus {} will be used", use_system);
log::info!(
"systemd cgroup manager with system bus {} will be used",
use_system
);
return Ok(Box::new(systemd::manager::Manager::new(
DEFAULT_CGROUP_ROOT.into(),
cgroup_path.into(),
container_name.into(),
use_system
use_system,
)?));
}
log::info!("cgroup manager V2 will be used");

@ -2,7 +2,7 @@
#![allow(unused_variables)]
use std::{
collections::HashMap,
fmt::Display,
fmt::{Debug, Display},
fs::{self},
os::unix::fs::PermissionsExt,
path::Component::RootDir,
@ -18,7 +18,7 @@ use super::{
controller_type::{ControllerType, CONTROLLER_TYPES},
cpu::Cpu,
cpuset::CpuSet,
dbus::client::Client,
dbus::client::{Client, SystemdClient},
memory::Memory,
pids::Pids,
};
@ -32,14 +32,21 @@ const CGROUP_PROCS: &str = "cgroup.procs";
const CGROUP_CONTROLLERS: &str = "cgroup.controllers";
const CGROUP_SUBTREE_CONTROL: &str = "cgroup.subtree_control";
/// SystemDCGroupManager is a driver for managing cgroups via systemd.
pub struct Manager {
/// Root path of the cgroup hierarchy e.g. /sys/fs/cgroup
root_path: PathBuf,
/// Path relative to the root path e.g. /system.slice/youki-569d5ce3afe1074769f67.scope for rootfull containers
/// and e.g. /user.slice/user-1000/user@1000.service/youki-569d5ce3afe1074769f67.scope for rootless containers
cgroups_path: PathBuf,
/// Combination of root path and cgroups path
full_path: PathBuf,
/// Destructured cgroups path as specified in the runtime spec e.g. system.slice:youki:569d5ce3afe1074769f67
destructured_path: CgroupsPath,
/// Name of the container e.g. 569d5ce3afe1074769f67
container_name: String,
/// Name of the systemd unit e.g. youki-569d5ce3afe1074769f67.scope
unit_name: String,
/// Client for communicating with systemd
client: Client,
}
@ -60,11 +67,37 @@ impl Display for CgroupsPath {
}
}
// custom debug impl as Manager contains fields that do not implement Debug
// and therefore Debug cannot be derived
impl Debug for Manager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Manager")
.field("root_path", &self.root_path)
.field("cgroups_path", &self.cgroups_path)
.field("full_path", &self.full_path)
.field("destructured_path", &self.destructured_path)
.field("container_name", &self.container_name)
.field("unit_name", &self.unit_name)
.finish()
}
}
impl Manager {
pub fn new(root_path: PathBuf, cgroups_path: PathBuf, container_name: String, use_system: bool) -> Result<Self> {
// TODO: create the systemd unit using a dbus client.
let destructured_path = Self::destructure_cgroups_path(cgroups_path)?;
let (cgroups_path, parent) = Self::construct_cgroups_path(&destructured_path)?;
pub fn new(
root_path: PathBuf,
cgroups_path: PathBuf,
container_name: String,
use_system: bool,
) -> Result<Self> {
let destructured_path = Self::destructure_cgroups_path(&cgroups_path)
.with_context(|| format!("failed to destructure cgroups path {:?}", cgroups_path))?;
let client = match use_system {
true => Client::new_system().context("failed to create system dbus client")?,
false => Client::new_session().context("failed to create session dbus client")?,
};
let (cgroups_path, parent) = Self::construct_cgroups_path(&destructured_path, &client)
.context("failed to construct cgroups path")?;
let full_path = root_path.join_safely(&cgroups_path)?;
Ok(Manager {
@ -74,15 +107,11 @@ impl Manager {
container_name,
unit_name: Self::get_unit_name(&destructured_path),
destructured_path,
client: match use_system {
true => Client::new_system().context("failed to create system dbus client")?,
false => Client::new_session().context("failed to create session dbus client")?,
}
client,
})
}
fn destructure_cgroups_path(cgroups_path: PathBuf) -> Result<CgroupsPath> {
log::debug!("CGROUPS PATH IS {:?}", cgroups_path);
fn destructure_cgroups_path(cgroups_path: &Path) -> Result<CgroupsPath> {
// cgroups path may never be empty as it is defaulted to `/youki`
// see 'get_cgroup_path' under utils.rs.
// if cgroups_path was provided it should be of the form [slice]:[prefix]:[name],
@ -95,11 +124,11 @@ impl Manager {
name = cgroups_path
.strip_prefix("/youki/")?
.to_str()
.ok_or_else(|| anyhow!("failed to parse cgroupsPath field"))?;
.ok_or_else(|| anyhow!("failed to parse cgroups path"))?;
} else {
let parts = cgroups_path
.to_str()
.ok_or_else(|| anyhow!("failed to parse cgroupsPath field"))?
.ok_or_else(|| anyhow!("failed to parse cgroups path"))?
.split(':')
.collect::<Vec<&str>>();
parent = parts[0];
@ -124,6 +153,33 @@ impl Manager {
cgroups_path.name.clone()
}
// get_cgroups_path generates a cgroups path from the one provided by the user via cgroupsPath.
// an example of the final path: "/system.slice/docker-foo.scope"
fn construct_cgroups_path(
cgroups_path: &CgroupsPath,
client: &dyn SystemdClient,
) -> Result<(PathBuf, PathBuf)> {
let mut parent = match client.is_system() {
true => PathBuf::from("/system.slice"),
false => PathBuf::from("/user.slice"),
};
// if the user provided a '.slice' (as in a branch of a tree)
// we need to convert it to a filesystem path.
if !cgroups_path.parent.is_empty() {
parent = Self::expand_slice(&cgroups_path.parent)?;
}
let systemd_root = client.control_cgroup_root()?;
let unit_name = Self::get_unit_name(cgroups_path);
let cgroups_path = systemd_root
.join_safely(&parent)
.with_context(|| format!("failed to join {:?} with {:?}", systemd_root, parent))?
.join_safely(&unit_name)
.with_context(|| format!("failed to join {:?} with {:?}", parent, unit_name))?;
Ok((cgroups_path, parent))
}
// systemd represents slice hierarchy using `-`, so we need to follow suit when
// generating the path of slice. For example, 'test-a-b.slice' becomes
// '/test.slice/test-a.slice/test-a-b.slice'.
@ -144,7 +200,7 @@ impl Manager {
}
for component in slice_name.split('-') {
if component.is_empty() {
anyhow!("Invalid slice name: {}", slice);
anyhow!("invalid slice name: {}", slice);
}
// Append the component to the path and to the prefix.
path = format!("{}/{}{}{}", path, prefix, component, suffix);
@ -153,23 +209,6 @@ impl Manager {
Ok(Path::new(&path).to_path_buf())
}
// get_cgroups_path generates a cgroups path from the one provided by the user via cgroupsPath.
// an example of the final path: "/machine.slice/docker-foo.scope"
fn construct_cgroups_path(cgroups_path: &CgroupsPath) -> Result<(PathBuf, PathBuf)> {
// the root slice is under 'machine.slice'.
let mut parent = PathBuf::from("/system.slice");
// if the user provided a '.slice' (as in a branch of a tree)
// we need to convert it to a filesystem path.
if !cgroups_path.parent.is_empty() {
parent = Self::expand_slice(&cgroups_path.parent)?;
}
let unit_name = Self::get_unit_name(cgroups_path);
let cgroups_path = parent
.join_safely(&unit_name)
.with_context(|| format!("failed to join {:?} with {:?}", parent, unit_name))?;
Ok((cgroups_path, parent))
}
/// create_unified_cgroup verifies sure that *each level* in the downward path from the root cgroup
/// down to the cgroup_path provided by the user is a valid cgroup hierarchy,
/// containing the attached controllers and that it contains the container pid.
@ -266,6 +305,10 @@ impl CgroupManager for Manager {
)
})?;
let cg = self.client.control_cgroup_root().context("cgroup root")?;
log::debug!("CONTROL GROUP ROOT: {:?}", cg);
log::debug!("MANAGER {:?}", self);
Ok(())
}
@ -332,8 +375,48 @@ impl CgroupManager for Manager {
#[cfg(test)]
mod tests {
use crate::systemd::dbus::client::SystemdClient;
use super::*;
struct TestSystemdClient {}
impl SystemdClient for TestSystemdClient {
fn is_system(&self) -> bool {
true
}
fn start_transient_unit(
&self,
container_name: &str,
pid: u32,
parent: &str,
unit_name: &str,
) -> Result<()> {
Ok(())
}
fn stop_transient_unit(&self, unit_name: &str) -> Result<()> {
Ok(())
}
fn set_unit_properties(
&self,
unit_name: &str,
properties: &HashMap<&str, Box<dyn RefArg>>,
) -> Result<()> {
Ok(())
}
fn systemd_version(&self) -> Result<u32> {
Ok(245)
}
fn control_cgroup_root(&self) -> Result<PathBuf> {
Ok(PathBuf::from("/"))
}
}
#[test]
fn expand_slice_works() -> Result<()> {
assert_eq!(
@ -347,11 +430,10 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_a_complex_slice() -> Result<()> {
let cgroups_path =
Manager::destructure_cgroups_path(PathBuf::from("test-a-b.slice:docker:foo"))
.expect("");
Manager::destructure_cgroups_path(Path::new("test-a-b.slice:docker:foo")).expect("");
assert_eq!(
Manager::construct_cgroups_path(&cgroups_path)?.0,
Manager::construct_cgroups_path(&cgroups_path, &TestSystemdClient {})?.0,
PathBuf::from("/test.slice/test-a.slice/test-a-b.slice/docker-foo.scope"),
);
@ -361,10 +443,10 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_a_simple_slice() -> Result<()> {
let cgroups_path =
Manager::destructure_cgroups_path(PathBuf::from("machine.slice:libpod:foo")).expect("");
Manager::destructure_cgroups_path(Path::new("machine.slice:libpod:foo")).expect("");
assert_eq!(
Manager::construct_cgroups_path(&cgroups_path)?.0,
Manager::construct_cgroups_path(&cgroups_path, &TestSystemdClient {})?.0,
PathBuf::from("/machine.slice/libpod-foo.scope"),
);
@ -373,11 +455,10 @@ mod tests {
#[test]
fn get_cgroups_path_works_with_scope() -> Result<()> {
let cgroups_path =
Manager::destructure_cgroups_path(PathBuf::from(":docker:foo")).expect("");
let cgroups_path = Manager::destructure_cgroups_path(Path::new(":docker:foo")).expect("");
assert_eq!(
Manager::construct_cgroups_path(&cgroups_path)?.0,
Manager::construct_cgroups_path(&cgroups_path, &TestSystemdClient {})?.0,
PathBuf::from("/system.slice/docker-foo.scope"),
);