Handle refinement to gatekeeper protocol allowing JIT binding and/or direct rejection

This commit is contained in:
Tony Garnock-Jones 2024-03-22 11:22:58 +01:00
parent eb9d9bed0f
commit 55456621d4
4 changed files with 68 additions and 35 deletions

View File

@ -15,7 +15,7 @@ use std::convert::TryInto;
use std::sync::Arc; use std::sync::Arc;
use syndicate::actor::*; use syndicate::actor::*;
use syndicate::during::DuringResult; use syndicate::enclose;
use syndicate::value::NestedValue; use syndicate::value::NestedValue;
use syndicate::schemas::dataspace; use syndicate::schemas::dataspace;
use syndicate::schemas::gatekeeper; use syndicate::schemas::gatekeeper;
@ -102,49 +102,78 @@ pub fn handle_binds(t: &mut Activation, ds: &Arc<Cap>) -> ActorResult {
Ok(()) Ok(())
} }
fn eventually_retract<E>(h: Option<Handle>) -> DuringResult<E> { pub fn facet_handle_resolve(
if let Some(h) = h {
Ok(Some(Box::new(move |_state, t: &mut Activation| Ok(t.retract(h)))))
} else {
Ok(None)
}
}
pub fn handle_resolves(
ds: &mut Arc<Cap>, ds: &mut Arc<Cap>,
t: &mut Activation, t: &mut Activation,
a: gatekeeper::Resolve, a: gatekeeper::Resolve,
) -> DuringResult<Arc<Cap>> { ) -> ActorResult {
let mut detail: &'static str = "unsupported"; let mut detail: &'static str = "unsupported";
if a.step.step_type == sturdy_step_type() { if a.step.step_type == sturdy_step_type() {
detail = "invalid"; detail = "invalid";
if let Ok(s) = language().parse::<sturdy::SturdyStepDetail>(&a.step.detail) { if let Ok(s) = language().parse::<sturdy::SturdyStepDetail>(&a.step.detail) {
return handle_resolve_sturdyref(ds, t, sturdy::SturdyRef { parameters: s.0 }, a.observer); t.facet(|t| {
let f = handle_direct_resolution(ds, t, a.clone())?;
await_bind_sturdyref(ds, t, sturdy::SturdyRef { parameters: s.0 }, a.observer, f)
})?;
return Ok(());
} }
} }
if a.step.step_type == noise_step_type() { if a.step.step_type == noise_step_type() {
detail = "invalid"; detail = "invalid";
if let Ok(s) = language().parse::<noise::NoiseStepDetail<AnyValue>>(&a.step.detail) { if let Ok(s) = language().parse::<noise::NoiseStepDetail<AnyValue>>(&a.step.detail) {
return handle_resolve_noise(ds, t, s.0.0, a.observer); t.facet(|t| {
let f = handle_direct_resolution(ds, t, a.clone())?;
await_bind_noise(ds, t, s.0.0, a.observer, f)
})?;
return Ok(());
} }
} }
eventually_retract(ds.assert(t, language(), &gatekeeper::Rejected { a.observer.assert(t, language(), &gatekeeper::Rejected {
detail: AnyValue::symbol(detail), detail: AnyValue::symbol(detail),
})) });
Ok(())
} }
fn handle_resolve_sturdyref( fn handle_direct_resolution(
ds: &mut Arc<Cap>,
t: &mut Activation,
a: gatekeeper::Resolve,
) -> Result<FacetId, ActorError> {
let outer_facet = t.facet_id();
t.facet(move |t| {
let handler = syndicate::entity(a.observer)
.on_asserted(move |observer, t, a: AnyValue| {
t.stop_facet_and_continue(outer_facet, Some(
enclose!((observer, a) move |t: &mut Activation| {
observer.assert(t, language(), &a);
Ok(())
})))?;
Ok(None)
})
.create_cap(t);
ds.assert(t, language(), &gatekeeper::Resolve {
step: a.step.clone(),
observer: handler,
});
Ok(())
})
}
fn await_bind_sturdyref(
ds: &mut Arc<Cap>, ds: &mut Arc<Cap>,
t: &mut Activation, t: &mut Activation,
sturdyref: sturdy::SturdyRef, sturdyref: sturdy::SturdyRef,
observer: Arc<Cap>, observer: Arc<Cap>,
) -> DuringResult<Arc<Cap>> { direct_resolution_facet: FacetId,
) -> ActorResult {
let queried_oid = sturdyref.parameters.oid.clone(); let queried_oid = sturdyref.parameters.oid.clone();
let handler = syndicate::entity(observer) let handler = syndicate::entity(observer)
.on_asserted(move |observer, t, a: AnyValue| { .on_asserted(move |observer, t, a: AnyValue| {
t.stop_facet(direct_resolution_facet);
let bindings = a.value().to_sequence()?; let bindings = a.value().to_sequence()?;
let key = bindings[0].value().to_bytestring()?; let key = bindings[0].value().to_bytestring()?;
let unattenuated_target = bindings[1].value().to_embedded()?; let unattenuated_target = bindings[1].value().to_embedded()?;
@ -152,27 +181,29 @@ fn handle_resolve_sturdyref(
Err(e) => { Err(e) => {
tracing::warn!(sturdyref = ?language().unparse(&sturdyref), tracing::warn!(sturdyref = ?language().unparse(&sturdyref),
"sturdyref failed validation: {}", e); "sturdyref failed validation: {}", e);
eventually_retract(observer.assert(t, language(), &gatekeeper::Resolved::Rejected( observer.assert(t, language(), &gatekeeper::Resolved::Rejected(
Box::new(gatekeeper::Rejected { Box::new(gatekeeper::Rejected {
detail: AnyValue::symbol("sturdyref-failed-validation"), detail: AnyValue::symbol("sturdyref-failed-validation"),
})))) })));
}, },
Ok(target) => { Ok(target) => {
tracing::trace!(sturdyref = ?language().unparse(&sturdyref), tracing::trace!(sturdyref = ?language().unparse(&sturdyref),
?target, ?target,
"sturdyref resolved"); "sturdyref resolved");
eventually_retract(observer.assert(t, language(), &gatekeeper::Resolved::Accepted { observer.assert(t, language(), &gatekeeper::Resolved::Accepted {
responder_session: target, responder_session: target,
})) });
} }
} }
Ok(None)
}) })
.create_cap(t); .create_cap(t);
eventually_retract(ds.assert(t, language(), &dataspace::Observe { ds.assert(t, language(), &dataspace::Observe {
// TODO: codegen plugin to generate pattern constructors // TODO: codegen plugin to generate pattern constructors
pattern: pattern!{<bind <ref { oid: #(&queried_oid), key: $ }> $ _>}, pattern: pattern!{<bind <ref { oid: #(&queried_oid), key: $ }> $ _>},
observer: handler, observer: handler,
})) });
Ok(())
} }
struct ValidatedNoiseSpec { struct ValidatedNoiseSpec {
@ -232,14 +263,16 @@ fn validate_noise_spec(
}) })
} }
fn handle_resolve_noise( fn await_bind_noise(
ds: &mut Arc<Cap>, ds: &mut Arc<Cap>,
t: &mut Activation, t: &mut Activation,
service_selector: AnyValue, service_selector: AnyValue,
initiator_session: Arc<Cap>, initiator_session: Arc<Cap>,
) -> DuringResult<Arc<Cap>> { direct_resolution_facet: FacetId,
) -> ActorResult {
let handler = syndicate::entity(()) let handler = syndicate::entity(())
.on_asserted_facet(move |_state, t, a: AnyValue| { .on_asserted_facet(move |_state, t, a: AnyValue| {
t.stop_facet(direct_resolution_facet);
let initiator_session = Arc::clone(&initiator_session); let initiator_session = Arc::clone(&initiator_session);
t.spawn_link(None, move |t| { t.spawn_link(None, move |t| {
let bindings = a.value().to_sequence()?; let bindings = a.value().to_sequence()?;
@ -250,13 +283,14 @@ fn handle_resolve_noise(
Ok(()) Ok(())
}) })
.create_cap(t); .create_cap(t);
eventually_retract(ds.assert(t, language(), &dataspace::Observe { ds.assert(t, language(), &dataspace::Observe {
// TODO: codegen plugin to generate pattern constructors // TODO: codegen plugin to generate pattern constructors
pattern: pattern!{ pattern: pattern!{
<bind <noise $spec:NoiseServiceSpec{ { service: #(&service_selector) } }> $service _> <bind <noise $spec:NoiseServiceSpec{ { service: #(&service_selector) } }> $service _>
}, },
observer: handler, observer: handler,
})) });
Ok(())
} }
struct ResponderDetails { struct ResponderDetails {

View File

@ -120,7 +120,7 @@ async fn main() -> ActorResult {
let gatekeeper = Cap::guard(Language::arc(), t.create( let gatekeeper = Cap::guard(Language::arc(), t.create(
syndicate::entity(Arc::clone(&server_config_ds)) syndicate::entity(Arc::clone(&server_config_ds))
.on_asserted(gatekeeper::handle_resolves))); .on_asserted_facet(gatekeeper::facet_handle_resolve)));
gatekeeper::handle_binds(t, &server_config_ds)?; gatekeeper::handle_binds(t, &server_config_ds)?;
let mut env = Map::new(); let mut env = Map::new();

View File

@ -27,7 +27,7 @@ pub fn on_demand(t: &mut Activation, ds: Arc<Cap>) {
fn run(t: &mut Activation, ds: Arc<Cap>, spec: Gatekeeper<AnyValue>) -> ActorResult { fn run(t: &mut Activation, ds: Arc<Cap>, spec: Gatekeeper<AnyValue>) -> ActorResult {
let resolver = t.create(syndicate::entity(Arc::clone(&spec.bindspace)) let resolver = t.create(syndicate::entity(Arc::clone(&spec.bindspace))
.on_asserted(gatekeeper::handle_resolves)); .on_asserted_facet(gatekeeper::facet_handle_resolve));
ds.assert(t, language(), &syndicate::schemas::service::ServiceObject { ds.assert(t, language(), &syndicate::schemas::service::ServiceObject {
service_name: language().unparse(&spec), service_name: language().unparse(&spec),
object: AnyValue::domain(Cap::guard(Language::arc(), resolver)), object: AnyValue::domain(Cap::guard(Language::arc(), resolver)),

View File

@ -1510,8 +1510,7 @@ impl Activation {
trace::FacetStopReason::ExplicitAction) trace::FacetStopReason::ExplicitAction)
} }
/// Arranges for the [`Facet`] named by `facet_id` to be stopped cleanly when `self` /// Cleanly stops the [`Facet`] named by `facet_id`.
/// commits.
/// ///
/// Equivalent to `self.stop_facet_and_continue(facet_id, None)`, except that the lack of a /// Equivalent to `self.stop_facet_and_continue(facet_id, None)`, except that the lack of a
/// continuation means that there's no need for this method to return `ActorResult`. /// continuation means that there's no need for this method to return `ActorResult`.
@ -1520,15 +1519,15 @@ impl Activation {
.expect("Non-failing stop_facet_and_continue") .expect("Non-failing stop_facet_and_continue")
} }
/// Arranges for the active facet to be stopped cleanly when `self` commits. /// Cleanly stops the active facet.
/// ///
/// Equivalent to `self.stop_facet(self.facet.facet_id)`. /// Equivalent to `self.stop_facet(self.facet_id())`.
pub fn stop(&mut self) { pub fn stop(&mut self) {
self.stop_facet(self.facet_id()) self.stop_facet(self.facet_id())
} }
/// Arranges for the active actor's root facet to be stopped cleanly when `self` commits; /// Cleanly stops the active actor's root facet.
/// this is one way to arrange a clean shutdown of the entire actor. /// This is one way to arrange a clean shutdown of the entire actor.
/// ///
/// Equivalent to `self.stop_facet(self.state.root)`. /// Equivalent to `self.stop_facet(self.state.root)`.
pub fn stop_root(&mut self) { pub fn stop_root(&mut self) {