Stop terminal grace polling after authority loss
This commit is contained in:
parent
c5ed009fb8
commit
0f4845d4f8
1 changed files with 189 additions and 63 deletions
|
|
@ -167,25 +167,7 @@ impl Fixture {
|
|||
}
|
||||
|
||||
fn release_sandbox(&self) {
|
||||
let sandbox = self
|
||||
.store
|
||||
.snapshot()
|
||||
.unwrap()
|
||||
.participants
|
||||
.into_iter()
|
||||
.find(|participant| participant.role() == ParticipantRole::Sandbox)
|
||||
.unwrap();
|
||||
self.store
|
||||
.apply(&[DomainStoreMutation::PutParticipant(
|
||||
Participant::snapshot(
|
||||
sandbox.generation_id(),
|
||||
sandbox.role(),
|
||||
ParticipantState::ReleaseIntent,
|
||||
sandbox.ownership().cloned(),
|
||||
)
|
||||
.unwrap(),
|
||||
)])
|
||||
.unwrap();
|
||||
release_sandbox(&self.store);
|
||||
}
|
||||
|
||||
fn add_newer_generation(&self) {
|
||||
|
|
@ -203,6 +185,27 @@ impl Fixture {
|
|||
}
|
||||
}
|
||||
|
||||
fn release_sandbox(store: &HostCoordinatorDomainStore) {
|
||||
let sandbox = store
|
||||
.snapshot()
|
||||
.unwrap()
|
||||
.participants
|
||||
.into_iter()
|
||||
.find(|participant| participant.role() == ParticipantRole::Sandbox)
|
||||
.unwrap();
|
||||
store
|
||||
.apply(&[DomainStoreMutation::PutParticipant(
|
||||
Participant::snapshot(
|
||||
sandbox.generation_id(),
|
||||
sandbox.role(),
|
||||
ParticipantState::ReleaseIntent,
|
||||
sandbox.ownership().cloned(),
|
||||
)
|
||||
.unwrap(),
|
||||
)])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn insert_attached_sessions(store: &HostCoordinatorDomainStore, session_ids: &[SessionId]) {
|
||||
for (index, session_id) in session_ids.iter().enumerate() {
|
||||
let mut session = Session {
|
||||
|
|
@ -222,12 +225,13 @@ fn insert_attached_sessions(store: &HostCoordinatorDomainStore, session_ids: &[S
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockState {
|
||||
output: VecDeque<Vec<u8>>,
|
||||
log: Arc<Mutex<Vec<String>>>,
|
||||
guest: MockGuestState,
|
||||
resources: MockResourceState,
|
||||
group_queries: usize,
|
||||
group_query_hook: Option<(usize, Arc<dyn Fn() + Send + Sync>)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -367,11 +371,29 @@ impl SessionTerminalProcess for MockProcess {
|
|||
}
|
||||
|
||||
fn group_absent(&self) -> Result<bool, TerminalError> {
|
||||
let state = self.state.lock().unwrap();
|
||||
if state.guest.identity_mismatch {
|
||||
let (identity_mismatch, group_absent, hook) = {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
state.group_queries += 1;
|
||||
let group_queries = state.group_queries;
|
||||
state.log.lock().unwrap().push("query:group".into());
|
||||
let hook = state
|
||||
.group_query_hook
|
||||
.as_ref()
|
||||
.filter(|(query, _)| *query == group_queries)
|
||||
.map(|(_, hook)| Arc::clone(hook));
|
||||
(
|
||||
state.guest.identity_mismatch,
|
||||
!state.guest.group_present,
|
||||
hook,
|
||||
)
|
||||
};
|
||||
if let Some(hook) = hook {
|
||||
hook();
|
||||
}
|
||||
if identity_mismatch {
|
||||
return Err(TerminalError::GuestIdentityMismatch);
|
||||
}
|
||||
Ok(!state.guest.group_present)
|
||||
Ok(group_absent)
|
||||
}
|
||||
|
||||
fn terminate_exec(&self) -> io::Result<()> {
|
||||
|
|
@ -468,6 +490,8 @@ impl SessionTerminalFactory for MockFactory {
|
|||
exec_reaped: false,
|
||||
pty_open: true,
|
||||
},
|
||||
group_queries: 0,
|
||||
group_query_hook: None,
|
||||
}));
|
||||
self.states.lock().unwrap().push(Arc::clone(&state));
|
||||
Ok((running, Box::new(MockProcess { state })))
|
||||
|
|
@ -537,6 +561,16 @@ fn assert_no_group_signal(factory: &MockFactory) {
|
|||
);
|
||||
}
|
||||
|
||||
fn assert_no_guest_action_after(factory: &MockFactory, marker: &str) {
|
||||
let log = factory.log.lock().unwrap();
|
||||
let marker = log.iter().position(|action| action == marker).unwrap();
|
||||
assert!(
|
||||
log[marker + 1..]
|
||||
.iter()
|
||||
.all(|action| !action.starts_with("query:") && !action.starts_with("signal:"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acquisition_refuses_authority_outside_the_committed_sandbox() {
|
||||
let session = id(6);
|
||||
|
|
@ -694,6 +728,68 @@ fn explicit_close_and_loss_revalidate_before_guest_cleanup() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grace_wait_revocation_and_read_error_stop_all_later_guest_actions() {
|
||||
let session = id(6);
|
||||
let (fixture, factory, mut engine) = setup(&[session]);
|
||||
engine.acquire(&acquisition(session)).unwrap();
|
||||
let store = Arc::clone(&fixture.store);
|
||||
let log = Arc::clone(&factory.log);
|
||||
factory.state(0).lock().unwrap().group_query_hook = Some((
|
||||
2,
|
||||
Arc::new(move || {
|
||||
log.lock().unwrap().push("revoke:sandbox".into());
|
||||
release_sandbox(&store);
|
||||
}),
|
||||
));
|
||||
engine
|
||||
.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
session_id: session,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_no_guest_action_after(&factory, "revoke:sandbox");
|
||||
let state = factory.state(0);
|
||||
assert!(state.lock().unwrap().resources.exec_reaped);
|
||||
assert!(!state.lock().unwrap().resources.pty_open);
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
|
||||
let fixture = Fixture::new(&[session]);
|
||||
let store = Arc::new(FailingRunningStore::new(Arc::clone(&fixture.store)));
|
||||
let (factory, mut engine) = engine_for_store(store.clone());
|
||||
engine.acquire(&acquisition(session)).unwrap();
|
||||
let failing_store = Arc::clone(&store);
|
||||
let log = Arc::clone(&factory.log);
|
||||
factory.state(0).lock().unwrap().group_query_hook = Some((
|
||||
2,
|
||||
Arc::new(move || {
|
||||
log.lock().unwrap().push("revoke:read-error".into());
|
||||
failing_store.arm_snapshot_failure();
|
||||
}),
|
||||
));
|
||||
engine
|
||||
.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
session_id: session,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_no_guest_action_after(&factory, "revoke:read-error");
|
||||
let state = factory.state(0);
|
||||
assert!(state.lock().unwrap().resources.exec_reaped);
|
||||
assert!(!state.lock().unwrap().resources.pty_open);
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_caller_and_superseded_generation_refuse_without_cross_generation_action() {
|
||||
let session = id(6);
|
||||
|
|
@ -923,6 +1019,8 @@ fn running_store_failures_release_every_live_partial_handle() {
|
|||
exec_reaped: false,
|
||||
pty_open: true,
|
||||
},
|
||||
group_queries: 0,
|
||||
group_query_hook: None,
|
||||
}));
|
||||
let mut process_handle = MockProcess {
|
||||
state: Arc::clone(&state),
|
||||
|
|
|
|||
Loading…
Reference in a new issue