appcore_supervisor/
service.rs1use crate::{
14 DependencyRequirement, RestartPolicy, ServiceActivationState, ServiceHealth,
15 ServiceRuntimeState, SupervisorResult,
16};
17use std::time::Duration;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ManagedResource {
22 Runtime,
24 Security,
26 Scheduler,
28 PeerRpc,
30 ControlPlane,
32 Jobs,
34 Update,
36 AuthServer,
38 Metrics,
40 Observation,
42 Sync,
44 Http,
46 Gateway,
48 Worker,
50 Queue,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct ServiceDependency {
57 service_id: String,
58 requirement: DependencyRequirement,
59}
60
61impl ServiceDependency {
62 pub fn new(
64 service_id: impl Into<String>,
65 requirement: DependencyRequirement,
66 ) -> SupervisorResult<Self> {
67 let dependency = Self {
68 service_id: service_id.into(),
69 requirement,
70 };
71 validate_name(&dependency.service_id)?;
72 Ok(dependency)
73 }
74
75 pub fn service_id(&self) -> &str {
77 &self.service_id
78 }
79
80 pub fn requirement(&self) -> DependencyRequirement {
82 self.requirement
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct ServiceDescriptor {
89 name: String,
90 resource: ManagedResource,
91 dependencies: Vec<ServiceDependency>,
92 restart_policy: RestartPolicy,
93 activation: ServiceActivationState,
94 critical: bool,
95}
96
97impl ServiceDescriptor {
98 pub fn new(
100 name: impl Into<String>,
101 resource: ManagedResource,
102 restart_policy: RestartPolicy,
103 ) -> SupervisorResult<Self> {
104 let descriptor = Self {
105 name: name.into(),
106 resource,
107 dependencies: Vec::new(),
108 restart_policy,
109 activation: ServiceActivationState::Enabled,
110 critical: true,
111 };
112 descriptor.validate()?;
113 Ok(descriptor)
114 }
115
116 pub fn with_dependency(self, dependency: impl Into<String>) -> SupervisorResult<Self> {
118 self.add_dependency(ServiceDependency::new(
119 dependency,
120 DependencyRequirement::DegradedAllowed,
121 )?)
122 }
123
124 pub fn with_dependency_requirement(
126 self,
127 dependency: impl Into<String>,
128 requirement: DependencyRequirement,
129 ) -> SupervisorResult<Self> {
130 self.add_dependency(ServiceDependency::new(dependency, requirement)?)
131 }
132
133 fn add_dependency(mut self, dependency: ServiceDependency) -> SupervisorResult<Self> {
134 if dependency.service_id == self.name {
135 return Err(crate::SupervisorError::InvalidConfiguration(
136 "a service cannot depend on itself".to_string(),
137 ));
138 }
139 if !self
140 .dependencies
141 .iter()
142 .any(|current| current.service_id == dependency.service_id)
143 {
144 self.dependencies.push(dependency);
145 self.dependencies
146 .sort_by(|left, right| left.service_id.cmp(&right.service_id));
147 }
148 Ok(self)
149 }
150
151 pub fn with_activation(mut self, activation: ServiceActivationState) -> Self {
153 self.activation = activation;
154 self
155 }
156
157 pub fn with_critical(mut self, critical: bool) -> Self {
159 self.critical = critical;
160 self
161 }
162
163 pub fn name(&self) -> &str {
165 &self.name
166 }
167
168 pub fn resource(&self) -> ManagedResource {
170 self.resource
171 }
172
173 pub fn dependencies(&self) -> &[ServiceDependency] {
175 &self.dependencies
176 }
177
178 pub fn restart_policy(&self) -> RestartPolicy {
180 self.restart_policy
181 }
182
183 pub fn activation(&self) -> ServiceActivationState {
185 self.activation
186 }
187
188 pub fn is_critical(&self) -> bool {
190 self.critical
191 }
192
193 pub fn validate(&self) -> SupervisorResult<()> {
195 validate_name(&self.name)?;
196 self.restart_policy.validate()
197 }
198}
199
200pub trait ManagedService: Send + Sync {
202 fn descriptor(&self) -> &ServiceDescriptor;
204 fn start(&self) -> SupervisorResult<()>;
206 fn stop(&self, timeout: Duration) -> SupervisorResult<()>;
208 fn health(&self) -> ServiceHealth;
210 fn runtime_state(&self) -> ServiceRuntimeState {
212 match self.health() {
213 ServiceHealth::Starting => ServiceRuntimeState::Starting,
214 ServiceHealth::Ready | ServiceHealth::Healthy | ServiceHealth::Degraded => {
215 ServiceRuntimeState::Running
216 }
217 ServiceHealth::Stopping => ServiceRuntimeState::Stopping,
218 ServiceHealth::Failed => ServiceRuntimeState::Failed,
219 ServiceHealth::Unknown => ServiceRuntimeState::Stopped,
220 }
221 }
222}
223
224fn validate_name(name: &str) -> SupervisorResult<()> {
225 if name.is_empty()
226 || name.len() > 128
227 || !name
228 .bytes()
229 .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'-' | b'_'))
230 {
231 return Err(crate::SupervisorError::InvalidConfiguration(
232 "service name is invalid".to_string(),
233 ));
234 }
235 Ok(())
236}