clawft_kernel/workload_kind/
mod.rs1use std::collections::BTreeMap;
14use std::path::Path;
15use std::sync::Arc;
16
17#[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
18use clawft_types::placement::engine::WorkloadSpec;
19
20#[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
21use crate::workload_ctl::PlaneError;
22#[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
23use crate::workload_pkg::TrustAnchors;
24use crate::workload_pkg::{ManifestEnvelope, ManifestError};
25#[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
26use crate::workload_runtime::VerifiedWorkload;
27
28mod cog;
29mod health;
30mod project;
31
32#[cfg(test)]
33mod tests;
34#[cfg(all(test, feature = "workload-runtime", feature = "mesh", unix, feature = "native"))]
35mod project_tests;
36
37pub use cog::CogKind;
38pub use health::{Health, HealthSample, HealthSpec, SampleState, judge_process};
39#[cfg(all(feature = "workload-runtime", feature = "mesh", unix, feature = "native"))]
40pub use project::{ProjectFacts, ProjectPrepareError, prepare_project};
41pub use project::{KIND_PROJECT, ProjectKind, project_healthy};
42#[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
43pub use cog::load_cog_shaped;
44
45pub const MAX_KIND_LEN: usize = 32;
48
49#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
52#[error("unknown workload kind {0:?}")]
53pub struct UnknownKind(pub String);
54
55#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
57#[error("workload kind {0:?} is already registered")]
58pub struct DuplicateKind(pub String);
59
60pub trait WorkloadKind: Send + Sync {
62 fn id(&self) -> &'static str;
64
65 fn validate(&self, envelope: &ManifestEnvelope) -> Result<(), ManifestError>;
68
69 fn health(&self) -> HealthSpec {
72 HealthSpec::default()
73 }
74
75 fn judge(&self, sample: &HealthSample) -> Health {
78 judge_process(sample)
79 }
80
81 fn migratable(&self) -> bool {
86 true
87 }
88
89 #[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
90 fn load(
95 &self,
96 package_dir: &Path,
97 anchors: &TrustAnchors,
98 kinds: &KindRegistry,
99 ) -> Result<VerifiedWorkload, PlaneError>;
100
101 #[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
102 fn spec(&self, workload: &VerifiedWorkload) -> Result<WorkloadSpec, String>;
104
105 #[cfg(all(feature = "workload-runtime", feature = "mesh", unix))]
106 fn adapters(&self) -> &'static [&'static str];
109}
110
111#[derive(Clone, Default)]
113pub struct KindRegistry {
114 kinds: BTreeMap<&'static str, Arc<dyn WorkloadKind>>,
115}
116
117impl KindRegistry {
118 pub fn new() -> Self {
120 Self::default()
121 }
122
123 pub fn builtin() -> Self {
125 let mut r = Self::new();
126 r.register(Arc::new(CogKind))
127 .expect("builtin kind ids are distinct");
128 r.register(Arc::new(ProjectKind))
129 .expect("builtin kind ids are distinct");
130 r
131 }
132
133 pub fn register(&mut self, kind: Arc<dyn WorkloadKind>) -> Result<(), DuplicateKind> {
135 let id = kind.id();
136 if self.kinds.contains_key(id) {
137 return Err(DuplicateKind(id.to_string()));
138 }
139 self.kinds.insert(id, kind);
140 Ok(())
141 }
142
143 pub fn get(&self, id: &str) -> Option<&Arc<dyn WorkloadKind>> {
145 self.kinds.get(id)
146 }
147
148 pub fn require(&self, id: &str) -> Result<&Arc<dyn WorkloadKind>, UnknownKind> {
150 self.get(id)
151 .ok_or_else(|| UnknownKind(id.chars().take(MAX_KIND_LEN).collect()))
152 }
153
154 pub fn ids(&self) -> Vec<&'static str> {
156 self.kinds.keys().copied().collect()
157 }
158}
159
160impl std::fmt::Debug for KindRegistry {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.debug_set().entries(self.kinds.keys()).finish()
163 }
164}
165
166pub fn validate_envelope(
169 registry: &KindRegistry,
170 envelope: &ManifestEnvelope,
171) -> Result<(), ManifestError> {
172 registry.require(&envelope.kind)?.validate(envelope)
173}
174
175pub fn peek_manifest_kind(package_dir: &Path) -> Option<String> {
182 use crate::workload_pkg::verify::read_bounded;
183 use crate::workload_pkg::{MANIFEST_FILE, MAX_MANIFEST_BYTES};
184 let bytes = read_bounded(
185 &package_dir.join(MANIFEST_FILE),
186 MANIFEST_FILE,
187 MAX_MANIFEST_BYTES as u64,
188 )
189 .ok()?;
190 peek_kind_in(&bytes)
191}
192
193pub fn peek_kind_in(manifest: &[u8]) -> Option<String> {
196 use crate::workload_pkg::manifest::valid_token;
197 let doc: serde_json::Value = serde_json::from_slice(manifest).ok()?;
198 let kind = doc.get("kind")?.as_str()?;
199 valid_token(kind, MAX_KIND_LEN).then(|| kind.to_string())
200}