修复预览构建状态解析与工作进程恢复
限制 Jenkins 状态接口只返回监控所需字段 避免完整构建 JSON 触发反序列化递归深度限制 让预览外部生成工作进程在身份初始化后自动重启 补充定向测试、防回归检查和技术说明
This commit is contained in:
@@ -94,6 +94,8 @@ SpacetimeDB 2.7 CLI 发布到受控 Compose 网络地址时固定使用 `--yes=r
|
||||
|
||||
Jenkins 在构建完成、归档 artifact 和更新 REST 状态之间可能短暂返回不可解析的状态正文。控制服务对队列、构建状态和 artifact 查询执行有限重试;单次瞬态响应不得把已经成功并健康的部署永久写成 `failed`。
|
||||
|
||||
控制服务查询 Jenkins 队列与构建状态时必须使用 `tree` 参数限制到所需字段,避免完整 `api/json` 的大体积深层对象触发 JSON 递归深度限制。预览 Compose 中的外部生成 worker 使用 `restart: on-failure`;它若早于 API 完成模型定价运行时身份初始化而启动失败,应由 Docker 自动重启并在身份就绪后稳定运行。
|
||||
|
||||
## 安全边界
|
||||
|
||||
- 服务端缺少控制面访问口令或 Jenkins service account 凭据时必须拒绝启动,不允许退化成匿名写接口。
|
||||
|
||||
@@ -111,6 +111,16 @@ assertIncludes(
|
||||
'spacetimedb:\n mem_limit: 2g\n ports: !reset []',
|
||||
'预览 SpacetimeDB 必须覆盖压测基线内存限制,避免模块首次加载时 OOM。',
|
||||
);
|
||||
assertIncludes(
|
||||
deployer,
|
||||
'external-generation-worker:\n build:',
|
||||
'预览 compose override 必须配置外部生成 worker。',
|
||||
);
|
||||
assertIncludes(
|
||||
deployer,
|
||||
'restart: on-failure',
|
||||
'预览外部生成 worker 必须在运行时身份初始化竞态后自动重启。',
|
||||
);
|
||||
assertIncludes(
|
||||
deployer,
|
||||
'^preview-[0-9a-f]{16}$',
|
||||
|
||||
@@ -296,6 +296,7 @@ services:
|
||||
build:
|
||||
context: ${GENARRATIVE_PREVIEW_SOURCE_DIR}
|
||||
dockerfile: ${GENARRATIVE_PREVIEW_CONTROLLER_ROOT}/deploy/container/api-server.Dockerfile
|
||||
restart: on-failure
|
||||
nginx:
|
||||
build:
|
||||
context: ${GENARRATIVE_PREVIEW_SOURCE_DIR}
|
||||
|
||||
@@ -205,10 +205,13 @@ impl JenkinsClient {
|
||||
Fut: Future<Output = ()>,
|
||||
{
|
||||
let build_url = loop {
|
||||
let item_url = reference
|
||||
let mut item_url = reference
|
||||
.queue_url
|
||||
.join("api/json")
|
||||
.map_err(|error| error.to_string())?;
|
||||
item_url
|
||||
.query_pairs_mut()
|
||||
.append_pair("tree", "cancelled,executable[url]");
|
||||
let item: QueueItem = self.get_json_with_retry(item_url).await?;
|
||||
if item.cancelled.unwrap_or(false) {
|
||||
return Ok(JenkinsOutcome {
|
||||
@@ -226,9 +229,12 @@ impl JenkinsClient {
|
||||
|
||||
on_build(build_url.as_str()).await;
|
||||
let successful = loop {
|
||||
let state_url = build_url
|
||||
let mut state_url = build_url
|
||||
.join("api/json")
|
||||
.map_err(|error| error.to_string())?;
|
||||
state_url
|
||||
.query_pairs_mut()
|
||||
.append_pair("tree", "building,result");
|
||||
let state: BuildState = self.get_json_with_retry(state_url).await?;
|
||||
if state.building {
|
||||
tokio::time::sleep(self.poll_interval).await;
|
||||
|
||||
@@ -103,7 +103,13 @@ async fn mock_queue(axum::extract::Path(id): axum::extract::Path<u64>) -> Json<V
|
||||
)
|
||||
}
|
||||
|
||||
async fn mock_build(State(state): State<MockJenkinsState>) -> impl IntoResponse {
|
||||
async fn mock_build(
|
||||
State(state): State<MockJenkinsState>,
|
||||
uri: axum::http::Uri,
|
||||
) -> impl IntoResponse {
|
||||
if uri.query() != Some("tree=building%2Cresult") {
|
||||
return (StatusCode::BAD_REQUEST, "missing bounded tree query").into_response();
|
||||
}
|
||||
if state
|
||||
.invalid_build_responses
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |remaining| {
|
||||
|
||||
Reference in New Issue
Block a user