use crate::circuit::service::{Service, ServiceId, SplinterNode};
use crate::circuit::{ServiceDefinition, SplinterState};
use crate::service::network::handlers::{
ServiceAddInstanceError, ServiceInstances, ServiceRemoveInstanceError,
};
pub struct SplinterStateServiceInstances {
splinter_node: SplinterNode,
state: SplinterState,
}
impl SplinterStateServiceInstances {
pub fn new(splinter_node: SplinterNode, state: SplinterState) -> Self {
Self {
splinter_node,
state,
}
}
}
impl ServiceInstances for SplinterStateServiceInstances {
fn add_service_instance(
&self,
service_id: ServiceId,
component_id: String,
) -> Result<(), ServiceAddInstanceError> {
let has_service = self.state.has_service(&service_id).map_err(|err| {
ServiceAddInstanceError::InternalError {
context: format!(
"unable to check if service {} is already registered",
service_id
),
source: Some(Box::new(err)),
}
})?;
if has_service {
return Err(ServiceAddInstanceError::AlreadyRegistered);
}
let unique_id = service_id.clone();
let (circuit_name, service_id) = service_id.into_parts();
let circuit = self
.state
.circuit(&circuit_name)
.map_err(|err| ServiceAddInstanceError::InternalError {
context: format!("unable to load circuit information for {}", unique_id),
source: Some(Box::new(err)),
})?
.ok_or(ServiceAddInstanceError::CircuitDoesNotExist)?;
let service_def = if !service_id.starts_with("admin::") {
circuit
.roster()
.iter()
.find(|service| service.service_id == service_id)
.cloned()
} else {
Some(
ServiceDefinition::builder(service_id.clone(), "admin".into())
.with_allowed_nodes(vec![self.splinter_node.id().to_string()])
.build(),
)
};
if let Some(service_def) = service_def {
if !service_def
.allowed_nodes
.iter()
.any(|node_id| node_id == self.splinter_node.id())
{
return Err(ServiceAddInstanceError::NotAllowed);
}
let service = Service::new(
service_id.clone(),
Some(component_id),
self.splinter_node.clone(),
);
self.state.add_service(unique_id, service).map_err(|err| {
ServiceAddInstanceError::InternalError {
context: format!("unable to add service {}", service_id),
source: Some(Box::new(err)),
}
})?;
} else {
return Err(ServiceAddInstanceError::NotInCircuit);
}
Ok(())
}
fn remove_service_instance(
&self,
service_id: ServiceId,
_component_id: String,
) -> Result<(), ServiceRemoveInstanceError> {
let has_service = !self.state.has_service(&service_id).map_err(|err| {
ServiceRemoveInstanceError::InternalError {
context: format!("unable to check if service {} is registered", service_id),
source: Some(Box::new(err)),
}
})?;
if has_service {
return Err(ServiceRemoveInstanceError::NotRegistered);
}
self.state
.remove_service(&service_id)
.map(|_| ())
.map_err(|err| ServiceRemoveInstanceError::InternalError {
context: format!("unable to remove service {}", service_id),
source: Some(Box::new(err)),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::circuit::directory::CircuitDirectory;
use crate::circuit::{AuthorizationType, Circuit, DurabilityType, PersistenceType, RouteType};
#[test]
fn test_add_service_instance_circuit_does_not_exist() {
let circuit_directory = CircuitDirectory::new();
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service_instances = SplinterStateServiceInstances::new(splinter_node, state);
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 = build_circuit();
let mut circuit_directory = CircuitDirectory::new();
circuit_directory.add_circuit("alpha".into(), circuit);
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service_instances = SplinterStateServiceInstances::new(splinter_node, state);
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 = build_circuit();
let mut circuit_directory = CircuitDirectory::new();
circuit_directory.add_circuit("alpha".into(), circuit);
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service = Service::new(
"abc".to_string(),
Some("abc_network".to_string()),
splinter_node.clone(),
);
let id = ServiceId::new("alpha".into(), "abc".into());
state.add_service(id.clone(), service).unwrap();
let service_instances = SplinterStateServiceInstances::new(splinter_node, state);
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 = build_circuit();
let mut circuit_directory = CircuitDirectory::new();
circuit_directory.add_circuit("alpha".to_string(), circuit);
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service_instances = SplinterStateServiceInstances::new(splinter_node, state.clone());
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!(state
.has_service(&id)
.expect("cannot check if it has the service"));
}
#[test]
fn test_remove_service_instance_not_registred() {
let circuit = build_circuit();
let mut circuit_directory = CircuitDirectory::new();
circuit_directory.add_circuit("alpha".into(), circuit);
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service_instances = SplinterStateServiceInstances::new(splinter_node, state);
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 = build_circuit();
let mut circuit_directory = CircuitDirectory::new();
circuit_directory.add_circuit("alpha".into(), circuit);
let state = SplinterState::new("memory".to_string(), circuit_directory);
let splinter_node = SplinterNode::new("123".into(), vec!["tcp://127.0.0.1:0".into()]);
let service = Service::new(
"abc".to_string(),
Some("abc_network".to_string()),
splinter_node.clone(),
);
let id = ServiceId::new("alpha".into(), "abc".into());
state.add_service(id.clone(), service).unwrap();
let service_instances = SplinterStateServiceInstances::new(splinter_node, state.clone());
let res = service_instances.remove_service_instance(id.clone(), "my_component".into());
assert!(matches!(res, Ok(())));
assert!(!state
.has_service(&id)
.expect("cannot check if it has the service"));
}
fn build_circuit() -> Circuit {
let service_abc = ServiceDefinition::builder("abc".into(), "test".into())
.with_allowed_nodes(vec!["123".to_string()])
.build();
let service_def = ServiceDefinition::builder("def".into(), "test".into())
.with_allowed_nodes(vec!["345".to_string()])
.build();
let circuit = Circuit::builder()
.with_id("alpha".into())
.with_auth(AuthorizationType::Trust)
.with_members(vec!["123".into(), "345".into()])
.with_roster(vec![service_abc, service_def])
.with_persistence(PersistenceType::Any)
.with_durability(DurabilityType::NoDurability)
.with_routes(RouteType::Any)
.with_circuit_management_type("service_connect_test_app".into())
.build()
.expect("Should have built a correct circuit");
circuit
}
}