Integrate routed Git workspaces with persistent state
This commit is contained in:
parent
943c56eea8
commit
917409fcc6
1 changed files with 2363 additions and 119 deletions
189
crates/lazaret-ignored-workspace/src/daemon.rs
Normal file
189
crates/lazaret-ignored-workspace/src/daemon.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rustix::fs::{FileType, Mode, OFlags, ResolveFlags, fstat, openat2};
|
||||
|
||||
use crate::IgnoredWorkspace;
|
||||
use crate::filesystem::RouterConfiguration;
|
||||
|
||||
pub const PRIVATE_ROUTER_ROLE: &str = "--lazaret-private-ignored-workspace-router-v1";
|
||||
|
||||
#[must_use]
|
||||
pub fn dispatch_private_router(arguments: &[OsString]) -> Option<io::Result<()>> {
|
||||
if arguments
|
||||
.first()
|
||||
.is_none_or(|argument| argument != PRIVATE_ROUTER_ROLE)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(parse(arguments).and_then(run))
|
||||
}
|
||||
|
||||
struct Arguments {
|
||||
workspace_fd: i32,
|
||||
workspace_label: PathBuf,
|
||||
isolated_fd: i32,
|
||||
mountpoint: PathBuf,
|
||||
global_excludes: Option<PathBuf>,
|
||||
isolate_ignored: bool,
|
||||
threads: usize,
|
||||
}
|
||||
|
||||
fn parse(arguments: &[OsString]) -> io::Result<Arguments> {
|
||||
let mut arguments = arguments.iter().skip(1);
|
||||
let workspace_fd = number(named(&mut arguments, "--workspace-fd")?)?;
|
||||
let workspace_label = PathBuf::from(named(&mut arguments, "--workspace-label")?);
|
||||
let isolated_fd = number(named(&mut arguments, "--isolated-fd")?)?;
|
||||
let mountpoint = PathBuf::from(named(&mut arguments, "--mountpoint")?);
|
||||
let global_excludes = match named(&mut arguments, "--global-excludes")? {
|
||||
value if value.is_empty() => None,
|
||||
value => Some(PathBuf::from(value)),
|
||||
};
|
||||
let isolate_ignored = match named(&mut arguments, "--isolate-ignored")?.to_str() {
|
||||
Some("true") => true,
|
||||
Some("false") => false,
|
||||
_ => return Err(io::Error::other("router isolation mode is invalid")),
|
||||
};
|
||||
let threads = number(named(&mut arguments, "--threads")?)?;
|
||||
if arguments.next().is_some() || threads == 0 {
|
||||
return Err(io::Error::other("router arguments are invalid"));
|
||||
}
|
||||
Ok(Arguments {
|
||||
workspace_fd,
|
||||
workspace_label,
|
||||
isolated_fd,
|
||||
mountpoint,
|
||||
global_excludes,
|
||||
isolate_ignored,
|
||||
threads,
|
||||
})
|
||||
}
|
||||
|
||||
fn run(arguments: Arguments) -> io::Result<()> {
|
||||
let workspace = inherited_exact_directory(arguments.workspace_fd, &arguments.workspace_label)?;
|
||||
let isolated = inherited(arguments.isolated_fd)?;
|
||||
IgnoredWorkspace::serve_from_authorities(
|
||||
RouterConfiguration {
|
||||
workspace_label: arguments.workspace_label,
|
||||
workspace,
|
||||
isolated_state: isolated,
|
||||
mountpoint: arguments.mountpoint,
|
||||
global_excludes: arguments.global_excludes,
|
||||
isolate_ignored: arguments.isolate_ignored,
|
||||
threads: arguments.threads,
|
||||
},
|
||||
|| {
|
||||
let mut output = io::stdout().lock();
|
||||
output.write_all(b"ready\n")?;
|
||||
output.flush()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn inherited_exact_directory(descriptor: i32, label: &std::path::Path) -> io::Result<File> {
|
||||
let inherited = inherited(descriptor)?;
|
||||
let expected = fstat(&inherited)?;
|
||||
let reopened = File::from(openat2(
|
||||
rustix::fs::CWD,
|
||||
label,
|
||||
OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOFOLLOW,
|
||||
Mode::empty(),
|
||||
ResolveFlags::NO_MAGICLINKS | ResolveFlags::NO_SYMLINKS,
|
||||
)?);
|
||||
let observed = fstat(&reopened)?;
|
||||
if expected.st_dev != observed.st_dev
|
||||
|| expected.st_ino != observed.st_ino
|
||||
|| !FileType::from_raw_mode(observed.st_mode).is_dir()
|
||||
{
|
||||
return Err(io::Error::other(
|
||||
"namespace-local workspace differs from inherited authority",
|
||||
));
|
||||
}
|
||||
Ok(reopened)
|
||||
}
|
||||
|
||||
fn inherited(descriptor: i32) -> io::Result<File> {
|
||||
if descriptor < 0 {
|
||||
return Err(io::Error::other("router descriptor is invalid"));
|
||||
}
|
||||
File::open(format!("/proc/self/fd/{descriptor}"))
|
||||
}
|
||||
|
||||
fn named<'a>(
|
||||
arguments: &mut impl Iterator<Item = &'a OsString>,
|
||||
name: &str,
|
||||
) -> io::Result<&'a OsString> {
|
||||
if arguments.next().is_none_or(|argument| argument != name) {
|
||||
return Err(io::Error::other(format!(
|
||||
"router argument {name} is missing"
|
||||
)));
|
||||
}
|
||||
arguments
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::other(format!("router argument {name} has no value")))
|
||||
}
|
||||
|
||||
fn number<T>(value: &OsStr) -> io::Result<T>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
{
|
||||
value
|
||||
.to_str()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.ok_or_else(|| io::Error::other("router numeric argument is invalid"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn exact_private_arguments_parse() {
|
||||
let arguments = valid_arguments();
|
||||
let parsed = parse(&arguments).unwrap();
|
||||
assert_eq!(parsed.workspace_fd, 3);
|
||||
assert_eq!(parsed.isolated_fd, 4);
|
||||
assert_eq!(parsed.threads, 2);
|
||||
assert!(parsed.isolate_ignored);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_private_arguments_are_refused() {
|
||||
let mut extra = valid_arguments();
|
||||
extra.push("unexpected".into());
|
||||
assert!(parse(&extra).is_err());
|
||||
|
||||
let mut zero_threads = valid_arguments();
|
||||
*zero_threads.last_mut().unwrap() = "0".into();
|
||||
assert!(parse(&zero_threads).is_err());
|
||||
|
||||
let mut missing = valid_arguments();
|
||||
missing.remove(2);
|
||||
assert!(parse(&missing).is_err());
|
||||
}
|
||||
|
||||
fn valid_arguments() -> Vec<OsString> {
|
||||
[
|
||||
PRIVATE_ROUTER_ROLE,
|
||||
"--workspace-fd",
|
||||
"3",
|
||||
"--workspace-label",
|
||||
"/workspace",
|
||||
"--isolated-fd",
|
||||
"4",
|
||||
"--mountpoint",
|
||||
"/runtime/workspace",
|
||||
"--global-excludes",
|
||||
"",
|
||||
"--isolate-ignored",
|
||||
"true",
|
||||
"--threads",
|
||||
"2",
|
||||
]
|
||||
.into_iter()
|
||||
.map(OsString::from)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue