feat: add worker runtime registry overview
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,15 @@ pub enum Error {
|
||||
MissingFrontmatter(String),
|
||||
#[error("unknown local host `{0}`")]
|
||||
UnknownHost(String),
|
||||
#[error("unknown local worker `{0}`")]
|
||||
UnknownWorker(String),
|
||||
#[error("invalid runtime {kind} `{value}`")]
|
||||
InvalidRuntimeIdentifier { kind: String, value: String },
|
||||
#[error("runtime `{runtime_id}` does not support `{capability}`")]
|
||||
RuntimeCapabilityUnsupported {
|
||||
runtime_id: String,
|
||||
capability: String,
|
||||
},
|
||||
#[error("unknown local repository `{0}`")]
|
||||
UnknownRepository(String),
|
||||
#[error("workspace identity error: {0}")]
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::process::{Command, Output};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::hosts::RuntimeDiagnostic;
|
||||
use crate::hosts::{DiagnosticSeverity, RuntimeDiagnostic};
|
||||
|
||||
const LEGACY_LOCAL_REPOSITORY_ID: &str = "local";
|
||||
const LOCAL_REPOSITORY_PREFIX: &str = "local-";
|
||||
@@ -340,7 +340,11 @@ fn truncate_field(value: &str, limit: usize) -> String {
|
||||
fn diagnostic(code: &str, severity: &str, message: String) -> RuntimeDiagnostic {
|
||||
RuntimeDiagnostic {
|
||||
code: code.to_string(),
|
||||
severity: severity.to_string(),
|
||||
severity: match severity {
|
||||
"error" => DiagnosticSeverity::Error,
|
||||
"warning" => DiagnosticSeverity::Warning,
|
||||
_ => DiagnosticSeverity::Info,
|
||||
},
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::hosts::{
|
||||
HostSummary, LocalRuntimeBridge, RuntimeDiagnostic, WorkerSummary, WorkspaceWorkerRuntime,
|
||||
DiagnosticSeverity, HostSummary, LocalPodRuntime, RuntimeDiagnostic, RuntimeSummary,
|
||||
WorkerRuntimeRegistry, WorkerSummary,
|
||||
};
|
||||
use crate::identity::WorkspaceIdentity;
|
||||
use crate::records::{
|
||||
@@ -63,7 +64,7 @@ pub struct WorkspaceApi {
|
||||
config: ServerConfig,
|
||||
store: Arc<dyn ControlPlaneStore>,
|
||||
records: LocalProjectRecordReader,
|
||||
runtime: Arc<dyn WorkspaceWorkerRuntime>,
|
||||
runtime: Arc<WorkerRuntimeRegistry>,
|
||||
}
|
||||
|
||||
impl WorkspaceApi {
|
||||
@@ -77,11 +78,11 @@ impl WorkspaceApi {
|
||||
updated_at: config.workspace_created_at.clone(),
|
||||
})
|
||||
.await?;
|
||||
let runtime = Arc::new(LocalRuntimeBridge::new(
|
||||
let runtime = Arc::new(WorkerRuntimeRegistry::for_local_pods(LocalPodRuntime::new(
|
||||
config.workspace_id.clone(),
|
||||
config.workspace_root.clone(),
|
||||
config.local_runtime_data_dir.clone(),
|
||||
));
|
||||
)));
|
||||
Ok(Self {
|
||||
records: LocalProjectRecordReader::new(config.workspace_root.clone()),
|
||||
config,
|
||||
@@ -125,6 +126,7 @@ pub fn build_router(api: WorkspaceApi) -> Router {
|
||||
get(repository_tickets),
|
||||
)
|
||||
.route("/api/hosts", get(list_hosts))
|
||||
.route("/api/runtimes", get(list_runtimes))
|
||||
.route("/api/workers", get(list_workers))
|
||||
.route("/api/hosts/{host_id}/workers", get(list_host_workers))
|
||||
.fallback(get(static_or_spa_fallback))
|
||||
@@ -381,7 +383,7 @@ async fn repository_tickets(
|
||||
source: "workspace_local_ticket_fallback".to_string(),
|
||||
diagnostics: vec![RuntimeDiagnostic {
|
||||
code: "repository_ticket_target_metadata_absent".to_string(),
|
||||
severity: "info".to_string(),
|
||||
severity: DiagnosticSeverity::Info,
|
||||
message: "Ticket target Repository metadata is not available yet; Kanban groups all workspace-local Tickets by state as a read-only fallback.".to_string(),
|
||||
}],
|
||||
}))
|
||||
@@ -396,11 +398,25 @@ async fn list_hosts(
|
||||
workspace_id: api.config.workspace_id,
|
||||
limit,
|
||||
items: runtime_hosts.items,
|
||||
source: "worker_runtime".to_string(),
|
||||
source: "worker_runtime_registry".to_string(),
|
||||
diagnostics: runtime_hosts.diagnostics,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn list_runtimes(
|
||||
State(api): State<WorkspaceApi>,
|
||||
) -> ApiResult<Json<RuntimeListResponse<RuntimeSummary>>> {
|
||||
let limit = api.config.max_records.min(200);
|
||||
let runtimes = api.runtime.list_runtimes(limit);
|
||||
Ok(Json(RuntimeListResponse {
|
||||
workspace_id: api.config.workspace_id,
|
||||
limit,
|
||||
items: runtimes.items,
|
||||
source: "worker_runtime_registry".to_string(),
|
||||
diagnostics: runtimes.diagnostics,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn list_workers(
|
||||
State(api): State<WorkspaceApi>,
|
||||
) -> ApiResult<Json<RuntimeListResponse<WorkerSummary>>> {
|
||||
@@ -411,16 +427,18 @@ async fn list_host_workers(
|
||||
State(api): State<WorkspaceApi>,
|
||||
AxumPath(host_id): AxumPath<String>,
|
||||
) -> ApiResult<Json<RuntimeListResponse<WorkerSummary>>> {
|
||||
let runtime_hosts = api.runtime.list_hosts(1);
|
||||
let expected_host_id = runtime_hosts
|
||||
.items
|
||||
.first()
|
||||
.map(|host| host.host_id.as_str())
|
||||
.ok_or_else(|| Error::UnknownHost(host_id.clone()))?;
|
||||
if host_id != expected_host_id {
|
||||
return Err(Error::UnknownHost(host_id).into());
|
||||
}
|
||||
workers_response(api).map(Json)
|
||||
let limit = api.config.max_records.min(200);
|
||||
let runtime_workers = api
|
||||
.runtime
|
||||
.list_workers_for_host(&host_id, limit)
|
||||
.map_err(|err| err.into_error())?;
|
||||
Ok(Json(RuntimeListResponse {
|
||||
workspace_id: api.config.workspace_id,
|
||||
limit,
|
||||
items: runtime_workers.items,
|
||||
source: "worker_runtime_registry".to_string(),
|
||||
diagnostics: runtime_workers.diagnostics,
|
||||
}))
|
||||
}
|
||||
|
||||
fn workers_response(api: WorkspaceApi) -> ApiResult<RuntimeListResponse<WorkerSummary>> {
|
||||
@@ -430,7 +448,7 @@ fn workers_response(api: WorkspaceApi) -> ApiResult<RuntimeListResponse<WorkerSu
|
||||
workspace_id: api.config.workspace_id,
|
||||
limit,
|
||||
items: runtime_workers.items,
|
||||
source: "worker_runtime".to_string(),
|
||||
source: "worker_runtime_registry".to_string(),
|
||||
diagnostics: runtime_workers.diagnostics,
|
||||
})
|
||||
}
|
||||
@@ -589,11 +607,14 @@ impl From<Error> for ApiError {
|
||||
impl IntoResponse for ApiError {
|
||||
fn into_response(self) -> Response {
|
||||
let status = match &self.0 {
|
||||
Error::InvalidRuntimeIdentifier { .. } => StatusCode::BAD_REQUEST,
|
||||
Error::InvalidRecordId(_)
|
||||
| Error::MissingFrontmatter(_)
|
||||
| Error::UnknownHost(_)
|
||||
| Error::UnknownWorker(_)
|
||||
| Error::UnknownRepository(_) => StatusCode::NOT_FOUND,
|
||||
Error::Ticket(_) => StatusCode::NOT_FOUND,
|
||||
Error::RuntimeCapabilityUnsupported { .. } => StatusCode::NOT_IMPLEMENTED,
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
};
|
||||
(
|
||||
@@ -703,18 +724,30 @@ mod tests {
|
||||
assert_eq!(unknown_repository_response.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let hosts = get_json(app.clone(), "/api/hosts").await;
|
||||
assert_eq!(hosts["source"], "worker_runtime_registry");
|
||||
assert_eq!(hosts["items"][0]["runtime_id"], "local-pod-runtime");
|
||||
assert_eq!(hosts["items"][0]["host_id"], TEST_REPOSITORY_ID);
|
||||
assert_eq!(hosts["items"][0]["kind"], "local_host");
|
||||
assert_eq!(hosts["items"][0]["kind"], "local-pod-host");
|
||||
assert_eq!(
|
||||
hosts["items"][0]["capabilities"]["local_pod_inspection"],
|
||||
"unavailable"
|
||||
"available"
|
||||
);
|
||||
assert_eq!(
|
||||
hosts["items"][0]["capabilities"]["workspace_scope"],
|
||||
"current_workspace"
|
||||
);
|
||||
assert!(!hosts.to_string().contains("metadata.json"));
|
||||
|
||||
let runtimes = get_json(app.clone(), "/api/runtimes").await;
|
||||
assert_eq!(runtimes["source"], "worker_runtime_registry");
|
||||
assert_eq!(runtimes["items"][0]["runtime_id"], "local-pod-runtime");
|
||||
assert_eq!(runtimes["items"][0]["host_ids"][0], TEST_REPOSITORY_ID);
|
||||
|
||||
let workers = get_json(app.clone(), "/api/workers").await;
|
||||
assert!(workers["items"].as_array().unwrap().is_empty());
|
||||
assert_eq!(
|
||||
workers["diagnostics"][0]["code"],
|
||||
"local_pod_metadata_root_missing"
|
||||
"local_pod_registry_unreadable"
|
||||
);
|
||||
|
||||
let host_workers = get_json(
|
||||
|
||||
Reference in New Issue
Block a user