//! `[runner]` in `brokerd.toml` (M3b spec, section 6). Do not edit. use std::path::{Path, PathBuf}; use std::time::Duration; use brokerd::args::ToolName; use brokerd::config::{Config, ConfigError}; const DIGEST: &str = "localhost/boxmaker-tools@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; fn fixture(name: &str) -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/config") .join(name) } fn invalid(name: &str) -> String { match Config::load(&fixture(name)) { Err(ConfigError::Invalid(_, why)) => why, other => panic!("{name}: expected Invalid, got {other:?}"), } } #[test] fn without_a_runner_section_there_is_no_runner() { assert_eq!(Config::load(&fixture("empty.toml")).unwrap().runner, None); } #[test] fn a_runner_with_only_an_image_gets_every_default() { let r = Config::load(&fixture("runner_minimal.toml")) .unwrap() .runner .unwrap(); assert_eq!(r.podman, PathBuf::from("podman")); assert_eq!(r.image, DIGEST); assert_eq!(r.egress_network, "pasta"); assert_eq!(r.output_cap, 262_144); assert_eq!(r.memory, "512m"); assert_eq!(r.pids, 128); assert_eq!( r.time_limit(ToolName::ReadFile), Duration::from_millis(30_000) ); assert_eq!( r.time_limit(ToolName::WriteFile), Duration::from_millis(30_000) ); assert_eq!( r.time_limit(ToolName::Shell), Duration::from_millis(100_000) ); assert_eq!( r.time_limit(ToolName::HttpFetch), Duration::from_millis(60_000) ); } #[test] fn every_runner_value_can_be_set() { let r = Config::load(&fixture("runner_full.toml")) .unwrap() .runner .unwrap(); assert_eq!(r.podman, PathBuf::from("/run/current-system/sw/bin/podman")); assert_eq!(r.egress_network, "slirp4netns"); assert_eq!((r.output_cap, r.memory.as_str(), r.pids), (1000, "1g", 64)); let limits: Vec = ToolName::ALL.iter().map(|t| r.time_limit(*t)).collect(); assert_eq!(limits, [1, 2, 3, 4].map(Duration::from_millis)); } #[test] fn a_runner_needs_an_image_named_by_digest() { assert!(matches!( Config::load(&fixture("runner_no_image.toml")), Err(ConfigError::Parse(..)) )); assert!(invalid("runner_tag.toml").contains("by digest")); } #[test] fn unknown_runner_keys_are_errors() { assert!(matches!( Config::load(&fixture("runner_unknown_key.toml")), Err(ConfigError::Parse(..)) )); } #[test] fn bad_values_are_errors_that_name_them() { let dir = std::env::temp_dir().join(format!("bx-runner-cfg-{}", std::process::id())); std::fs::create_dir_all(&dir).unwrap(); let cases = [ ( format!("image = \"x@sha256:{}\"", "0".repeat(63)), "by digest", ), ( format!("image = \"x@sha256:{}\"", "A".repeat(64)), "by digest", ), ( format!("image = \"@sha256:{}\"", "0".repeat(64)), "by digest", ), (format!("image = \"{DIGEST}\"\nmemory = \"lots\""), "memory"), (format!("image = \"{DIGEST}\"\nmemory = \"512\""), "memory"), (format!("image = \"{DIGEST}\"\nmemory = \"m\""), "memory"), ( format!("image = \"{DIGEST}\"\noutput_cap = 0"), "output_cap", ), (format!("image = \"{DIGEST}\"\npids = 0"), "pids"), (format!("image = \"{DIGEST}\"\nshell_ms = 0"), "shell_ms"), ( format!("image = \"{DIGEST}\"\nhttp_fetch_ms = 0"), "http_fetch_ms", ), ( format!("image = \"{DIGEST}\"\negress_network = \"\""), "egress_network", ), (format!("image = \"{DIGEST}\"\npodman = \"\""), "podman"), ]; for (n, (body, word)) in cases.iter().enumerate() { let path = dir.join(format!("c{n}.toml")); std::fs::write(&path, format!("[runner]\n{body}\n")).unwrap(); match Config::load(&path) { Err(ConfigError::Invalid(_, why)) => assert!(why.contains(word), "{body}: {why}"), other => panic!("{body}: expected Invalid, got {other:?}"), } } let _ = std::fs::remove_dir_all(&dir); } #[test] fn the_egress_directory_is_under_home() { let c = Config::parse("[paths]\nhome = \"/h\"\n").unwrap(); assert_eq!(c.egress_dir(), PathBuf::from("/h/run/egress")); }