Run non-interactive sessions through production coordination

This commit is contained in:
ms2k 2026-07-16 16:28:18 +09:00
commit 71b0266cf2
2 changed files with 3770 additions and 44 deletions

View file

@ -421,7 +421,7 @@ fn real_uds_tls_enrollment_and_registration_preserve_exact_profiles_and_session_
assert_eq!(socket_metadata.mode() & 0o7777, 0o600);
assert_eq!(identity_metadata.uid(), getuid().as_raw());
assert_eq!(identity_metadata.mode() & 0o7777, 0o600);
assert_eq!(face.socket_path().file_name().unwrap(), "control.sock");
assert_eq!(face.socket_path().file_name().unwrap(), "c.sock");
assert_eq!(
face.identity_path().file_name().unwrap(),
"protocol-identity.postcard"
@ -740,7 +740,7 @@ fn publication_refuses_preexisting_fixed_control_path() {
let directory = TestDirectory::new("existing");
let identity = generation_identity(7);
let store = open_store(&directory, &identity);
File::create(directory.path("control.sock")).unwrap();
File::create(directory.path("c.sock")).unwrap();
assert!(matches!(
PublishedControlFace::publish(
&directory.0,
@ -765,7 +765,7 @@ fn prepared_control_face_admits_only_after_generation_publication() {
registry,
)
.unwrap();
assert!(std::os::unix::net::UnixStream::connect(directory.path("control.sock")).is_err());
assert!(std::os::unix::net::UnixStream::connect(directory.path("c.sock")).is_err());
store
.apply(&[DomainStoreMutation::PutGeneration(generation(

View file

@ -12,7 +12,9 @@ use lazaret_host_coordinator::lifecycle::{
DomainPhase, HostCoordinatorLifecycle, LifecycleAction, LifecycleAdapter, LifecycleError,
SessionAdmissionIdentity, SessionLifecycleAdapter,
};
use lazaret_host_coordinator::production::BootstrapOwnerGuard;
use lazaret_linux::filesystem::try_exclusive_flock;
use rustix::pipe::{PipeFlags, pipe_with};
#[derive(Clone, Default)]
struct RecordingAdapter {
@ -256,6 +258,61 @@ fn bootstrap_owner_guard_failure_uses_exact_modeled_disconnect_transition() {
);
}
#[test]
fn published_bootstrap_owner_eof_disconnects_waiting_session_and_tears_down() {
let directory = tempfile::tempdir().unwrap();
let store = open_store(&directory);
let (domain_adapter, session_adapter) = adapters();
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
let owner = session(10, 1);
lifecycle
.start_new_domain(generation(1), owner.clone())
.unwrap();
complete_startup(&mut lifecycle);
let (reader, writer) = pipe_with(PipeFlags::CLOEXEC).unwrap();
let owner_guard = BootstrapOwnerGuard::from_inherited(reader).unwrap();
drop(writer);
lifecycle
.reconcile_published_bootstrap_owner(owner.session_id, owner_guard.is_live().unwrap())
.unwrap();
assert_eq!(lifecycle.phase(), DomainPhase::Absent);
assert!(!lifecycle.is_published());
assert_eq!(
lifecycle.session_phase(owner.session_id),
Some(SessionPhase::Closed)
);
}
#[test]
fn admitted_bootstrap_owner_ignores_later_bootstrap_pipe_eof() {
let directory = tempfile::tempdir().unwrap();
let store = open_store(&directory);
let (domain_adapter, session_adapter) = adapters();
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
let owner = session(10, 1);
lifecycle
.start_new_domain(generation(1), owner.clone())
.unwrap();
complete_startup(&mut lifecycle);
lifecycle.admit_session(admission(owner.clone())).unwrap();
let (reader, writer) = pipe_with(PipeFlags::CLOEXEC).unwrap();
let owner_guard = BootstrapOwnerGuard::from_inherited(reader).unwrap();
drop(writer);
lifecycle
.reconcile_published_bootstrap_owner(owner.session_id, owner_guard.is_live().unwrap())
.unwrap();
assert_eq!(lifecycle.phase(), DomainPhase::Ready);
assert!(lifecycle.is_published());
assert_eq!(
lifecycle.session_phase(owner.session_id),
Some(SessionPhase::Attached)
);
}
#[test]
fn exact_start_join_disconnect_and_teardown_actions_are_ordered() {
let directory = tempfile::tempdir().unwrap();
@ -274,15 +331,10 @@ fn exact_start_join_disconnect_and_teardown_actions_are_ordered() {
lifecycle.admit_session(admission(waiter.clone())).unwrap();
lifecycle.disconnect_session(owner.session_id).unwrap();
assert!(!lifecycle.is_teardown_ready());
assert!(!lifecycle.teardown_if_idle().unwrap());
lifecycle.disconnect_session(waiter.session_id).unwrap();
assert!(lifecycle.is_teardown_ready());
lifecycle.begin_teardown().unwrap();
lifecycle.stop_sandbox().unwrap();
lifecycle.stop_sidecar().unwrap();
lifecycle.unmount_state_anchor().unwrap();
lifecycle.release_prepared_lower_lease().unwrap();
lifecycle.graceful_supervisor_exit().unwrap();
lifecycle.finish_teardown().unwrap();
assert!(lifecycle.teardown_if_idle().unwrap());
assert_eq!(lifecycle.phase(), DomainPhase::Absent);
assert!(!lifecycle.is_published());