179 lines
5.7 KiB
Rust
179 lines
5.7 KiB
Rust
|
|
use std::fs::{File, OpenOptions};
|
||
|
|
use std::path::Path;
|
||
|
|
|
||
|
|
use lazaret_core::identity::{
|
||
|
|
BootId, ConfigurationFingerprint, CoordinatorId, DomainId, HostPidLocator, NetworkSidecarId,
|
||
|
|
PidfsDeviceId, PidfsInodeId, PidfsProcessAuthorityV1, ProcessStartTimeTicks, SandboxId,
|
||
|
|
};
|
||
|
|
use lazaret_core::records::{Domain, DurableDecision, Generation};
|
||
|
|
use lazaret_linux::filesystem::try_exclusive_flock;
|
||
|
|
use tempfile::TempDir;
|
||
|
|
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
fn bytes(value: u8) -> [u8; 32] {
|
||
|
|
[value; 32]
|
||
|
|
}
|
||
|
|
|
||
|
|
fn process(value: u32) -> PidfsProcessIdentityV1 {
|
||
|
|
PidfsProcessIdentityV1::new(
|
||
|
|
PidfsProcessAuthorityV1 {
|
||
|
|
boot_id: BootId::new([1; 16]),
|
||
|
|
pidfs_st_dev: PidfsDeviceId::new(u64::from(value)),
|
||
|
|
pidfs_st_ino: PidfsInodeId::new(u64::from(value) + 100),
|
||
|
|
},
|
||
|
|
HostPidLocator::new(value),
|
||
|
|
ProcessStartTimeTicks::new(u64::from(value) + 200),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
fn domain() -> Domain {
|
||
|
|
Domain {
|
||
|
|
domain_id: DomainId::new(bytes(1)),
|
||
|
|
profile_name: "trusted".into(),
|
||
|
|
canonical_workspace: HostPath::new(b"/workspace/project".to_vec()),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn generation() -> Generation {
|
||
|
|
Generation {
|
||
|
|
domain_id: domain().domain_id,
|
||
|
|
generation_id: GenerationId::new(7),
|
||
|
|
configuration_fingerprint: ConfigurationFingerprint::new(bytes(2)),
|
||
|
|
coordinator_id: CoordinatorId::new(bytes(3)),
|
||
|
|
network_sidecar_id: NetworkSidecarId::new(bytes(4)),
|
||
|
|
sandbox_id: SandboxId::new(bytes(5)),
|
||
|
|
durable_decision: DurableDecision::None,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn lock_file(path: &Path) -> File {
|
||
|
|
OpenOptions::new()
|
||
|
|
.read(true)
|
||
|
|
.write(true)
|
||
|
|
.create(true)
|
||
|
|
.truncate(false)
|
||
|
|
.open(path)
|
||
|
|
.unwrap()
|
||
|
|
}
|
||
|
|
|
||
|
|
fn store(directory: &TempDir) -> HostCoordinatorDomainStore {
|
||
|
|
let lock = try_exclusive_flock(lock_file(&directory.path().join("domain.lock"))).unwrap();
|
||
|
|
let store =
|
||
|
|
HostCoordinatorDomainStore::open(lock, &directory.path().join("domain.redb"), &domain())
|
||
|
|
.unwrap();
|
||
|
|
store
|
||
|
|
.apply(&[DomainStoreMutation::PutGeneration(generation())])
|
||
|
|
.unwrap();
|
||
|
|
store
|
||
|
|
}
|
||
|
|
|
||
|
|
fn staged(
|
||
|
|
slot: HostProcessOwnershipSlot,
|
||
|
|
identity: PidfsProcessIdentityV1,
|
||
|
|
) -> StagedProcessOwnership {
|
||
|
|
StagedProcessOwnership::v1(
|
||
|
|
domain().domain_id,
|
||
|
|
generation().generation_id,
|
||
|
|
generation().coordinator_id,
|
||
|
|
slot,
|
||
|
|
identity,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn sandbox_staging_readiness_and_release_preserve_exact_ownership() {
|
||
|
|
let directory = tempfile::tempdir().unwrap();
|
||
|
|
let store = store(&directory);
|
||
|
|
let coordinator = SandboxCoordinator::new(&store);
|
||
|
|
coordinator
|
||
|
|
.begin_intent(
|
||
|
|
generation().generation_id,
|
||
|
|
ContainerId::new(bytes(6)),
|
||
|
|
directory.path(),
|
||
|
|
BundleDigest::new(bytes(7)),
|
||
|
|
process(100),
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
let rs = staged(
|
||
|
|
HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
|
||
|
|
process(101),
|
||
|
|
);
|
||
|
|
coordinator.stage_runtime_supervisor(rs).unwrap();
|
||
|
|
assert!(
|
||
|
|
coordinator
|
||
|
|
.acknowledge_runtime_supervisor(rs)
|
||
|
|
.unwrap()
|
||
|
|
.acknowledges(&rs)
|
||
|
|
);
|
||
|
|
let runsc = staged(HostProcessOwnershipSlot::SandboxAttachedRunsc, process(102));
|
||
|
|
assert!(
|
||
|
|
coordinator
|
||
|
|
.stage_attached_runsc(runsc)
|
||
|
|
.unwrap()
|
||
|
|
.acknowledges(&runsc)
|
||
|
|
);
|
||
|
|
let intent = coordinator.participant(generation().generation_id).unwrap();
|
||
|
|
let Some(ParticipantOwnership::Sandbox(ownership)) = intent.ownership() else {
|
||
|
|
panic!("Sandbox ownership missing");
|
||
|
|
};
|
||
|
|
assert_eq!(intent.state(), ParticipantState::Intent);
|
||
|
|
assert_eq!(
|
||
|
|
ownership.runtime_supervisor,
|
||
|
|
PersistedHostProcessOwnership::committed(process(101))
|
||
|
|
);
|
||
|
|
assert_eq!(
|
||
|
|
ownership.attached_runsc,
|
||
|
|
PersistedHostProcessOwnership::staged(process(102))
|
||
|
|
);
|
||
|
|
coordinator.commit_ready(runsc).unwrap();
|
||
|
|
assert_eq!(
|
||
|
|
coordinator
|
||
|
|
.participant(generation().generation_id)
|
||
|
|
.unwrap()
|
||
|
|
.state(),
|
||
|
|
ParticipantState::Committed
|
||
|
|
);
|
||
|
|
coordinator
|
||
|
|
.begin_release(generation().generation_id)
|
||
|
|
.unwrap();
|
||
|
|
coordinator
|
||
|
|
.complete_clean_after_absence(generation().generation_id)
|
||
|
|
.unwrap();
|
||
|
|
let clean = coordinator.participant(generation().generation_id).unwrap();
|
||
|
|
assert_eq!(clean.state(), ParticipantState::Clean);
|
||
|
|
assert!(clean.ownership().unwrap().is_empty());
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn mismatched_and_skipped_acquisition_boundaries_refuse_without_state_change() {
|
||
|
|
let directory = tempfile::tempdir().unwrap();
|
||
|
|
let store = store(&directory);
|
||
|
|
let coordinator = SandboxCoordinator::new(&store);
|
||
|
|
coordinator
|
||
|
|
.begin_intent(
|
||
|
|
generation().generation_id,
|
||
|
|
ContainerId::new(bytes(6)),
|
||
|
|
directory.path(),
|
||
|
|
BundleDigest::new(bytes(7)),
|
||
|
|
process(100),
|
||
|
|
)
|
||
|
|
.unwrap();
|
||
|
|
let runsc = staged(HostProcessOwnershipSlot::SandboxAttachedRunsc, process(102));
|
||
|
|
assert!(coordinator.stage_attached_runsc(runsc).is_err());
|
||
|
|
assert!(coordinator.commit_ready(runsc).is_err());
|
||
|
|
let wrong_domain = StagedProcessOwnership::v1(
|
||
|
|
DomainId::new(bytes(9)),
|
||
|
|
generation().generation_id,
|
||
|
|
generation().coordinator_id,
|
||
|
|
HostProcessOwnershipSlot::SandboxRuntimeSupervisor,
|
||
|
|
process(101),
|
||
|
|
);
|
||
|
|
assert!(coordinator.stage_runtime_supervisor(wrong_domain).is_err());
|
||
|
|
let participant = coordinator.participant(generation().generation_id).unwrap();
|
||
|
|
let Some(ParticipantOwnership::Sandbox(ownership)) = participant.ownership() else {
|
||
|
|
panic!("Sandbox ownership missing");
|
||
|
|
};
|
||
|
|
assert!(ownership.runtime_supervisor.is_unassigned());
|
||
|
|
assert!(ownership.attached_runsc.is_unassigned());
|
||
|
|
}
|