use super::{Lifecycle, LifecycleError, Placer, ServiceState, digest};
impl Lifecycle {
pub(super) fn place_children(
&self,
placer: &dyn Placer,
id: &str,
subjects: &[String],
) -> Result<(), LifecycleError> {
let network = ("liminal.network", self.network.as_str());
let children = std::iter::once(("liminal.service", id)).chain(
subjects
.iter()
.map(|subject| ("liminal.subject", subject.as_str())),
);
for child in children {
let operation = format!("op-{}", digest(&["liminal-place/v1", child.0, child.1]));
placer
.place(&operation, child, network)
.map_err(LifecycleError::Placement)?;
tracing::info!(
service = id,
kind = child.0,
child = child.1,
"SERVICE_PLACED"
);
}
Ok(())
}
#[must_use]
pub fn catch_up(&self) -> Vec<(String, String)> {
let Some(placer) = self.placer.as_ref() else {
return Vec::new();
};
let pending: Vec<(String, Vec<String>)> = match self.lock() {
Ok(registry) => registry
.records
.values()
.filter(|record| !record.placed && record.state != ServiceState::Retired)
.map(|record| (record.id.clone(), record.subjects.clone()))
.collect(),
Err(error) => return vec![(String::new(), error.to_string())],
};
let mut refused = Vec::new();
let mut marked = Vec::new();
for (id, subjects) in pending {
match self.place_children(placer.as_ref(), &id, &subjects) {
Ok(()) => marked.push(id),
Err(error) => refused.push((id, error.to_string())),
}
}
if marked.is_empty() {
return refused;
}
if let Err(error) = self.mark_placed(&marked) {
refused.extend(marked.into_iter().map(|id| (id, error.to_string())));
}
refused
}
fn mark_placed(&self, ids: &[String]) -> Result<(), LifecycleError> {
let mut registry = self.lock()?;
let mut snapshot = super::site::storage::Snapshot {
records: registry.records.clone(),
construction: registry.construction.clone(),
};
for id in ids {
if let Some(record) = snapshot.records.get_mut(id) {
record.placed = true;
}
}
let committed = super::site::storage::commit(self, &mut registry, &snapshot);
drop(registry);
committed
}
}