clawft_kernel/workload_ctl/
plane_node_admin.rs1use serde_json::{Value, json};
11
12use clawft_types::placement::TrustTier;
13
14use super::msg::method;
15use super::plane::{PlacementControlPlane, PlaneError};
16
17pub const EVENT_NODE_ADMIN_SENT: &str = "node_admin.sent";
19pub const EVENT_NODE_ADMIN_OUTCOME: &str = "node_admin.outcome";
21
22impl PlacementControlPlane {
23 pub async fn node_admin(&self, node_id: &str, m: &str, body: Value) -> Result<Value, PlaneError> {
25 if !method::is_node_admin(m) {
26 return Err(PlaneError::Invalid(format!("{m} is not a node-admin method")));
27 }
28 let (target, _) = self.target(node_id)?;
29 if method::mutates(m) && target.tier < TrustTier::Pinned {
30 return Err(PlaneError::Governance(format!(
31 "{m} needs an operator-pinned peer; {node_id} is {:?} (set its tier to pinned in workload-peers.json)",
32 target.tier
33 )));
34 }
35 self.ensure_described(node_id).await;
36 let decision_id = method::mutates(m).then(|| {
37 self.chain_event(EVENT_NODE_ADMIN_SENT, json!({ "node": node_id, "method": m }))
38 });
39 let out = self.call(node_id, m, decision_id.clone(), body).await;
40 self.chain_event(
41 EVENT_NODE_ADMIN_OUTCOME,
42 json!({ "node": node_id, "method": m, "decision_id": decision_id,
43 "ok": out.is_ok(), "error": out.as_ref().err().map(|e| e.to_string()) }),
44 );
45 out
46 }
47}