use crate::circuit::routing::{RoutingTableReader, RoutingTableWriter, Service, ServiceId};
use crate::peer::{PeerAuthorizationToken, PeerTokenPair};
use crate::service::network::handlers::{
ServiceAddInstanceError, ServiceInstances, ServiceRemoveInstanceError,
};
const ADMIN_SERVICE_ID_PREFIX: &str = "admin::";
const ADMIN_CIRCUIT_ID: &str = "admin";
pub struct RoutingTableServiceInstances {
node_id: String,
routing_table_reader: Box<dyn RoutingTableReader>,
routing_table_writer: Box<dyn RoutingTableWriter>,
}
impl RoutingTableServiceInstances {
pub fn new(
node_id: String,
routing_table_reader: Box<dyn RoutingTableReader>,
routing_table_writer: Box<dyn RoutingTableWriter>,
) -> Self {
Self {
node_id,
routing_table_reader,
routing_table_writer,
}
}
}
impl ServiceInstances for RoutingTableServiceInstances {
fn add_service_instance(
&self,
service_id: ServiceId,
component_id: String,
) -> Result<(), ServiceAddInstanceError> {
let unique_id = service_id.clone();
let (circuit_name, service_id) = service_id.into_parts();
let circuit = self
.routing_table_reader
.get_circuit(&circuit_name)
.map_err(|err| ServiceAddInstanceError::InternalError {
context: err.reduce_to_string(),
source: None,
})?
.ok_or(ServiceAddInstanceError::CircuitDoesNotExist)?;
if !circuit
.roster()
.iter()
.any(|service| service.service_id() == service_id)
{
return Err(ServiceAddInstanceError::NotInCircuit);
}
let mut service = if !service_id.starts_with(ADMIN_SERVICE_ID_PREFIX) {
self.routing_table_reader
.get_service(&unique_id)
.map_err(|err| ServiceAddInstanceError::InternalError {
context: err.reduce_to_string(),
source: None,
})?
.ok_or(ServiceAddInstanceError::NotInCircuit)?
} else {
Service::new(
service_id,
ADMIN_CIRCUIT_ID.to_string(),
self.node_id.to_string(),
vec![],
)
};
if service.node_id() != self.node_id {
return Err(ServiceAddInstanceError::NotAllowed);
}
if service.peer_id().is_some() {
return Err(ServiceAddInstanceError::AlreadyRegistered);
}
service.set_peer_id(PeerTokenPair::new(
PeerAuthorizationToken::from_peer_id(&component_id),
PeerAuthorizationToken::from_peer_id(&self.node_id),
));
let mut writer = self.routing_table_writer.clone();
writer.add_service(unique_id, service).map_err(|err| {
ServiceAddInstanceError::InternalError {
context: err.reduce_to_string(),
source: None,
}
})?;
Ok(())
}
fn remove_service_instance(
&self,
service_id: ServiceId,
_component_id: String,
) -> Result<(), ServiceRemoveInstanceError> {
let mut service = self
.routing_table_reader
.get_service(&service_id)
.map_err(|err| ServiceRemoveInstanceError::InternalError {
context: err.reduce_to_string(),
source: None,
})?
.ok_or(ServiceRemoveInstanceError::NotRegistered)?;
if service.peer_id().is_none() {
return Err(ServiceRemoveInstanceError::NotRegistered);
}
service.remove_peer_id();
let mut writer = self.routing_table_writer.clone();
writer.add_service(service_id, service).map_err(|err| {
ServiceRemoveInstanceError::InternalError {
context: err.reduce_to_string(),
source: None,
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::routing::AuthorizationType;
use crate::circuit::routing::{
memory::RoutingTable, Circuit, CircuitNode, RoutingTableWriter, Service,
};
#[test]
fn test_add_service_instance_circuit_does_not_exist() {
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader, writer);
let res = service_instances.add_service_instance(
ServiceId::new("alpha".into(), "abc".into()),
"my_component".into(),
);
assert!(matches!(
res,
Err(ServiceAddInstanceError::CircuitDoesNotExist)
));
}
#[test]
fn test_add_service_instance_not_in_circuit() {
let (circuit, nodes) = build_circuit();
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let mut writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
writer
.add_circuit(circuit.circuit_id().into(), circuit, nodes)
.expect("Unable to add circuit");
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader, writer);
let res = service_instances.add_service_instance(
ServiceId::new("alpha".into(), "BAD".into()),
"my_component".into(),
);
assert!(matches!(res, Err(ServiceAddInstanceError::NotInCircuit)));
}
#[test]
fn test_add_service_instance_already_registered() {
let (circuit, nodes) = build_circuit();
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let mut writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
writer
.add_circuit(circuit.circuit_id().into(), circuit, nodes)
.expect("Unable to add circuit");
let id = ServiceId::new("alpha".into(), "abc".into());
let mut service = reader
.get_service(&id)
.expect("Unable to get service")
.expect("Missing service");
service.set_peer_id(PeerTokenPair::new(
PeerAuthorizationToken::from_peer_id("abc_network"),
PeerAuthorizationToken::from_peer_id("123"),
));
writer
.add_service(id, service)
.expect("Unable to add service");
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader, writer);
let res = service_instances.add_service_instance(
ServiceId::new("alpha".into(), "abc".into()),
"my_component".into(),
);
assert!(matches!(
res,
Err(ServiceAddInstanceError::AlreadyRegistered)
));
}
#[test]
fn test_add_service_instance_accepted() {
let (circuit, nodes) = build_circuit();
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let mut writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
writer
.add_circuit(circuit.circuit_id().into(), circuit, nodes)
.expect("Unable to add circuit");
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader.clone(), writer);
let id = ServiceId::new("alpha".into(), "abc".into());
let res = service_instances.add_service_instance(id.clone(), "my_component".into());
assert!(matches!(res, Ok(())));
assert_eq!(
reader
.get_service(&id)
.expect("cannot check if it has the service")
.expect("no service returned")
.peer_id(),
&Some(PeerTokenPair::new(
PeerAuthorizationToken::from_peer_id("my_component"),
PeerAuthorizationToken::from_peer_id("123"),
))
);
}
#[test]
fn test_remove_service_instance_not_registred() {
let (circuit, nodes) = build_circuit();
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let mut writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
writer
.add_circuit(circuit.circuit_id().into(), circuit, nodes)
.expect("Unable to add circuit");
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader.clone(), writer);
let res = service_instances.remove_service_instance(
ServiceId::new("alpha".into(), "abc".into()),
"my_component".into(),
);
assert!(matches!(
res,
Err(ServiceRemoveInstanceError::NotRegistered)
));
}
#[test]
fn test_remove_service_instance_accepted() {
let (circuit, nodes) = build_circuit();
let table = RoutingTable::default();
let reader: Box<dyn RoutingTableReader> = Box::new(table.clone());
let mut writer: Box<dyn RoutingTableWriter> = Box::new(table.clone());
writer
.add_circuit(circuit.circuit_id().into(), circuit, nodes)
.expect("Unable to add circuit");
let id = ServiceId::new("alpha".into(), "abc".into());
let mut service = reader
.get_service(&id)
.expect("Unable to get service")
.expect("Missing service");
service.set_peer_id(PeerTokenPair::new(
PeerAuthorizationToken::from_peer_id("abc_network"),
PeerAuthorizationToken::from_peer_id("123"),
));
writer
.add_service(id.clone(), service)
.expect("Unable to add service");
let service_instances =
RoutingTableServiceInstances::new("123".to_string(), reader.clone(), writer);
let res = service_instances.remove_service_instance(id.clone(), "my_component".into());
assert!(matches!(res, Ok(())));
assert!(reader
.get_service(&id)
.expect("cannot check if it has the service")
.expect("no service returned")
.peer_id()
.is_none());
}
fn build_circuit() -> (Circuit, Vec<CircuitNode>) {
let node_123 = CircuitNode::new("123".to_string(), vec!["123.0.0.1:0".to_string()], None);
let node_345 = CircuitNode::new("345".to_string(), vec!["123.0.0.1:1".to_string()], None);
let service_abc = Service::new(
"abc".to_string(),
"test".to_string(),
"123".to_string(),
vec![],
);
let service_def = Service::new(
"def".to_string(),
"test".to_string(),
"345".to_string(),
vec![],
);
let circuit = Circuit::new(
"alpha".into(),
vec![service_abc.clone(), service_def.clone()],
vec!["123".into(), "345".into()],
AuthorizationType::Trust,
);
(circuit, vec![node_123, node_345])
}
}