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:
parent
c6b91abf35
commit
64fd60dda3
@ -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"),
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user