feat: unify workspace settings and runtime resources

This commit is contained in:
2026-08-31 01:32:31 +09:00
parent e84a9d3f9b
commit 12cc2eb0e9
24 changed files with 776 additions and 916 deletions
+41
View File
@@ -237,6 +237,7 @@ pub enum RuntimeSourceStatus {
#[serde(rename_all = "snake_case")]
pub enum RuntimeIdentityAuthority {
RuntimeRegistryProjection,
ServerRuntimeConfiguration,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@@ -263,6 +264,46 @@ pub struct RuntimeSummary {
pub diagnostics: Vec<Diagnostic>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RuntimeManagementSummary {
pub built_in: bool,
pub config_managed: bool,
pub removable: bool,
pub endpoint_configured: bool,
pub token_ref_configured: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WorkspaceRuntimeResource {
#[serde(flatten)]
pub runtime: RuntimeSummary,
pub management: RuntimeManagementSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct CreateRemoteRuntimeRequest {
pub runtime_id: String,
pub display_name: Option<String>,
pub endpoint: String,
pub token_ref: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RuntimeConnectionTestResponse {
pub workspace_id: String,
pub runtime_id: String,
pub checked_at: String,
pub state: String,
pub protocol_version: Option<String>,
pub compatibility_basis: String,
#[serde(default)]
pub capabilities: Vec<String>,
pub health_result: String,
#[serde(default)]
pub diagnostics: Vec<Diagnostic>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct WorkerWorkspaceSummary {
pub visibility: String,
+211 -341
View File
@@ -58,12 +58,13 @@ use worker::feature::builtin::{WorkerObservationSubject, WorkerObservationSubjec
use worker_runtime::resource::{BackendResourceError, BackendResourceFetchRequest};
use worker_runtime::worker_backend::{ProfileRuntimeWorkerFactory, WorkerRuntimeExecutionBackend};
use workspace_api::{
CreateRepositorySshCredentialRequest, DeleteRepositorySshCredentialRequest,
DeleteRepositorySshHostTrustRequest, ObjectiveCreateRequest, ObjectiveEditRequest,
ObjectiveLinkTicketRequest, ObjectiveStateRequest, PutRepositorySshHostTrustRequest,
RepositoryAccessProjection, RepositorySshCredential, RepositorySshHostTrust,
RotateRepositorySshCredentialRequest, TICKET_ORCHESTRATION_PLANS_QUERY_PATH,
TICKET_RELATIONS_QUERY_PATH,
CreateRemoteRuntimeRequest, CreateRepositorySshCredentialRequest,
DeleteRepositorySshCredentialRequest, DeleteRepositorySshHostTrustRequest,
ObjectiveCreateRequest, ObjectiveEditRequest, ObjectiveLinkTicketRequest,
ObjectiveStateRequest, PutRepositorySshHostTrustRequest, RepositoryAccessProjection,
RepositorySshCredential, RepositorySshHostTrust, RotateRepositorySshCredentialRequest,
RuntimeConnectionTestResponse, RuntimeManagementSummary, TICKET_ORCHESTRATION_PLANS_QUERY_PATH,
TICKET_RELATIONS_QUERY_PATH, WorkspaceRuntimeResource,
};
use crate::auth::{
@@ -2428,7 +2429,18 @@ fn build_inner_router(api: WorkspaceApi) -> Router {
get(scoped_working_directory_detail).delete(scoped_cleanup_working_directory),
)
.route("/api/runtimes", get(list_runtimes))
.route("/api/w/{workspace_id}/runtimes", get(scoped_list_runtimes))
.route(
"/api/w/{workspace_id}/runtimes",
get(scoped_list_runtimes).post(scoped_create_remote_runtime),
)
.route(
"/api/w/{workspace_id}/runtimes/{runtime_id}",
delete(scoped_delete_remote_runtime),
)
.route(
"/api/w/{workspace_id}/runtimes/{runtime_id}/connection-tests",
post(scoped_test_runtime_connection),
)
.route(
"/api/workers",
get(list_workers).post(create_workspace_worker),
@@ -2486,38 +2498,6 @@ fn build_inner_router(api: WorkspaceApi) -> Router {
"/api/w/{workspace_id}/workers/launch-options",
get(scoped_get_worker_launch_options),
)
.route(
"/api/settings/runtime-connections",
get(get_runtime_connection_settings),
)
.route(
"/api/w/{workspace_id}/settings/runtime-connections",
get(scoped_get_runtime_connection_settings),
)
.route(
"/api/settings/runtime-connections/remotes",
post(add_remote_runtime_connection),
)
.route(
"/api/w/{workspace_id}/settings/runtime-connections/remotes",
post(scoped_add_remote_runtime_connection),
)
.route(
"/api/settings/runtime-connections/remotes/{runtime_id}",
delete(delete_remote_runtime_connection),
)
.route(
"/api/w/{workspace_id}/settings/runtime-connections/remotes/{runtime_id}",
delete(scoped_delete_remote_runtime_connection),
)
.route(
"/api/settings/runtime-connections/remotes/{runtime_id}/test",
post(test_remote_runtime_connection),
)
.route(
"/api/w/{workspace_id}/settings/runtime-connections/remotes/{runtime_id}/test",
post(scoped_test_remote_runtime_connection),
)
.route(
"/api/runtime/v1/workspaces/{workspace_id}/resources/fetch",
post(scoped_post_internal_runtime_resource_fetch),
@@ -2958,66 +2938,6 @@ pub struct WorkerRetentionResponse {
pub retention_state: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RuntimeConnectionSettingsResponse {
pub workspace_id: String,
pub embedded: RuntimeConnectionSummary,
pub remotes: Vec<RemoteRuntimeConnectionSummary>,
pub diagnostics: Vec<RuntimeDiagnostic>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RuntimeConnectionSummary {
pub runtime_id: String,
pub display_name: String,
pub kind: String,
pub built_in: bool,
pub config_managed: bool,
pub active: bool,
pub worker_creation_available: bool,
pub restart_required: bool,
pub status: String,
pub diagnostics: Vec<RuntimeDiagnostic>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RemoteRuntimeConnectionSummary {
#[serde(flatten)]
pub summary: RuntimeConnectionSummary,
pub endpoint_configured: bool,
pub token_ref_configured: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RuntimeConnectionMutationResponse {
pub workspace_id: String,
pub restart_required: bool,
pub remotes: Vec<RemoteRuntimeConnectionSummary>,
pub diagnostics: Vec<RuntimeDiagnostic>,
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AddRemoteRuntimeConnectionRequest {
pub runtime_id: String,
pub display_name: Option<String>,
pub endpoint: String,
pub token_ref: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RemoteRuntimeTestResponse {
pub workspace_id: String,
pub runtime_id: String,
pub checked_at: String,
pub state: String,
pub protocol_version: Option<String>,
pub compatibility_basis: String,
pub capabilities: Vec<String>,
pub health_result: String,
pub diagnostics: Vec<RuntimeDiagnostic>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WorkerLaunchOptionsResponse {
pub workspace_id: String,
@@ -7961,9 +7881,13 @@ async fn scoped_get_profile_source_archive(
async fn scoped_list_runtimes(
State(api): State<WorkspaceApi>,
AxumPath(path): AxumPath<ScopedWorkspacePath>,
) -> ApiResult<Json<workspace_api::ListResponse<workspace_api::RuntimeSummary>>> {
) -> ApiResult<Json<workspace_api::ListResponse<WorkspaceRuntimeResource>>> {
validate_workspace_scope(&api, &path.workspace_id)?;
list_runtimes(State(api)).await
let runtime_config = load_backend_runtimes_config_for_settings(&api)?;
Ok(Json(workspace_runtime_resources_response(
&api,
&runtime_config,
)))
}
async fn scoped_workspace_protocol_ws(
@@ -9833,37 +9757,29 @@ fn cleanup_api_error(runtime_id: &str, code: &str, message: &str) -> ApiError {
.into()
}
async fn scoped_get_runtime_connection_settings(
async fn scoped_create_remote_runtime(
State(api): State<WorkspaceApi>,
AxumPath(path): AxumPath<ScopedWorkspacePath>,
) -> ApiResult<Json<RuntimeConnectionSettingsResponse>> {
Json(request): Json<CreateRemoteRuntimeRequest>,
) -> ApiResult<(StatusCode, Json<WorkspaceRuntimeResource>)> {
validate_workspace_scope(&api, &path.workspace_id)?;
get_runtime_connection_settings(State(api)).await
create_remote_runtime(State(api), Json(request)).await
}
async fn scoped_add_remote_runtime_connection(
State(api): State<WorkspaceApi>,
AxumPath(path): AxumPath<ScopedWorkspacePath>,
Json(request): Json<AddRemoteRuntimeConnectionRequest>,
) -> ApiResult<Json<RuntimeConnectionMutationResponse>> {
validate_workspace_scope(&api, &path.workspace_id)?;
add_remote_runtime_connection(State(api), Json(request)).await
}
async fn scoped_delete_remote_runtime_connection(
async fn scoped_delete_remote_runtime(
State(api): State<WorkspaceApi>,
AxumPath(path): AxumPath<ScopedRuntimePath>,
) -> ApiResult<Json<RuntimeConnectionMutationResponse>> {
) -> ApiResult<StatusCode> {
validate_workspace_scope(&api, &path.workspace_id)?;
delete_remote_runtime_connection(State(api), AxumPath(path.runtime_id)).await
delete_remote_runtime(State(api), AxumPath(path.runtime_id)).await
}
async fn scoped_test_remote_runtime_connection(
async fn scoped_test_runtime_connection(
State(api): State<WorkspaceApi>,
AxumPath(path): AxumPath<ScopedRuntimePath>,
) -> ApiResult<Json<RemoteRuntimeTestResponse>> {
) -> ApiResult<Json<RuntimeConnectionTestResponse>> {
validate_workspace_scope(&api, &path.workspace_id)?;
test_remote_runtime_connection(State(api), AxumPath(path.runtime_id)).await
test_runtime_connection(State(api), AxumPath(path.runtime_id)).await
}
async fn scoped_get_companion_status(
@@ -11154,27 +11070,17 @@ async fn list_workers(
workers_response(api).map(Json)
}
async fn get_runtime_connection_settings(
async fn create_remote_runtime(
State(api): State<WorkspaceApi>,
) -> ApiResult<Json<RuntimeConnectionSettingsResponse>> {
let runtime_config = load_backend_runtimes_config_for_settings(&api)?;
Ok(Json(runtime_connection_settings_response(
&api,
&runtime_config,
)))
}
async fn add_remote_runtime_connection(
State(api): State<WorkspaceApi>,
Json(request): Json<AddRemoteRuntimeConnectionRequest>,
) -> ApiResult<Json<RuntimeConnectionMutationResponse>> {
Json(request): Json<CreateRemoteRuntimeRequest>,
) -> ApiResult<(StatusCode, Json<WorkspaceRuntimeResource>)> {
validate_runtime_connection_request(&request)?;
let mut runtime_config = load_backend_runtimes_config_for_settings(&api)?;
let id = request.runtime_id.trim().to_string();
if id == EMBEDDED_WORKER_RUNTIME_ID {
return Err(settings_bad_request(
"embedded_runtime_not_config_managed",
"the embedded Runtime is built in and cannot be managed from local remote Runtime config",
"the embedded Runtime is built in and cannot be managed as a remote Runtime",
));
}
if request
@@ -11184,7 +11090,7 @@ async fn add_remote_runtime_connection(
{
return Err(settings_bad_request(
"remote_runtime_token_ref_unsupported",
"remote Runtime token_ref persistence is not supported by this v0 browser settings surface",
"remote Runtime token_ref persistence is not supported",
));
}
if runtime_config
@@ -11195,11 +11101,11 @@ async fn add_remote_runtime_connection(
{
return Err(settings_bad_request(
"remote_runtime_already_exists",
"a remote Runtime connection with that id is already configured",
"a remote Runtime with that id already exists",
));
}
let remote_config = RemoteRuntimeConfigFile {
id,
id: id.clone(),
endpoint: request.endpoint.trim().to_string(),
display_name: request
.display_name
@@ -11232,31 +11138,19 @@ async fn add_remote_runtime_connection(
runtime_config.runtimes.remote.push(remote_config);
write_backend_runtimes_config_for_settings(&api, &runtime_config)?;
api.runtime.register_or_replace(active_runtime);
let mut response = runtime_connection_mutation_response(
&api,
&runtime_config,
vec![settings_diagnostic(
"runtime_registry_applied",
DiagnosticSeverity::Info,
"Remote Runtime config was persisted and applied to the active Runtime registry without restarting the Workspace backend.",
)],
);
response.diagnostics.push(settings_diagnostic(
"backend_runtimes_config_rewritten",
DiagnosticSeverity::Info,
"Backend runtimes config was rewritten from the typed schema; comments and formatting are not preserved in v0.",
));
Ok(Json(response))
let resource = workspace_runtime_resource_by_id(&api, &runtime_config, &id)
.ok_or_else(|| Error::UnknownRuntime(id.clone()))?;
Ok((StatusCode::CREATED, Json(resource)))
}
async fn delete_remote_runtime_connection(
async fn delete_remote_runtime(
State(api): State<WorkspaceApi>,
AxumPath(runtime_id): AxumPath<String>,
) -> ApiResult<Json<RuntimeConnectionMutationResponse>> {
) -> ApiResult<StatusCode> {
if runtime_id == EMBEDDED_WORKER_RUNTIME_ID {
return Err(settings_bad_request(
"embedded_runtime_not_config_managed",
"the embedded Runtime is built in and cannot be deleted from remote Runtime config",
"the embedded Runtime is built in and cannot be deleted",
));
}
let mut runtime_config = load_backend_runtimes_config_for_settings(&api)?;
@@ -11283,41 +11177,27 @@ async fn delete_remote_runtime_connection(
"remote_runtime_delete_blocked",
DiagnosticSeverity::Error,
format!(
"Remote Runtime '{runtime_id}' has {worker_count} active worker(s); stop or move them before deleting the connection."
"Remote Runtime '{runtime_id}' has {worker_count} active worker(s); stop or move them before deleting it."
),
));
return Err(ApiError::with_diagnostics(
Error::RuntimeOperationFailed {
runtime_id,
code: "remote_runtime_delete_blocked".to_string(),
message: "Remote Runtime connection has active workers".to_string(),
message: "Remote Runtime has active workers".to_string(),
},
diagnostics,
));
}
}
write_backend_runtimes_config_for_settings(&api, &runtime_config)?;
let mut response = runtime_connection_mutation_response(
&api,
&runtime_config,
vec![settings_diagnostic(
"runtime_registry_applied",
DiagnosticSeverity::Info,
"Remote Runtime config was removed from persisted config and the active Runtime registry without restarting the Workspace backend.",
)],
);
response.diagnostics.push(settings_diagnostic(
"backend_runtimes_config_rewritten",
DiagnosticSeverity::Info,
"Backend runtimes config was rewritten from the typed schema; comments and formatting are not preserved in v0.",
));
Ok(Json(response))
Ok(StatusCode::NO_CONTENT)
}
async fn test_remote_runtime_connection(
async fn test_runtime_connection(
State(api): State<WorkspaceApi>,
AxumPath(runtime_id): AxumPath<String>,
) -> ApiResult<Json<RemoteRuntimeTestResponse>> {
) -> ApiResult<Json<RuntimeConnectionTestResponse>> {
let runtime_config = load_backend_runtimes_config_for_settings(&api)?;
let remote = runtime_config
.runtimes
@@ -13218,142 +13098,112 @@ fn write_backend_runtimes_config_for_settings(
})
}
fn runtime_connection_settings_response(
fn workspace_runtime_resources_response(
api: &WorkspaceApi,
runtime_config: &BackendRuntimesConfigFile,
) -> RuntimeConnectionSettingsResponse {
RuntimeConnectionSettingsResponse {
workspace_id: api.config.workspace_id.clone(),
embedded: embedded_runtime_connection_summary(api),
remotes: remote_runtime_connection_summaries(api, runtime_config, false),
diagnostics: Vec::new(),
}
}
fn runtime_connection_mutation_response(
api: &WorkspaceApi,
runtime_config: &BackendRuntimesConfigFile,
diagnostics: Vec<RuntimeDiagnostic>,
) -> RuntimeConnectionMutationResponse {
RuntimeConnectionMutationResponse {
workspace_id: api.config.workspace_id.clone(),
restart_required: false,
remotes: remote_runtime_connection_summaries(api, runtime_config, false),
diagnostics,
}
}
fn embedded_runtime_connection_summary(api: &WorkspaceApi) -> RuntimeConnectionSummary {
let active = api
.runtime
.list_runtimes(api.config.max_records.min(200))
) -> workspace_api::ListResponse<WorkspaceRuntimeResource> {
let limit = api.config.max_records.min(200);
let runtimes = api.runtime.list_runtimes(limit);
let mut items = runtimes
.items
.into_iter()
.find(|runtime| runtime.runtime_id == EMBEDDED_WORKER_RUNTIME_ID);
match active {
Some(runtime) => RuntimeConnectionSummary {
runtime_id: runtime.runtime_id,
display_name: runtime.label,
kind: runtime.kind,
built_in: true,
config_managed: false,
active: runtime.status == "active",
worker_creation_available: runtime.worker_creation_available,
restart_required: false,
status: runtime.status,
diagnostics: runtime.diagnostics,
},
None => RuntimeConnectionSummary {
runtime_id: EMBEDDED_WORKER_RUNTIME_ID.to_string(),
display_name: "Embedded Runtime".to_string(),
kind: "embedded_worker_runtime".to_string(),
built_in: true,
config_managed: false,
active: false,
worker_creation_available: false,
restart_required: false,
status: "unavailable".to_string(),
diagnostics: vec![settings_diagnostic(
"embedded_runtime_unavailable",
DiagnosticSeverity::Warning,
"The built-in embedded Runtime is not active in the current Runtime registry projection.",
)],
},
}
}
fn remote_runtime_connection_summaries(
api: &WorkspaceApi,
runtime_config: &BackendRuntimesConfigFile,
restart_required: bool,
) -> Vec<RemoteRuntimeConnectionSummary> {
let live_runtimes = api
.runtime
.list_runtimes(api.config.max_records.min(200))
.items;
runtime_config
.runtimes
.remote
.iter()
.map(|remote| {
let live = live_runtimes
.map(|runtime| {
let remote = runtime_config
.runtimes
.remote
.iter()
.find(|runtime| runtime.runtime_id == remote.id);
let (display_name, kind, active, worker_creation_available, status, diagnostics) = match live {
Some(runtime) => (
runtime.label.clone(),
runtime.kind.clone(),
runtime.status == "active",
runtime.worker_creation_available,
runtime.status.clone(),
runtime.diagnostics.clone(),
),
None => (
remote
.display_name
.clone()
.unwrap_or_else(|| remote.id.clone()),
"remote_http".to_string(),
false,
false,
"configured_restart_required".to_string(),
if restart_required {
vec![settings_diagnostic(
"runtime_registry_restart_required",
DiagnosticSeverity::Warning,
"This remote Runtime config is persisted but not active until the Workspace backend restarts.",
)]
} else {
Vec::new()
},
),
};
RemoteRuntimeConnectionSummary {
summary: RuntimeConnectionSummary {
runtime_id: remote.id.clone(),
display_name,
kind,
built_in: false,
config_managed: true,
active,
worker_creation_available,
restart_required,
status,
diagnostics,
.find(|remote| remote.id == runtime.runtime_id);
let built_in = runtime.runtime_id == EMBEDDED_WORKER_RUNTIME_ID;
WorkspaceRuntimeResource {
runtime: runtime.into(),
management: RuntimeManagementSummary {
built_in,
config_managed: remote.is_some(),
removable: remote.is_some() && !built_in,
endpoint_configured: remote
.is_some_and(|remote| !remote.endpoint.trim().is_empty()),
token_ref_configured: remote.is_some_and(|remote| {
remote
.token_ref
.as_deref()
.is_some_and(|value| !value.trim().is_empty())
}),
},
}
})
.collect::<Vec<_>>();
for remote in &runtime_config.runtimes.remote {
if items
.iter()
.any(|resource| resource.runtime.runtime_id == remote.id)
{
continue;
}
items.push(WorkspaceRuntimeResource {
runtime: workspace_api::RuntimeSummary {
runtime_id: remote.id.clone(),
label: remote
.display_name
.clone()
.unwrap_or_else(|| remote.id.clone()),
kind: "remote_http".to_string(),
status: "unavailable".to_string(),
source: workspace_api::RuntimeSourceSummary {
kind: workspace_api::RuntimeSourceKind::RemoteHttp,
status: workspace_api::RuntimeSourceStatus::Reserved,
identity_authority:
workspace_api::RuntimeIdentityAuthority::ServerRuntimeConfiguration,
note: "The configured Runtime is not present in the active Runtime registry."
.to_string(),
},
host_ids: Vec::new(),
worker_creation_available: false,
os: String::new(),
arch: String::new(),
diagnostics: vec![
settings_diagnostic(
"configured_runtime_unavailable",
DiagnosticSeverity::Warning,
"The configured Runtime is not present in the active Runtime registry.",
)
.into(),
],
},
management: RuntimeManagementSummary {
built_in: false,
config_managed: true,
removable: true,
endpoint_configured: !remote.endpoint.trim().is_empty(),
token_ref_configured: remote
.token_ref
.as_deref()
.is_some_and(|value| !value.trim().is_empty()),
}
})
.collect()
},
});
}
workspace_api::ListResponse {
workspace_id: api.config.workspace_id.clone(),
limit,
items,
source: "workspace-runtime-resources".to_string(),
diagnostics: runtimes.diagnostics.into_iter().map(Into::into).collect(),
}
}
fn validate_runtime_connection_request(
request: &AddRemoteRuntimeConnectionRequest,
) -> ApiResult<()> {
fn workspace_runtime_resource_by_id(
api: &WorkspaceApi,
runtime_config: &BackendRuntimesConfigFile,
runtime_id: &str,
) -> Option<WorkspaceRuntimeResource> {
workspace_runtime_resources_response(api, runtime_config)
.items
.into_iter()
.find(|resource| resource.runtime.runtime_id == runtime_id)
}
fn validate_runtime_connection_request(request: &CreateRemoteRuntimeRequest) -> ApiResult<()> {
validate_public_runtime_id(request.runtime_id.trim())?;
let endpoint = request.endpoint.trim();
if endpoint.is_empty() || !(endpoint.starts_with("http://") || endpoint.starts_with("https://"))
@@ -13411,14 +13261,14 @@ fn remote_runtime_config_from_file(
async fn test_remote_runtime_config(
api: &WorkspaceApi,
remote: &RemoteRuntimeConfigFile,
) -> RemoteRuntimeTestResponse {
) -> RuntimeConnectionTestResponse {
let checked_at = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
if remote
.token_ref
.as_deref()
.is_some_and(|value| !value.trim().is_empty())
{
return RemoteRuntimeTestResponse {
return RuntimeConnectionTestResponse {
workspace_id: api.config.workspace_id.clone(),
runtime_id: remote.id.clone(),
checked_at,
@@ -13431,7 +13281,8 @@ async fn test_remote_runtime_config(
"remote_runtime_token_ref_unsupported",
DiagnosticSeverity::Error,
"Remote Runtime test cannot use token_ref in v0; no token or secret value was exposed to the Browser.",
)],
)
.into()],
};
}
@@ -13690,7 +13541,7 @@ async fn test_remote_runtime_config(
"No connection problem found. Config-bundle sync was not checked because this lightweight test does not upload bundles as a side effect.",
);
RemoteRuntimeTestResponse {
RuntimeConnectionTestResponse {
workspace_id: api.config.workspace_id.clone(),
runtime_id: remote.id.clone(),
checked_at,
@@ -13705,7 +13556,11 @@ async fn test_remote_runtime_config(
observation.incompatible_count,
observation.unknown_count
),
diagnostics: observation.diagnostics,
diagnostics: observation
.diagnostics
.into_iter()
.map(Into::into)
.collect(),
}
}
@@ -13715,8 +13570,8 @@ fn remote_runtime_test_failed(
checked_at: String,
code: impl Into<String>,
message: impl Into<String>,
) -> RemoteRuntimeTestResponse {
RemoteRuntimeTestResponse {
) -> RuntimeConnectionTestResponse {
RuntimeConnectionTestResponse {
workspace_id: api.config.workspace_id.clone(),
runtime_id: remote.id.clone(),
checked_at,
@@ -13725,11 +13580,7 @@ fn remote_runtime_test_failed(
compatibility_basis: "worker-runtime lightweight HTTP compatibility probes".to_string(),
capabilities: Vec::new(),
health_result: "failed".to_string(),
diagnostics: vec![settings_diagnostic(
code,
DiagnosticSeverity::Error,
message,
)],
diagnostics: vec![settings_diagnostic(code, DiagnosticSeverity::Error, message).into()],
}
}
@@ -17046,7 +16897,7 @@ mod tests {
#[test]
fn runtime_connection_request_validation_bounds_browser_input() {
let ok = AddRemoteRuntimeConnectionRequest {
let ok = CreateRemoteRuntimeRequest {
runtime_id: "team-runtime_1".to_string(),
display_name: Some("Team Runtime".to_string()),
endpoint: "https://runtime.example".to_string(),
@@ -17054,7 +16905,7 @@ mod tests {
};
assert!(validate_runtime_connection_request(&ok).is_ok());
let bad_endpoint = AddRemoteRuntimeConnectionRequest {
let bad_endpoint = CreateRemoteRuntimeRequest {
endpoint: "/tmp/socket".to_string(),
..ok
};
@@ -22698,34 +22549,52 @@ mod tests {
}
#[tokio::test]
async fn runtime_connection_settings_add_delete_apply_live_registry() {
async fn runtime_rest_resource_create_list_and_delete_apply_live_registry() {
let dir = tempfile::tempdir().unwrap();
let app = test_app(dir.path()).await;
let runtimes_uri = format!("/api/w/{TEST_WORKSPACE_ID}/runtimes");
let settings = get_json(app.clone(), "/api/settings/runtime-connections").await;
assert_eq!(settings["embedded"]["built_in"], true);
assert_eq!(settings["embedded"]["config_managed"], false);
let added = post_json(
let initial = get_json(app.clone(), &runtimes_uri).await;
let embedded = initial["items"]
.as_array()
.unwrap()
.iter()
.find(|runtime| runtime["runtime_id"] == EMBEDDED_WORKER_RUNTIME_ID)
.unwrap();
assert_eq!(embedded["management"]["built_in"], true);
assert_eq!(embedded["management"]["config_managed"], false);
request_json(
app.clone(),
"/api/settings/runtime-connections/remotes",
serde_json::json!({
"DELETE",
&format!("{runtimes_uri}/{EMBEDDED_WORKER_RUNTIME_ID}"),
None,
StatusCode::BAD_REQUEST,
)
.await;
request_json(
app.clone(),
"GET",
"/api/settings/runtime-connections",
None,
StatusCode::NOT_FOUND,
)
.await;
let added = request_json(
app.clone(),
"POST",
&runtimes_uri,
Some(serde_json::json!({
"runtime_id": "team-runtime",
"display_name": "Team Runtime",
"endpoint": "https://runtime.example.invalid"
}),
})),
StatusCode::CREATED,
)
.await;
assert_eq!(added["restart_required"], false);
assert_eq!(added["remotes"][0]["runtime_id"], "team-runtime");
assert_eq!(added["remotes"][0]["endpoint_configured"], true);
assert!(
added["diagnostics"]
.as_array()
.unwrap()
.iter()
.any(|diagnostic| diagnostic["code"] == "runtime_registry_applied")
);
assert_eq!(added["runtime_id"], "team-runtime");
assert_eq!(added["management"]["config_managed"], true);
assert_eq!(added["management"]["endpoint_configured"], true);
let projected = serde_json::to_string(&added).unwrap();
assert!(!projected.contains("runtime.example.invalid"));
@@ -22756,13 +22625,12 @@ mod tests {
let deleted = request_json(
app.clone(),
"DELETE",
"/api/settings/runtime-connections/remotes/team-runtime",
&format!("{runtimes_uri}/team-runtime"),
None,
StatusCode::OK,
StatusCode::NO_CONTENT,
)
.await;
assert_eq!(deleted["restart_required"], false);
assert_eq!(deleted["remotes"].as_array().unwrap().len(), 0);
assert_eq!(deleted["message"], "");
let launch_options = get_json(app.clone(), "/api/workers/launch-options").await;
assert!(
!launch_options["runtimes"]
@@ -22794,17 +22662,19 @@ mod tests {
let dir = tempfile::tempdir().unwrap();
let app = test_app(dir.path()).await;
let added = post_json(
let added = request_json(
app.clone(),
"/api/settings/runtime-connections/remotes",
serde_json::json!({
"POST",
&format!("/api/w/{TEST_WORKSPACE_ID}/runtimes"),
Some(serde_json::json!({
"runtime_id": "busy-runtime",
"display_name": "Busy Runtime",
"endpoint": format!("http://{runtime_addr}")
}),
})),
StatusCode::CREATED,
)
.await;
assert_eq!(added["restart_required"], false);
assert_eq!(added["runtime_id"], "busy-runtime");
let workers = get_json(app.clone(), "/api/workers").await;
assert!(
workers["items"]
@@ -22818,7 +22688,7 @@ mod tests {
let response = request_json(
app,
"DELETE",
"/api/settings/runtime-connections/remotes/busy-runtime",
&format!("/api/w/{TEST_WORKSPACE_ID}/runtimes/busy-runtime"),
None,
StatusCode::CONFLICT,
)
@@ -22876,7 +22746,7 @@ mod tests {
let response = post_json(
app,
"/api/settings/runtime-connections/remotes/probe-runtime/test",
&format!("/api/w/{TEST_WORKSPACE_ID}/runtimes/probe-runtime/connection-tests"),
serde_json::json!({}),
)
.await;
@@ -22940,7 +22810,7 @@ mod tests {
let response = post_json(
app,
"/api/settings/runtime-connections/remotes/control-only-runtime/test",
&format!("/api/w/{TEST_WORKSPACE_ID}/runtimes/control-only-runtime/connection-tests"),
serde_json::json!({}),
)
.await;