feat: add backend resource fetch api

This commit is contained in:
2026-07-08 20:18:38 +09:00
parent 1a2097b10d
commit 57e96d3be8
16 changed files with 859 additions and 41 deletions
+80 -13
View File
@@ -12,7 +12,8 @@ use chrono::{SecondsFormat, Utc};
use futures::StreamExt;
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;
use worker_runtime::worker_backend::WorkerRuntimeExecutionBackend;
use worker_runtime::resource::{BackendResourceError, BackendResourceFetchRequest};
use worker_runtime::worker_backend::{ProfileRuntimeWorkerFactory, WorkerRuntimeExecutionBackend};
use worker_runtime::working_directory::{
LocalGitWorktreeMaterializer, WorkingDirectoryDiagnostic, WorkingDirectoryMaterializer,
};
@@ -42,6 +43,7 @@ use crate::repositories::{
ConfiguredRepository, RepositoryListProjection, RepositoryLogRead, RepositoryLookupError,
RepositoryRegistryReader, RepositorySummary,
};
use crate::resource_broker::BackendResourceBroker;
use crate::store::{ControlPlaneStore, WorkspaceRecord};
use crate::{Error, Result};
use worker_runtime::catalog::{
@@ -154,6 +156,7 @@ pub struct WorkspaceApi {
runtime: Arc<RuntimeRegistry>,
companion: Arc<CompanionConsole>,
observation_proxy: BackendObservationProxy,
resource_broker: BackendResourceBroker,
working_directory_materializer: Arc<LocalGitWorktreeMaterializer>,
}
@@ -162,21 +165,46 @@ impl WorkspaceApi {
let materializer = Arc::new(LocalGitWorktreeMaterializer::new(
config.embedded_runtime_store_root.clone(),
));
let execution_backend =
WorkerRuntimeExecutionBackend::from_workspace(config.workspace_root.clone())
.map_err(|err| {
crate::Error::Store(format!(
"failed to initialize embedded Worker backend: {err}"
))
})?
.with_working_directory_materializer((*materializer).clone());
Self::new_with_execution_backend(config, store, Arc::new(execution_backend)).await
let resource_broker = BackendResourceBroker::default();
let execution_backend = WorkerRuntimeExecutionBackend::new(
ProfileRuntimeWorkerFactory::new(config.workspace_root.clone())
.with_resource_client(Arc::new(resource_broker.clone())),
)
.map_err(|err| {
crate::Error::Store(format!(
"failed to initialize embedded Worker backend: {err}"
))
})?
.with_working_directory_materializer((*materializer).clone());
Self::new_with_execution_backend_and_broker(
config,
store,
Arc::new(execution_backend),
resource_broker,
)
.await
}
#[cfg(test)]
async fn new_with_execution_backend(
config: ServerConfig,
store: Arc<dyn ControlPlaneStore>,
execution_backend: Arc<dyn worker_runtime::execution::WorkerExecutionBackend>,
) -> Result<Self> {
Self::new_with_execution_backend_and_broker(
config,
store,
execution_backend,
BackendResourceBroker::default(),
)
.await
}
async fn new_with_execution_backend_and_broker(
config: ServerConfig,
store: Arc<dyn ControlPlaneStore>,
execution_backend: Arc<dyn worker_runtime::execution::WorkerExecutionBackend>,
resource_broker: BackendResourceBroker,
) -> Result<Self> {
store
.upsert_workspace(&WorkspaceRecord {
@@ -193,13 +221,17 @@ impl WorkspaceApi {
config.embedded_runtime_store_root.clone(),
execution_backend,
)
.map(|runtime| runtime.with_resource_broker(resource_broker.clone()))
.map_err(|err| {
crate::Error::Store(format!("invalid embedded Worker backend: {err}"))
})?,
);
for remote_config in config.remote_runtime_sources.iter().cloned() {
runtime
.register(RemoteWorkerRuntime::new(remote_config).map_err(|err| err.into_error())?);
runtime.register(
RemoteWorkerRuntime::new(remote_config)
.map(|host| host.with_resource_broker(resource_broker.clone()))
.map_err(|err| err.into_error())?,
);
}
let runtime = Arc::new(runtime);
let companion = Arc::new(CompanionConsole::new(runtime.clone()));
@@ -214,6 +246,7 @@ impl WorkspaceApi {
runtime,
companion,
observation_proxy,
resource_broker,
working_directory_materializer,
})
}
@@ -328,6 +361,10 @@ pub fn build_router(api: WorkspaceApi) -> Router {
"/api/w/{workspace_id}/settings/runtime-connections/remotes/{runtime_id}/test",
post(scoped_test_remote_runtime_connection),
)
.route(
"/internal/runtime/resources/fetch",
post(post_internal_runtime_resource_fetch),
)
.route("/api/companion/status", get(get_companion_status))
.route(
"/api/w/{workspace_id}/companion/status",
@@ -1445,7 +1482,9 @@ async fn add_remote_runtime_connection(
vec![diagnostic],
)
})?;
let active_runtime = RemoteWorkerRuntime::new(active_config).map_err(|err| err.into_error())?;
let active_runtime = RemoteWorkerRuntime::new(active_config)
.map(|host| host.with_resource_broker(api.resource_broker.clone()))
.map_err(|err| err.into_error())?;
local_config.runtimes.remote.push(remote_config);
write_workspace_backend_config_for_settings(&api, &local_config)?;
api.runtime.register_or_replace(active_runtime);
@@ -1696,6 +1735,33 @@ async fn create_workspace_worker(
}))
}
async fn post_internal_runtime_resource_fetch(
State(api): State<WorkspaceApi>,
Json(request): Json<BackendResourceFetchRequest>,
) -> std::result::Result<
Json<worker_runtime::resource::BackendResourceFetchResponse>,
(StatusCode, Json<BackendResourceError>),
> {
api.resource_broker
.fetch_profile_source_archive(request)
.map(Json)
.map_err(|error| (backend_resource_error_status(&error), Json(error)))
}
fn backend_resource_error_status(error: &BackendResourceError) -> StatusCode {
match error {
BackendResourceError::Expired => StatusCode::GONE,
BackendResourceError::Unauthorized { .. } => StatusCode::UNAUTHORIZED,
BackendResourceError::MissingResource => StatusCode::NOT_FOUND,
BackendResourceError::UnsupportedKind
| BackendResourceError::DigestMismatch { .. }
| BackendResourceError::Oversized { .. }
| BackendResourceError::ContentTypeMismatch { .. }
| BackendResourceError::InvalidResponse { .. } => StatusCode::BAD_REQUEST,
BackendResourceError::Transport { .. } => StatusCode::BAD_GATEWAY,
}
}
async fn get_companion_status(
State(api): State<WorkspaceApi>,
) -> ApiResult<Json<CompanionStatusResponse>> {
@@ -3406,6 +3472,7 @@ mod tests {
}],
declarations: Vec::new(),
profile_source_archive: None,
profile_source_archive_handle: None,
}
.with_computed_digest()
}