1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-05-21 15:06:07 +02:00
youki/cgroups/src/v2/freezer.rs
2021-08-12 11:31:26 +02:00

193 lines
6.0 KiB
Rust

use anyhow::{bail, Result};
use std::{
fs::OpenOptions,
io::{BufRead, BufReader, Read, Seek, SeekFrom, Write},
path::Path,
str, thread,
time::Duration,
};
use oci_spec::{FreezerState, LinuxResources};
use super::controller::Controller;
const CGROUP_FREEZE: &str = "cgroup.freeze";
const CGROUP_EVENTS: &str = "cgroup.events";
pub struct Freezer {}
impl Controller for Freezer {
fn apply(linux_resources: &LinuxResources, cgroup_path: &Path) -> Result<()> {
if let Some(freezer_state) = linux_resources.freezer {
Self::apply(freezer_state, cgroup_path)?;
}
Ok(())
}
}
impl Freezer {
fn apply(freezer_state: FreezerState, path: &Path) -> Result<()> {
let state_str = match freezer_state {
FreezerState::Undefined => return Ok(()),
FreezerState::Frozen => "1",
FreezerState::Thawed => "0",
};
match OpenOptions::new()
.create(false)
.write(true)
.open(path.join(CGROUP_FREEZE))
{
Err(e) => {
if let FreezerState::Frozen = freezer_state {
bail!("freezer not supported {}", e);
}
return Ok(());
}
Ok(mut file) => file.write_all(state_str.as_bytes())?,
};
// confirm that the cgroup did actually change states.
let actual_state = Self::read_freezer_state(path)?;
if !actual_state.eq(&freezer_state) {
bail!(
"expected \"cgroup.freeze\" to be in state {:?} but was in {:?}",
freezer_state,
actual_state
);
}
Ok(())
}
fn read_freezer_state(path: &Path) -> Result<FreezerState> {
let mut buf = [0; 1];
OpenOptions::new()
.create(false)
.read(true)
.open(path.join(CGROUP_FREEZE))?
.read_exact(&mut buf)?;
let state = str::from_utf8(&buf)?;
match state {
"0" => Ok(FreezerState::Thawed),
"1" => Self::wait_frozen(path),
_ => bail!("unknown \"cgroup.freeze\" state: {}", state),
}
}
// wait_frozen polls cgroup.events until it sees "frozen 1" in it.
fn wait_frozen(path: &Path) -> Result<FreezerState> {
let f = OpenOptions::new()
.create(false)
.read(true)
.open(path.join(CGROUP_EVENTS))?;
let mut f = BufReader::new(f);
let wait_time = Duration::from_millis(10);
let max_iter = 1000;
let mut iter = 0;
let mut line = String::new();
loop {
if iter == max_iter {
bail!(
"timeout of {} ms reached waiting for the cgroup to freeze",
wait_time.as_millis() * max_iter
);
}
line.clear();
let num_bytes = f.read_line(&mut line)?;
if num_bytes == 0 {
break;
}
if line.starts_with("frozen ") {
if line.starts_with("frozen 1") {
if iter > 1 {
log::debug!("frozen after {} retries", iter)
}
return Ok(FreezerState::Frozen);
}
iter += 1;
thread::sleep(wait_time);
f.seek(SeekFrom::Start(0))?;
}
}
Ok(FreezerState::Undefined)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::{create_temp_dir, set_fixture};
use oci_spec::FreezerState;
use std::sync::Arc;
#[test]
fn test_set_freezer_state() {
let tmp = Arc::new(
create_temp_dir("test_set_freezer_state").expect("create temp directory for test"),
);
set_fixture(&tmp, CGROUP_FREEZE, "").expect("Set fixure for freezer state");
set_fixture(&tmp, CGROUP_EVENTS, "populated 0\nfrozen 0")
.expect("Set fixure for freezer state");
// set Frozen state.
{
// use another thread to update events file async.
let p = Arc::clone(&tmp);
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
set_fixture(&p, CGROUP_EVENTS, "populated 0\nfrozen 1")
.expect("Set fixure for freezer state");
});
let freezer_state = FreezerState::Frozen;
Freezer::apply(freezer_state, &tmp).expect("Set freezer state");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZE)).expect("Read to string");
assert_eq!("1", state_content);
}
// set Thawed state.
{
let freezer_state = FreezerState::Thawed;
Freezer::apply(freezer_state, &tmp).expect("Set freezer state");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZE)).expect("Read to string");
assert_eq!("0", state_content);
}
// set Undefined state.
{
let old_state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZE)).expect("Read to string");
let freezer_state = FreezerState::Undefined;
Freezer::apply(freezer_state, &tmp).expect("Set freezer state");
let state_content =
std::fs::read_to_string(tmp.join(CGROUP_FREEZE)).expect("Read to string");
assert_eq!(old_state_content, state_content);
}
}
#[test]
fn test_set_freezer_state_error() {
let tmp = create_temp_dir("test_set_freezer_state_error")
.expect("create temp directory for test");
set_fixture(&tmp, CGROUP_FREEZE, "").expect("Set fixure for freezer state");
set_fixture(&tmp, CGROUP_EVENTS, "").expect("Set fixure for freezer state");
// events file does not contain "frozen 1"
{
let freezer_state = FreezerState::Frozen;
let r = Freezer::apply(freezer_state, &tmp);
assert!(r.is_err());
}
}
}