//! After a container ends, its output is collected within a grace period, never waited on for //! ever: something outside the container that still holds a pipe must not hold `brokerd` (M3b //! review finding 4). Against a fake `podman` whose shell leaves a background `sleep` holding the //! pipes, which real Podman does not do. Do not edit. #[path = "support/build.rs"] mod build; #[path = "support/fake_podman.rs"] mod fake_podman; use std::time::{Duration, Instant}; use brokerd::container::{OUTPUT_OPEN, Podman, TIMED_OUT}; use brokerd::pipes::GRACE; use brokerd::policy::{Outcome, SessionState, decide}; use brokerd::runner::run; use build::{grant, now, read, set}; use fake_podman::{Fake, Lines, serial}; use proto::{Mode, ToolResponse}; fn call(fake: &Fake, extra: &str, log: &Lines) -> (ToolResponse, Duration) { let podman = Podman::new(fake.runner(extra), fake.dir.join("egress"), log.sink()); let grants = set(vec![grant("notes", "read_file", Mode::Auto).paths(&["/n"])]); let decision = match decide(read("/n/a"), &grants, SessionState::default(), now()) { Outcome::Allowed(d) => d, other => panic!("not allowed: {other:?}"), }; let started = Instant::now(); let got = run(decision, &podman); (got, started.elapsed()) } #[test] fn the_grace_period_is_two_seconds() { assert_eq!(GRACE, Duration::from_secs(2)); } #[test] fn output_held_open_after_the_container_ended_is_abandoned_after_the_grace_period() { let _s = serial(); // The shell exits at once; the background sleep keeps standard output and error open. let fake = Fake::new("grace-open", "cat > /dev/null; printf ok; sleep 6 & exit 0"); let log = Lines::default(); let (got, took) = call(&fake, "", &log); assert_eq!( got, ToolResponse::Failed { message: OUTPUT_OPEN.to_string() } ); assert!(took >= GRACE, "{took:?}"); assert!(took < GRACE + Duration::from_secs(2), "{took:?}"); assert!(log.all().contains("abandoned"), "{}", log.all()); } #[test] fn a_tool_past_its_limit_is_answered_within_the_grace_period_even_if_its_pipes_stay_open() { let _s = serial(); // No `exec`: killing the shell leaves the sleep holding the pipes. let fake = Fake::new("grace-slow", "cat > /dev/null; sleep 6"); let log = Lines::default(); let (got, took) = call(&fake, "read_file_ms = 300", &log); assert_eq!( got, ToolResponse::Failed { message: TIMED_OUT.to_string() } ); assert!( took < Duration::from_millis(300) + GRACE + Duration::from_secs(2), "{took:?}" ); } #[test] fn a_tool_that_ends_normally_is_not_slowed_by_the_grace_period() { let _s = serial(); let fake = Fake::new("grace-ok", "cat > /dev/null; printf done; exit 0"); let log = Lines::default(); let (got, took) = call(&fake, "", &log); assert!( matches!(&got, ToolResponse::Result { content, .. } if content == "done"), "{got:?}" ); assert!(took < Duration::from_secs(1), "{took:?}"); }