Sync public tree to c852e65
This commit is contained in:
parent
4e04601794
commit
e2bc145f60
4 changed files with 223 additions and 7 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"kind": "disasmer-filtered-public-tree",
|
||||
"source_commit": "8a8f27c7bdaed2573256af31204dd4142be3319d",
|
||||
"release_name": "dryrun-8a8f27c7bdae",
|
||||
"source_commit": "c852e659435428b69cf3991a3fad015f60d121ff",
|
||||
"release_name": "dryrun-c852e6594354",
|
||||
"filtered_out": [
|
||||
"private/**",
|
||||
"experiments/**",
|
||||
|
|
|
|||
|
|
@ -1921,6 +1921,10 @@ fn artifact_download_report(args: ArtifactDownloadArgs) -> Result<Value> {
|
|||
"status": "requires_coordinator",
|
||||
"link_issued": false,
|
||||
"explicit_user_action_required": true,
|
||||
"machine_error": cli_error_summary_for_category(
|
||||
"connectivity",
|
||||
"artifact download requires a coordinator"
|
||||
),
|
||||
},
|
||||
"grant_disclosures": [],
|
||||
}))
|
||||
|
|
@ -1949,6 +1953,11 @@ fn artifact_export_report(args: ArtifactExportArgs) -> Result<Value> {
|
|||
"explicit_user_action": true,
|
||||
"local_path": &args.to,
|
||||
"local_bytes_written_by_cli": false,
|
||||
"machine_error": artifact_response_machine_error(
|
||||
&response,
|
||||
"coordinator rejected artifact export",
|
||||
"connectivity"
|
||||
),
|
||||
})
|
||||
};
|
||||
apply_local_export_summary(&mut export_plan, &local_export);
|
||||
|
|
@ -1980,6 +1989,10 @@ fn artifact_export_report(args: ArtifactExportArgs) -> Result<Value> {
|
|||
"explicit_user_action": true,
|
||||
"local_bytes_written_by_cli": false,
|
||||
"default_durable_store_assumed": false,
|
||||
"machine_error": cli_error_summary_for_category(
|
||||
"connectivity",
|
||||
"artifact export requires a coordinator"
|
||||
),
|
||||
},
|
||||
"grant_disclosures": [],
|
||||
}))
|
||||
|
|
@ -2014,6 +2027,11 @@ fn artifact_export_local_write_followup(
|
|||
"content_bytes_available": false,
|
||||
"download_session": artifact_download_session_summary(&link_response),
|
||||
"grant_disclosures": [],
|
||||
"machine_error": artifact_response_machine_error(
|
||||
&link_response,
|
||||
"coordinator rejected artifact download for local export",
|
||||
"connectivity"
|
||||
),
|
||||
"link_response": link_response,
|
||||
}));
|
||||
}
|
||||
|
|
@ -2045,6 +2063,11 @@ fn artifact_export_local_write_followup(
|
|||
"content_bytes_available": false,
|
||||
"download_session": download_session,
|
||||
"grant_disclosures": grant_disclosures,
|
||||
"machine_error": artifact_response_machine_error(
|
||||
&stream_response,
|
||||
"coordinator rejected artifact download stream",
|
||||
"connectivity"
|
||||
),
|
||||
"stream": artifact_stream_summary(&stream_response),
|
||||
}));
|
||||
}
|
||||
|
|
@ -2061,6 +2084,10 @@ fn artifact_export_local_write_followup(
|
|||
"content_bytes_available": false,
|
||||
"download_session": download_session,
|
||||
"grant_disclosures": grant_disclosures,
|
||||
"machine_error": cli_error_summary_for_category(
|
||||
"program",
|
||||
"artifact download stream opened without content"
|
||||
),
|
||||
"stream": artifact_stream_summary(&stream_response),
|
||||
}));
|
||||
};
|
||||
|
|
@ -2089,7 +2116,11 @@ fn artifact_export_local_write_followup(
|
|||
}
|
||||
|
||||
fn artifact_stream_summary(response: &Value) -> Value {
|
||||
json!({
|
||||
let status = response
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("coordinator_response");
|
||||
let mut summary = json!({
|
||||
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
||||
"streamed_bytes": response.get("streamed_bytes").cloned().unwrap_or_else(|| json!(0)),
|
||||
"charged_download_bytes": response.get("charged_download_bytes").cloned().unwrap_or_else(|| json!(0)),
|
||||
|
|
@ -2097,7 +2128,20 @@ fn artifact_stream_summary(response: &Value) -> Value {
|
|||
"content_source": response.get("content_source").cloned().unwrap_or(Value::Null),
|
||||
"content_material_returned_in_report": false,
|
||||
"error": response.get("message").cloned().unwrap_or(Value::Null),
|
||||
})
|
||||
});
|
||||
if status != "artifact_download_stream" {
|
||||
if let Some(object) = summary.as_object_mut() {
|
||||
object.insert(
|
||||
"machine_error".to_owned(),
|
||||
artifact_response_machine_error(
|
||||
response,
|
||||
"coordinator rejected artifact download stream",
|
||||
"connectivity",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
summary
|
||||
}
|
||||
|
||||
fn apply_local_export_summary(export_plan: &mut Value, local_export: &Value) {
|
||||
|
|
@ -2592,6 +2636,17 @@ fn human_report(value: &Value) -> String {
|
|||
}
|
||||
push_machine_error_line(&mut lines, value.get("machine_error"));
|
||||
push_machine_error_line(&mut lines, value.pointer("/run_start/machine_error"));
|
||||
push_machine_error_line(&mut lines, value.pointer("/download_session/machine_error"));
|
||||
push_machine_error_line(&mut lines, value.pointer("/export_plan/machine_error"));
|
||||
push_machine_error_line(&mut lines, value.pointer("/local_export/machine_error"));
|
||||
push_machine_error_line(
|
||||
&mut lines,
|
||||
value.pointer("/local_export/download_session/machine_error"),
|
||||
);
|
||||
push_machine_error_line(
|
||||
&mut lines,
|
||||
value.pointer("/local_export/stream/machine_error"),
|
||||
);
|
||||
if let Some(flow) = value.get("human_flow") {
|
||||
if let Some(device) = flow.get("Device") {
|
||||
lines.push("flow: device".to_owned());
|
||||
|
|
@ -2931,6 +2986,11 @@ fn apply_command_report_exit_code(value: &mut Value) -> Option<i32> {
|
|||
"/restart_request/machine_error",
|
||||
"/cancel_request/machine_error",
|
||||
"/task_restart/machine_error",
|
||||
"/download_session/machine_error",
|
||||
"/export_plan/machine_error",
|
||||
"/local_export/machine_error",
|
||||
"/local_export/download_session/machine_error",
|
||||
"/local_export/stream/machine_error",
|
||||
] {
|
||||
let Some(machine_error) = value.pointer_mut(pointer) else {
|
||||
continue;
|
||||
|
|
@ -4126,6 +4186,15 @@ fn response_error_message(response: &Value, fallback: &str) -> String {
|
|||
.unwrap_or_else(|| fallback.to_owned())
|
||||
}
|
||||
|
||||
fn artifact_response_machine_error(
|
||||
response: &Value,
|
||||
fallback: &str,
|
||||
default_category: &'static str,
|
||||
) -> Value {
|
||||
let message = response_error_message(response, fallback);
|
||||
cli_error_summary_with_default(&message, default_category)
|
||||
}
|
||||
|
||||
fn process_restart_request_summary(response: &Value, requires_confirmation: bool) -> Value {
|
||||
if response.get("type").and_then(Value::as_str) != Some("process_started") {
|
||||
let message = response_error_message(response, "coordinator rejected process restart");
|
||||
|
|
@ -4248,11 +4317,13 @@ fn task_restart_request_summary(response: &Value, requires_confirmation: bool) -
|
|||
|
||||
fn artifact_download_session_summary(response: &Value) -> Value {
|
||||
if response.get("type").and_then(Value::as_str) != Some("artifact_download_link") {
|
||||
let message = response_error_message(response, "coordinator rejected artifact download");
|
||||
return json!({
|
||||
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
||||
"link_issued": false,
|
||||
"explicit_user_action_required": true,
|
||||
"error": response.get("message").cloned().unwrap_or(Value::Null),
|
||||
"error": message.clone(),
|
||||
"machine_error": cli_error_summary_with_default(&message, "connectivity"),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -4317,13 +4388,15 @@ fn artifact_download_grant_disclosures(response: &Value) -> Value {
|
|||
|
||||
fn artifact_export_plan_summary(response: &Value, to: &Path) -> Value {
|
||||
if response.get("type").and_then(Value::as_str) != Some("artifact_export_plan") {
|
||||
let message = response_error_message(response, "coordinator rejected artifact export");
|
||||
return json!({
|
||||
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
||||
"explicit_user_action": true,
|
||||
"local_path": to,
|
||||
"local_bytes_written_by_cli": false,
|
||||
"default_durable_store_assumed": false,
|
||||
"error": response.get("message").cloned().unwrap_or(Value::Null),
|
||||
"error": message.clone(),
|
||||
"machine_error": cli_error_summary_with_default(&message, "connectivity"),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -8563,6 +8636,65 @@ mod tests {
|
|||
assert_eq!(export["export_plan"]["authorization_digest_present"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artifact_failure_reports_apply_stable_exit_codes() {
|
||||
let mut download = json!({
|
||||
"command": "artifact download",
|
||||
"download_session": artifact_download_session_summary(&json!({
|
||||
"type": "error",
|
||||
"message": "artifact download unauthorized for project",
|
||||
})),
|
||||
});
|
||||
assert_eq!(apply_command_report_exit_code(&mut download), Some(21));
|
||||
assert_eq!(
|
||||
download["download_session"]["machine_error"]["category"],
|
||||
"authorization"
|
||||
);
|
||||
assert_eq!(
|
||||
download["download_session"]["machine_error"]["process_exit_code_applied"],
|
||||
true
|
||||
);
|
||||
|
||||
let mut export = json!({
|
||||
"command": "artifact export",
|
||||
"export_plan": artifact_export_plan_summary(
|
||||
&json!({
|
||||
"type": "error",
|
||||
"message": "direct connectivity unavailable for artifact export",
|
||||
}),
|
||||
Path::new("dist/app.txt"),
|
||||
),
|
||||
});
|
||||
assert_eq!(apply_command_report_exit_code(&mut export), Some(25));
|
||||
assert_eq!(
|
||||
export["export_plan"]["machine_error"]["category"],
|
||||
"connectivity"
|
||||
);
|
||||
assert_eq!(
|
||||
export["export_plan"]["machine_error"]["process_exit_code_applied"],
|
||||
true
|
||||
);
|
||||
|
||||
let mut stream = json!({
|
||||
"command": "artifact export",
|
||||
"local_export": {
|
||||
"stream": artifact_stream_summary(&json!({
|
||||
"type": "error",
|
||||
"message": "quota unavailable: resource limit exceeded for artifact_download_bytes",
|
||||
})),
|
||||
},
|
||||
});
|
||||
assert_eq!(apply_command_report_exit_code(&mut stream), Some(22));
|
||||
assert_eq!(
|
||||
stream["local_export"]["stream"]["machine_error"]["category"],
|
||||
"quota"
|
||||
);
|
||||
assert_eq!(
|
||||
stream["local_export"]["stream"]["machine_error"]["process_exit_code_applied"],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_restart_and_cancel_reports_expose_control_boundaries() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,27 @@ function runDisasmer(args) {
|
|||
});
|
||||
}
|
||||
|
||||
async function runWithOneCoordinatorResponse(buildArgs, response) {
|
||||
let request = "";
|
||||
const server = net.createServer((socket) => {
|
||||
socket.setEncoding("utf8");
|
||||
socket.on("data", (chunk) => {
|
||||
request += chunk;
|
||||
if (!request.includes("\n")) return;
|
||||
socket.write(JSON.stringify(response) + "\n");
|
||||
socket.end();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
const address = await new Promise((resolve) => {
|
||||
server.listen(0, "127.0.0.1", () => resolve(server.address()));
|
||||
});
|
||||
const coordinator = `http://${address.address}:${address.port}`;
|
||||
const result = await runDisasmer(buildArgs(coordinator));
|
||||
return { request, result };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const nonInteractive = await runDisasmer([
|
||||
"run",
|
||||
|
|
@ -120,6 +141,62 @@ async function main() {
|
|||
report.run_start.machine_error.next_actions.includes("disasmer quota status")
|
||||
);
|
||||
|
||||
const artifactDownload = await runWithOneCoordinatorResponse(
|
||||
(coordinator) => [
|
||||
"artifact",
|
||||
"download",
|
||||
"app.txt",
|
||||
"--coordinator",
|
||||
coordinator,
|
||||
"--json",
|
||||
],
|
||||
{
|
||||
type: "artifact_download_denied",
|
||||
message: "artifact download unauthorized for project",
|
||||
}
|
||||
);
|
||||
assert.strictEqual(artifactDownload.result.signal, null, artifactDownload.result.stderr);
|
||||
assert.strictEqual(artifactDownload.result.code, 21, artifactDownload.result.stderr);
|
||||
assert.match(artifactDownload.request, /"type":"create_artifact_download_link"/);
|
||||
const artifactDownloadReport = JSON.parse(artifactDownload.result.stdout);
|
||||
assert.strictEqual(
|
||||
artifactDownloadReport.download_session.machine_error.category,
|
||||
"authorization"
|
||||
);
|
||||
assert.strictEqual(
|
||||
artifactDownloadReport.download_session.machine_error.process_exit_code_applied,
|
||||
true
|
||||
);
|
||||
|
||||
const artifactExport = await runWithOneCoordinatorResponse(
|
||||
(coordinator) => [
|
||||
"artifact",
|
||||
"export",
|
||||
"app.txt",
|
||||
"--to",
|
||||
path.join(project, "target", "blocked-artifact.txt"),
|
||||
"--coordinator",
|
||||
coordinator,
|
||||
"--json",
|
||||
],
|
||||
{
|
||||
type: "artifact_export_unavailable",
|
||||
message: "direct connectivity unavailable for artifact export",
|
||||
}
|
||||
);
|
||||
assert.strictEqual(artifactExport.result.signal, null, artifactExport.result.stderr);
|
||||
assert.strictEqual(artifactExport.result.code, 25, artifactExport.result.stderr);
|
||||
assert.match(artifactExport.request, /"type":"export_artifact_to_node"/);
|
||||
const artifactExportReport = JSON.parse(artifactExport.result.stdout);
|
||||
assert.strictEqual(
|
||||
artifactExportReport.export_plan.machine_error.category,
|
||||
"connectivity"
|
||||
);
|
||||
assert.strictEqual(
|
||||
artifactExportReport.export_plan.machine_error.process_exit_code_applied,
|
||||
true
|
||||
);
|
||||
|
||||
const confirmation = await runDisasmer([
|
||||
"process",
|
||||
"cancel",
|
||||
|
|
|
|||
|
|
@ -430,7 +430,12 @@ expect(
|
|||
expect(
|
||||
cli,
|
||||
"CLI applies process exit code after printing command failure report",
|
||||
/fn emit_report<T: Serialize>[\s\S]*apply_command_report_exit_code[\s\S]*std::process::exit\(exit_code\)/
|
||||
/fn emit_report<T: Serialize>[\s\S]*apply_command_report_exit_code[\s\S]*std::process::exit\(exit_code\)[\s\S]*fn human_report/
|
||||
);
|
||||
expect(
|
||||
cli,
|
||||
"CLI applies artifact nested failure exit codes",
|
||||
/fn apply_command_report_exit_code[\s\S]*"\/download_session\/machine_error"[\s\S]*"\/export_plan\/machine_error"[\s\S]*"\/local_export\/machine_error"[\s\S]*"\/local_export\/download_session\/machine_error"[\s\S]*"\/local_export\/stream\/machine_error"/
|
||||
);
|
||||
expect(
|
||||
cli,
|
||||
|
|
@ -541,6 +546,8 @@ for (const [name, pattern] of [
|
|||
["actual quota resource-category assertion", /machine_error\.resource_category, "api_calls"/],
|
||||
["actual quota community-tier label assertion", /machine_error\.community_tier_label,[\s\S]*"community tier"/],
|
||||
["actual quota abuse-heuristics assertion", /machine_error\.private_abuse_heuristics_exposed,[\s\S]*false/],
|
||||
["artifact download rejection exit code", /artifactDownload[\s\S]*assert\.strictEqual\(artifactDownload\.result\.code, 21/],
|
||||
["artifact export rejection exit code", /artifactExport[\s\S]*assert\.strictEqual\(artifactExport\.result\.code, 25/],
|
||||
["exit-code application in JSON", /process_exit_code_applied[\s\S]*true/],
|
||||
]) {
|
||||
expect(errorExitSmoke, name, pattern);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue