Unify host coordinator lifecycle state
This commit is contained in:
parent
85046fdd7f
commit
2f8a9f9178
1 changed files with 720 additions and 337 deletions
|
|
@ -1,5 +1,7 @@
|
|||
use std::cell::Cell;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use lazaret_core::identity::{
|
||||
|
|
@ -16,6 +18,31 @@ use lazaret_host_coordinator::lifecycle::{
|
|||
use lazaret_linux::filesystem::try_exclusive_flock;
|
||||
use rustix::pipe::{PipeFlags, pipe_with};
|
||||
|
||||
static NEXT_DIRECTORY: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
struct TestDirectory(PathBuf);
|
||||
|
||||
impl TestDirectory {
|
||||
fn new() -> Self {
|
||||
let sequence = NEXT_DIRECTORY.fetch_add(1, Ordering::Relaxed);
|
||||
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../target/test-artifacts/host-lifecycle")
|
||||
.join(format!("{}-{sequence}", std::process::id()));
|
||||
std::fs::create_dir_all(&path).unwrap();
|
||||
Self(path)
|
||||
}
|
||||
|
||||
fn path(&self) -> &Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestDirectory {
|
||||
fn drop(&mut self) {
|
||||
std::fs::remove_dir_all(&self.0).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct RecordingAdapter {
|
||||
actions: Arc<Mutex<Vec<LifecycleAction>>>,
|
||||
|
|
@ -200,7 +227,7 @@ fn session(id: u8, generation_id: u64) -> Session {
|
|||
}
|
||||
}
|
||||
|
||||
fn open_store(directory: &tempfile::TempDir) -> HostCoordinatorDomainStore {
|
||||
fn open_store(directory: &TestDirectory) -> HostCoordinatorDomainStore {
|
||||
let lock_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
|
|
@ -228,9 +255,55 @@ fn complete_startup(
|
|||
lifecycle.publish_domain().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn absent_aggregate_refuses_every_non_entry_action_before_effects() {
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let actions = Arc::clone(&domain_adapter.actions);
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
||||
macro_rules! refused {
|
||||
($operation:expr) => {
|
||||
assert!(matches!(
|
||||
$operation,
|
||||
Err(LifecycleError::InvalidTransition { .. })
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
refused!(lifecycle.admit_session(admission(session(10, 1))));
|
||||
refused!(lifecycle.wait_for_domain(session(10, 1)));
|
||||
refused!(lifecycle.restart_waiting_domain(generation(1), SessionId::new(bytes(10))));
|
||||
refused!(lifecycle.create_trust_anchor());
|
||||
refused!(lifecycle.start_runtime_supervisor());
|
||||
refused!(lifecycle.lease_prepared_lower());
|
||||
refused!(lifecycle.mount_state_anchor());
|
||||
refused!(lifecycle.start_sidecar());
|
||||
refused!(lifecycle.start_connectivity_helper());
|
||||
refused!(lifecycle.mark_network_ready());
|
||||
refused!(lifecycle.arm_runtime_causal_edge());
|
||||
refused!(lifecycle.start_sandbox());
|
||||
refused!(lifecycle.publish_domain());
|
||||
refused!(lifecycle.reject_new_configuration_mismatch(session(10, 1), fingerprint(9)));
|
||||
refused!(lifecycle.reject_configuration_mismatch(SessionId::new(bytes(10)), fingerprint(9)));
|
||||
refused!(lifecycle.disconnect_session(SessionId::new(bytes(10))));
|
||||
refused!(lifecycle.reconcile_published_bootstrap_owner(SessionId::new(bytes(10)), false));
|
||||
refused!(lifecycle.begin_teardown());
|
||||
refused!(lifecycle.stop_sandbox());
|
||||
refused!(lifecycle.stop_sidecar());
|
||||
refused!(lifecycle.unmount_state_anchor());
|
||||
refused!(lifecycle.release_prepared_lower_lease());
|
||||
refused!(lifecycle.graceful_supervisor_exit());
|
||||
refused!(lifecycle.finish_teardown());
|
||||
|
||||
assert!(actions.lock().unwrap().is_empty());
|
||||
assert!(store.snapshot().unwrap().generations.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bootstrap_owner_guard_failure_uses_exact_modeled_disconnect_transition() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let actions = Arc::clone(&domain_adapter.actions);
|
||||
|
|
@ -260,7 +333,7 @@ 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 directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -287,7 +360,7 @@ fn published_bootstrap_owner_eof_disconnects_waiting_session_and_tears_down() {
|
|||
|
||||
#[test]
|
||||
fn admitted_bootstrap_owner_ignores_later_bootstrap_pipe_eof() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -315,7 +388,7 @@ fn admitted_bootstrap_owner_ignores_later_bootstrap_pipe_eof() {
|
|||
|
||||
#[test]
|
||||
fn exact_start_join_disconnect_and_teardown_actions_are_ordered() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -383,7 +456,7 @@ fn exact_start_join_disconnect_and_teardown_actions_are_ordered() {
|
|||
|
||||
#[test]
|
||||
fn cold_start_entry_reaches_publication_in_exact_modeled_order() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -414,7 +487,7 @@ fn cold_start_entry_reaches_publication_in_exact_modeled_order() {
|
|||
|
||||
#[test]
|
||||
fn publication_exposure_failure_retains_recoverable_durable_decision() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (_, session_adapter) = adapters();
|
||||
let mut lifecycle =
|
||||
|
|
@ -435,7 +508,7 @@ fn publication_exposure_failure_retains_recoverable_durable_decision() {
|
|||
|
||||
#[test]
|
||||
fn publication_and_invalid_transitions_fail_before_adapter_execution() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -476,7 +549,7 @@ fn publication_and_invalid_transitions_fail_before_adapter_execution() {
|
|||
|
||||
#[test]
|
||||
fn aliased_generation_role_identities_are_refused_before_adapter_or_store() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -504,7 +577,7 @@ fn aliased_generation_role_identities_are_refused_before_adapter_or_store() {
|
|||
|
||||
#[test]
|
||||
fn configuration_mismatch_is_refused_without_registration() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -537,7 +610,7 @@ fn configuration_mismatch_is_refused_without_registration() {
|
|||
|
||||
#[test]
|
||||
fn waiting_session_restarts_after_normal_teardown() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -605,7 +678,7 @@ fn waiting_session_restarts_after_normal_teardown() {
|
|||
|
||||
#[test]
|
||||
fn admission_adapter_refuses_duplicate_cli_and_anchor_identities() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -640,7 +713,7 @@ fn admission_adapter_refuses_duplicate_cli_and_anchor_identities() {
|
|||
|
||||
#[test]
|
||||
fn serialized_concurrent_joins_register_distinct_sessions() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
@ -678,7 +751,7 @@ fn serialized_concurrent_joins_register_distinct_sessions() {
|
|||
|
||||
#[test]
|
||||
fn bootstrap_owner_disconnect_enters_failed_without_teardown_or_recovery() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let directory = TestDirectory::new();
|
||||
let store = open_store(&directory);
|
||||
let (domain_adapter, session_adapter) = adapters();
|
||||
let mut lifecycle = HostCoordinatorLifecycle::new(&store, domain_adapter, session_adapter);
|
||||
|
|
|
|||
Loading…
Reference in a new issue