use crate::{
InternalError,
domain::policy::pure::{
PolicyError,
fleet_activation::{require_prepared_nonroot_endpoint, require_prepared_root_endpoint},
},
dto::fleet_activation::{FleetActivationPhase, FleetActivationStatusResponse},
ids::EndpointCall,
ops::{
runtime::{env::EnvOps, fleet_activation::FleetActivationRuntimeOps},
storage::{StorageOpsError, fleet_activation::FleetActivationOps},
},
};
pub struct FleetActivationWorkflow;
impl FleetActivationWorkflow {
pub fn status() -> Result<FleetActivationStatusResponse, InternalError> {
FleetActivationOps::status(EnvOps::is_root())
.map_err(StorageOpsError::from)
.map_err(Into::into)
}
pub fn require_active() -> Result<(), InternalError> {
FleetActivationOps::require_active(EnvOps::is_root())
.map_err(StorageOpsError::from)
.map_err(Into::into)
}
pub fn require_endpoint_allowed(call: EndpointCall) -> Result<(), InternalError> {
let is_root = EnvOps::canister_role()?.is_root();
if !is_root && FleetActivationRuntimeOps::is_standalone_local() {
return Ok(());
}
let status = FleetActivationOps::status(is_root)
.map_err(StorageOpsError::from)
.map_err(InternalError::from)?;
require_endpoint_for_phase(is_root, status.phase, call).map_err(InternalError::from)
}
}
fn require_endpoint_for_phase(
is_root: bool,
phase: FleetActivationPhase,
call: EndpointCall,
) -> Result<(), PolicyError> {
match phase {
FleetActivationPhase::Prepared if is_root => {
require_prepared_root_endpoint(call).map_err(PolicyError::from)
}
FleetActivationPhase::Prepared => {
require_prepared_nonroot_endpoint(call).map_err(PolicyError::from)
}
FleetActivationPhase::Active => Ok(()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ids::{EndpointCallKind, EndpointId};
fn call(name: &'static str, kind: EndpointCallKind) -> EndpointCall {
EndpointCall {
endpoint: EndpointId::new(name),
kind,
}
}
#[test]
fn active_admits_ordinary_handlers_but_prepared_delegates_to_exact_policy() {
let ordinary = call("application_update", EndpointCallKind::Update);
assert!(require_endpoint_for_phase(true, FleetActivationPhase::Active, ordinary).is_ok());
assert!(matches!(
require_endpoint_for_phase(true, FleetActivationPhase::Prepared, ordinary),
Err(PolicyError::FleetActivationPolicy(_))
));
assert!(matches!(
require_endpoint_for_phase(false, FleetActivationPhase::Prepared, ordinary),
Err(PolicyError::FleetActivationPolicy(_))
));
}
}