Route lifecycle admission through the control registry
This commit is contained in:
parent
671473106c
commit
510dfccf5a
1 changed files with 431 additions and 158 deletions
|
|
@ -39,6 +39,9 @@ use sha2::{Digest, Sha256};
|
|||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
use crate::domain_store::{DomainStoreError, DomainStoreMutation, HostCoordinatorDomainStore};
|
||||
use crate::lifecycle::{
|
||||
HostCoordinatorLifecycle, LifecycleAction, SessionAdmissionIdentity, SessionLifecycleAdapter,
|
||||
};
|
||||
|
||||
pub const CONTROL_ALPN: &[u8] = b"lazaret-control/1";
|
||||
pub const CONTROL_SERVER_NAME: &str = "coordinator.lazaret.invalid";
|
||||
|
|
@ -387,6 +390,15 @@ pub struct RegisteredSession {
|
|||
pub session_anchor_id: SessionAnchorId,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ControlSessionAdmission {
|
||||
pub request: SessionRegistrationRequest,
|
||||
pub credential: TlsCredential,
|
||||
}
|
||||
|
||||
pub type ControlHostCoordinatorLifecycle<'store, A> =
|
||||
HostCoordinatorLifecycle<'store, A, Arc<SessionRegistry>>;
|
||||
|
||||
#[derive(Default)]
|
||||
struct RegistryState {
|
||||
enrolled: BTreeMap<CertificateDigest, TlsCredential>,
|
||||
|
|
@ -478,11 +490,26 @@ impl SessionRegistry {
|
|||
|| registered.binding.cli_id == binding.cli_id
|
||||
|| registered.session_anchor_id == session_anchor_id
|
||||
});
|
||||
let resumable_waiter = snapshot.sessions.iter().any(|session| {
|
||||
session.generation_id == binding.generation_identity.generation_id
|
||||
&& session.session_id == binding.session_id
|
||||
&& session.cli_id == binding.cli_id
|
||||
&& session.session_anchor_id == session_anchor_id
|
||||
&& session.session_phase == SessionPhase::Waiting
|
||||
});
|
||||
let duplicate_session = snapshot.sessions.iter().any(|session| {
|
||||
session.session_id == binding.session_id
|
||||
|| (is_live_session(session.session_phase)
|
||||
&& (session.cli_id == binding.cli_id
|
||||
|| session.session_anchor_id == session_anchor_id))
|
||||
let same_session_allowed = resumable_waiter
|
||||
&& session.session_id == binding.session_id
|
||||
&& (session.session_phase == SessionPhase::Closed
|
||||
|| (session.generation_id == binding.generation_identity.generation_id
|
||||
&& session.cli_id == binding.cli_id
|
||||
&& session.session_anchor_id == session_anchor_id
|
||||
&& session.session_phase == SessionPhase::Waiting));
|
||||
!same_session_allowed
|
||||
&& (session.session_id == binding.session_id
|
||||
|| (is_live_session(session.session_phase)
|
||||
&& (session.cli_id == binding.cli_id
|
||||
|| session.session_anchor_id == session_anchor_id)))
|
||||
});
|
||||
if duplicate_in_memory || duplicate_session {
|
||||
return Err(ControlError::DuplicateIdentity);
|
||||
|
|
@ -580,6 +607,69 @@ impl SessionRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
impl SessionLifecycleAdapter for Arc<SessionRegistry> {
|
||||
type Error = ControlError;
|
||||
type Admission = ControlSessionAdmission;
|
||||
|
||||
fn admission_identity(&self, admission: &Self::Admission) -> SessionAdmissionIdentity {
|
||||
let binding = &admission.request.session_binding;
|
||||
SessionAdmissionIdentity {
|
||||
generation_id: binding.generation_identity.generation_id,
|
||||
session_id: binding.session_id,
|
||||
configuration_fingerprint: binding.generation_identity.configuration_fingerprint,
|
||||
}
|
||||
}
|
||||
|
||||
fn register_session(
|
||||
&self,
|
||||
_store: &HostCoordinatorDomainStore,
|
||||
entry_action: LifecycleAction,
|
||||
admission: Self::Admission,
|
||||
) -> Result<Session, Self::Error> {
|
||||
if !matches!(
|
||||
entry_action,
|
||||
LifecycleAction::StartReadySession | LifecycleAction::AdvanceWaitingSession
|
||||
) {
|
||||
return Err(ControlError::InvalidRequest);
|
||||
}
|
||||
let registered = self.register(admission.request, &admission.credential)?;
|
||||
Ok(Session {
|
||||
generation_id: registered.binding.generation_identity.generation_id,
|
||||
session_id: registered.binding.session_id,
|
||||
cli_id: registered.binding.cli_id,
|
||||
session_anchor_id: registered.session_anchor_id,
|
||||
session_phase: SessionPhase::Attached,
|
||||
})
|
||||
}
|
||||
|
||||
fn disconnect_session(
|
||||
&self,
|
||||
_store: &HostCoordinatorDomainStore,
|
||||
session: &Session,
|
||||
) -> Result<(), Self::Error> {
|
||||
let mut state = self.lock()?;
|
||||
let registered = state.connected.get(&session.session_id);
|
||||
if let Some(registered) = registered
|
||||
&& (registered.binding.generation_identity.generation_id != session.generation_id
|
||||
|| registered.binding.cli_id != session.cli_id
|
||||
|| registered.session_anchor_id != session.session_anchor_id)
|
||||
{
|
||||
return Err(ControlError::InvalidRequest);
|
||||
}
|
||||
if session.session_phase == SessionPhase::Attached && registered.is_none() {
|
||||
return Err(ControlError::InvalidRequest);
|
||||
}
|
||||
state.connected.remove(&session.session_id);
|
||||
drop(state);
|
||||
self.store
|
||||
.apply(&[DomainStoreMutation::PutSession(Session {
|
||||
session_phase: SessionPhase::Closed,
|
||||
..session.clone()
|
||||
})])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ControlPaths {
|
||||
root: PathBuf,
|
||||
root_file: File,
|
||||
|
|
|
|||
Loading…
Reference in a new issue