fix: persist Workdir Runtime failure classifications
This commit is contained in:
@@ -7693,6 +7693,17 @@ fn registered_workdir_runtime_id(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn workdir_runtime_failure_code(error: &RuntimeRegistryError) -> String {
|
||||||
|
match error {
|
||||||
|
RuntimeRegistryError::UnknownRuntime(_) | RuntimeRegistryError::UnknownHost(_) => {
|
||||||
|
"runtime_unavailable".to_string()
|
||||||
|
}
|
||||||
|
RuntimeRegistryError::RuntimeOperationFailed { code, .. } => code.clone(),
|
||||||
|
RuntimeRegistryError::InvalidIdentifier { .. } => "invalid_runtime".to_string(),
|
||||||
|
RuntimeRegistryError::UnknownWorker { .. } => "runtime_workdir_lookup_failed".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_workspace_working_directory(
|
async fn create_workspace_working_directory(
|
||||||
api: &WorkspaceApi,
|
api: &WorkspaceApi,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
@@ -7762,9 +7773,16 @@ async fn create_workspace_working_directory(
|
|||||||
.clone()
|
.clone()
|
||||||
.or(runtime_projection.default_runtime_id.clone())
|
.or(runtime_projection.default_runtime_id.clone())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
|
ApiError::with_diagnostics(
|
||||||
Error::InvalidInput(
|
Error::InvalidInput(
|
||||||
"runtime_id was omitted and Workspace configuration has no runtime.default_runtime_id"
|
"runtime_id was omitted and Workspace configuration has no runtime.default_runtime_id"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
),
|
||||||
|
vec![RuntimeDiagnostic {
|
||||||
|
code: "default_runtime_not_configured".to_string(),
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Workspace default Runtime is not configured".to_string(),
|
||||||
|
}],
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let now = now_registry_timestamp();
|
let now = now_registry_timestamp();
|
||||||
@@ -7810,10 +7828,17 @@ async fn create_workspace_working_directory(
|
|||||||
&operation_id,
|
&operation_id,
|
||||||
&request_fingerprint,
|
&request_fingerprint,
|
||||||
false,
|
false,
|
||||||
Some("resolved Runtime is not registered"),
|
Some("runtime_unavailable"),
|
||||||
&now_registry_timestamp(),
|
&now_registry_timestamp(),
|
||||||
)?;
|
)?;
|
||||||
return Err(Error::UnknownRuntime(reserved.resolved_runtime_id).into());
|
return Err(ApiError::with_diagnostics(
|
||||||
|
Error::UnknownRuntime(reserved.resolved_runtime_id),
|
||||||
|
vec![RuntimeDiagnostic {
|
||||||
|
code: "runtime_unavailable".to_string(),
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Selected Runtime is not available".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if runtime.kind == "remote_http" && runtime.status != "connected" {
|
if runtime.kind == "remote_http" && runtime.status != "connected" {
|
||||||
@@ -7822,15 +7847,21 @@ async fn create_workspace_working_directory(
|
|||||||
&operation_id,
|
&operation_id,
|
||||||
&request_fingerprint,
|
&request_fingerprint,
|
||||||
false,
|
false,
|
||||||
Some("resolved Runtime is unavailable"),
|
Some("runtime_unavailable"),
|
||||||
&now_registry_timestamp(),
|
&now_registry_timestamp(),
|
||||||
)?;
|
)?;
|
||||||
return Err(Error::RuntimeOperationFailed {
|
return Err(ApiError::with_diagnostics(
|
||||||
|
Error::RuntimeOperationFailed {
|
||||||
runtime_id: reserved.resolved_runtime_id,
|
runtime_id: reserved.resolved_runtime_id,
|
||||||
code: "runtime_unavailable".to_string(),
|
code: "runtime_unavailable".to_string(),
|
||||||
message: "Selected Runtime is not connected".to_string(),
|
message: "Selected Runtime is not connected".to_string(),
|
||||||
}
|
},
|
||||||
.into());
|
vec![RuntimeDiagnostic {
|
||||||
|
code: "runtime_unavailable".to_string(),
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Selected Runtime is not available".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !runtime.capabilities.supports_worktrees {
|
if !runtime.capabilities.supports_worktrees {
|
||||||
@@ -7839,25 +7870,49 @@ async fn create_workspace_working_directory(
|
|||||||
&operation_id,
|
&operation_id,
|
||||||
&request_fingerprint,
|
&request_fingerprint,
|
||||||
false,
|
false,
|
||||||
Some("resolved Runtime does not support Workdir creation"),
|
Some("runtime_workdir_unsupported"),
|
||||||
&now_registry_timestamp(),
|
&now_registry_timestamp(),
|
||||||
)?;
|
)?;
|
||||||
return Err(Error::RuntimeOperationFailed {
|
return Err(ApiError::with_diagnostics(
|
||||||
|
Error::RuntimeOperationFailed {
|
||||||
runtime_id: reserved.resolved_runtime_id,
|
runtime_id: reserved.resolved_runtime_id,
|
||||||
code: "workdir_create_unsupported".to_string(),
|
code: "runtime_workdir_unsupported".to_string(),
|
||||||
message: "Selected Runtime does not support Workdir creation".to_string(),
|
message: "Selected Runtime does not support Workdir creation".to_string(),
|
||||||
}
|
},
|
||||||
.into());
|
vec![RuntimeDiagnostic {
|
||||||
|
code: "runtime_workdir_unsupported".to_string(),
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Selected Runtime does not support Workdir creation".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
working_directory_request.backend_workdir_id = Some(reserved.working_directory_id.clone());
|
working_directory_request.backend_workdir_id = Some(reserved.working_directory_id.clone());
|
||||||
let existing = api
|
let existing = match api.runtime.working_directory(
|
||||||
.runtime
|
|
||||||
.working_directory(
|
|
||||||
&reserved.resolved_runtime_id,
|
&reserved.resolved_runtime_id,
|
||||||
&reserved.working_directory_id,
|
&reserved.working_directory_id,
|
||||||
)
|
) {
|
||||||
.map_err(|err| err.into_error())?;
|
Ok(existing) => existing,
|
||||||
|
Err(error) => {
|
||||||
|
let failure_code = workdir_runtime_failure_code(&error);
|
||||||
|
api.config_store.finish_workdir_create_operation(
|
||||||
|
workspace_id,
|
||||||
|
&operation_id,
|
||||||
|
&request_fingerprint,
|
||||||
|
false,
|
||||||
|
Some(&failure_code),
|
||||||
|
&now_registry_timestamp(),
|
||||||
|
)?;
|
||||||
|
return Err(ApiError::with_diagnostics(
|
||||||
|
error.into_error(),
|
||||||
|
vec![RuntimeDiagnostic {
|
||||||
|
code: failure_code,
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Selected Runtime could not inspect the reserved Workdir".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
let result = if existing.working_directory.is_some() {
|
let result = if existing.working_directory.is_some() {
|
||||||
existing
|
existing
|
||||||
} else {
|
} else {
|
||||||
@@ -7867,7 +7922,7 @@ async fn create_workspace_working_directory(
|
|||||||
{
|
{
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error = error.into_error();
|
let failure_code = workdir_runtime_failure_code(&error);
|
||||||
let _ = api
|
let _ = api
|
||||||
.store
|
.store
|
||||||
.delete_workdir_registry(workspace_id, &reserved.working_directory_id);
|
.delete_workdir_registry(workspace_id, &reserved.working_directory_id);
|
||||||
@@ -7876,10 +7931,17 @@ async fn create_workspace_working_directory(
|
|||||||
&operation_id,
|
&operation_id,
|
||||||
&request_fingerprint,
|
&request_fingerprint,
|
||||||
false,
|
false,
|
||||||
Some("runtime rejected Workdir creation"),
|
Some(&failure_code),
|
||||||
&now_registry_timestamp(),
|
&now_registry_timestamp(),
|
||||||
)?;
|
)?;
|
||||||
return Err(error.into());
|
return Err(ApiError::with_diagnostics(
|
||||||
|
error.into_error(),
|
||||||
|
vec![RuntimeDiagnostic {
|
||||||
|
code: failure_code,
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Selected Runtime rejected Workdir creation".to_string(),
|
||||||
|
}],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -7892,16 +7954,25 @@ async fn create_workspace_working_directory(
|
|||||||
&operation_id,
|
&operation_id,
|
||||||
&request_fingerprint,
|
&request_fingerprint,
|
||||||
false,
|
false,
|
||||||
Some("runtime did not create working directory"),
|
Some("runtime_workdir_create_failed"),
|
||||||
&now_registry_timestamp(),
|
&now_registry_timestamp(),
|
||||||
)?;
|
)?;
|
||||||
|
let mut diagnostics = result.diagnostics;
|
||||||
|
diagnostics.insert(
|
||||||
|
0,
|
||||||
|
RuntimeDiagnostic {
|
||||||
|
code: "runtime_workdir_create_failed".to_string(),
|
||||||
|
severity: DiagnosticSeverity::Error,
|
||||||
|
message: "Runtime did not create the reserved Workdir".to_string(),
|
||||||
|
},
|
||||||
|
);
|
||||||
return Err(ApiError::with_diagnostics(
|
return Err(ApiError::with_diagnostics(
|
||||||
Error::RuntimeOperationFailed {
|
Error::RuntimeOperationFailed {
|
||||||
runtime_id: reserved.resolved_runtime_id,
|
runtime_id: reserved.resolved_runtime_id,
|
||||||
code: "workspace_working_directory_create_failed".to_string(),
|
code: "runtime_workdir_create_failed".to_string(),
|
||||||
message: "Runtime did not create working directory".to_string(),
|
message: "Runtime did not create working directory".to_string(),
|
||||||
},
|
},
|
||||||
result.diagnostics,
|
diagnostics,
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let record = workdir_record_from_summary(
|
let record = workdir_record_from_summary(
|
||||||
@@ -18937,6 +19008,10 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(response["error"], "Bad Request");
|
assert_eq!(response["error"], "Bad Request");
|
||||||
|
assert_eq!(
|
||||||
|
response["diagnostics"][0]["code"],
|
||||||
|
"default_runtime_not_configured"
|
||||||
|
);
|
||||||
let projected = serde_json::to_string(&response).unwrap();
|
let projected = serde_json::to_string(&response).unwrap();
|
||||||
assert!(!projected.contains(dir.path().to_string_lossy().as_ref()));
|
assert!(!projected.contains(dir.path().to_string_lossy().as_ref()));
|
||||||
}
|
}
|
||||||
@@ -18963,6 +19038,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(response["error"], "Not Found");
|
assert_eq!(response["error"], "Not Found");
|
||||||
|
assert_eq!(response["diagnostics"][0]["code"], "runtime_unavailable");
|
||||||
let operation = api
|
let operation = api
|
||||||
.config_store
|
.config_store
|
||||||
.load_workdir_create_operation(TEST_WORKSPACE_ID, operation_id)
|
.load_workdir_create_operation(TEST_WORKSPACE_ID, operation_id)
|
||||||
@@ -18974,6 +19050,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(operation.resolved_runtime_id, "missing-runtime");
|
assert_eq!(operation.resolved_runtime_id, "missing-runtime");
|
||||||
assert_eq!(operation.state, "failed");
|
assert_eq!(operation.state, "failed");
|
||||||
|
assert_eq!(operation.failure.as_deref(), Some("runtime_unavailable"));
|
||||||
assert!(
|
assert!(
|
||||||
api.store
|
api.store
|
||||||
.get_workdir_registry(TEST_WORKSPACE_ID, &operation.working_directory_id)
|
.get_workdir_registry(TEST_WORKSPACE_ID, &operation.working_directory_id)
|
||||||
@@ -19004,7 +19081,10 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(response["error"], "Bad Request");
|
assert_eq!(response["error"], "Bad Request");
|
||||||
|
assert_eq!(
|
||||||
|
response["diagnostics"][0]["code"],
|
||||||
|
"runtime_workdir_unsupported"
|
||||||
|
);
|
||||||
set_test_default_runtime(&api, "not-a-registered-runtime");
|
set_test_default_runtime(&api, "not-a-registered-runtime");
|
||||||
request_json(
|
request_json(
|
||||||
app,
|
app,
|
||||||
@@ -19028,7 +19108,7 @@ mod tests {
|
|||||||
assert_eq!(operation.state, "failed");
|
assert_eq!(operation.state, "failed");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
operation.failure.as_deref(),
|
operation.failure.as_deref(),
|
||||||
Some("resolved Runtime does not support Workdir creation")
|
Some("runtime_workdir_unsupported")
|
||||||
);
|
);
|
||||||
assert_eq!(operation.config_revision, 2);
|
assert_eq!(operation.config_revision, 2);
|
||||||
assert!(!operation.config_projection_digest.is_empty());
|
assert!(!operation.config_projection_digest.is_empty());
|
||||||
|
|||||||
Reference in New Issue
Block a user