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",
|
"kind": "disasmer-filtered-public-tree",
|
||||||
"source_commit": "8a8f27c7bdaed2573256af31204dd4142be3319d",
|
"source_commit": "c852e659435428b69cf3991a3fad015f60d121ff",
|
||||||
"release_name": "dryrun-8a8f27c7bdae",
|
"release_name": "dryrun-c852e6594354",
|
||||||
"filtered_out": [
|
"filtered_out": [
|
||||||
"private/**",
|
"private/**",
|
||||||
"experiments/**",
|
"experiments/**",
|
||||||
|
|
|
||||||
|
|
@ -1921,6 +1921,10 @@ fn artifact_download_report(args: ArtifactDownloadArgs) -> Result<Value> {
|
||||||
"status": "requires_coordinator",
|
"status": "requires_coordinator",
|
||||||
"link_issued": false,
|
"link_issued": false,
|
||||||
"explicit_user_action_required": true,
|
"explicit_user_action_required": true,
|
||||||
|
"machine_error": cli_error_summary_for_category(
|
||||||
|
"connectivity",
|
||||||
|
"artifact download requires a coordinator"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
"grant_disclosures": [],
|
"grant_disclosures": [],
|
||||||
}))
|
}))
|
||||||
|
|
@ -1949,6 +1953,11 @@ fn artifact_export_report(args: ArtifactExportArgs) -> Result<Value> {
|
||||||
"explicit_user_action": true,
|
"explicit_user_action": true,
|
||||||
"local_path": &args.to,
|
"local_path": &args.to,
|
||||||
"local_bytes_written_by_cli": false,
|
"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);
|
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,
|
"explicit_user_action": true,
|
||||||
"local_bytes_written_by_cli": false,
|
"local_bytes_written_by_cli": false,
|
||||||
"default_durable_store_assumed": false,
|
"default_durable_store_assumed": false,
|
||||||
|
"machine_error": cli_error_summary_for_category(
|
||||||
|
"connectivity",
|
||||||
|
"artifact export requires a coordinator"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
"grant_disclosures": [],
|
"grant_disclosures": [],
|
||||||
}))
|
}))
|
||||||
|
|
@ -2014,6 +2027,11 @@ fn artifact_export_local_write_followup(
|
||||||
"content_bytes_available": false,
|
"content_bytes_available": false,
|
||||||
"download_session": artifact_download_session_summary(&link_response),
|
"download_session": artifact_download_session_summary(&link_response),
|
||||||
"grant_disclosures": [],
|
"grant_disclosures": [],
|
||||||
|
"machine_error": artifact_response_machine_error(
|
||||||
|
&link_response,
|
||||||
|
"coordinator rejected artifact download for local export",
|
||||||
|
"connectivity"
|
||||||
|
),
|
||||||
"link_response": link_response,
|
"link_response": link_response,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -2045,6 +2063,11 @@ fn artifact_export_local_write_followup(
|
||||||
"content_bytes_available": false,
|
"content_bytes_available": false,
|
||||||
"download_session": download_session,
|
"download_session": download_session,
|
||||||
"grant_disclosures": grant_disclosures,
|
"grant_disclosures": grant_disclosures,
|
||||||
|
"machine_error": artifact_response_machine_error(
|
||||||
|
&stream_response,
|
||||||
|
"coordinator rejected artifact download stream",
|
||||||
|
"connectivity"
|
||||||
|
),
|
||||||
"stream": artifact_stream_summary(&stream_response),
|
"stream": artifact_stream_summary(&stream_response),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -2061,6 +2084,10 @@ fn artifact_export_local_write_followup(
|
||||||
"content_bytes_available": false,
|
"content_bytes_available": false,
|
||||||
"download_session": download_session,
|
"download_session": download_session,
|
||||||
"grant_disclosures": grant_disclosures,
|
"grant_disclosures": grant_disclosures,
|
||||||
|
"machine_error": cli_error_summary_for_category(
|
||||||
|
"program",
|
||||||
|
"artifact download stream opened without content"
|
||||||
|
),
|
||||||
"stream": artifact_stream_summary(&stream_response),
|
"stream": artifact_stream_summary(&stream_response),
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
@ -2089,7 +2116,11 @@ fn artifact_export_local_write_followup(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn artifact_stream_summary(response: &Value) -> Value {
|
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"),
|
"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)),
|
"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)),
|
"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_source": response.get("content_source").cloned().unwrap_or(Value::Null),
|
||||||
"content_material_returned_in_report": false,
|
"content_material_returned_in_report": false,
|
||||||
"error": response.get("message").cloned().unwrap_or(Value::Null),
|
"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) {
|
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.get("machine_error"));
|
||||||
push_machine_error_line(&mut lines, value.pointer("/run_start/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(flow) = value.get("human_flow") {
|
||||||
if let Some(device) = flow.get("Device") {
|
if let Some(device) = flow.get("Device") {
|
||||||
lines.push("flow: device".to_owned());
|
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",
|
"/restart_request/machine_error",
|
||||||
"/cancel_request/machine_error",
|
"/cancel_request/machine_error",
|
||||||
"/task_restart/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 {
|
let Some(machine_error) = value.pointer_mut(pointer) else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -4126,6 +4186,15 @@ fn response_error_message(response: &Value, fallback: &str) -> String {
|
||||||
.unwrap_or_else(|| fallback.to_owned())
|
.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 {
|
fn process_restart_request_summary(response: &Value, requires_confirmation: bool) -> Value {
|
||||||
if response.get("type").and_then(Value::as_str) != Some("process_started") {
|
if response.get("type").and_then(Value::as_str) != Some("process_started") {
|
||||||
let message = response_error_message(response, "coordinator rejected process restart");
|
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 {
|
fn artifact_download_session_summary(response: &Value) -> Value {
|
||||||
if response.get("type").and_then(Value::as_str) != Some("artifact_download_link") {
|
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!({
|
return json!({
|
||||||
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
||||||
"link_issued": false,
|
"link_issued": false,
|
||||||
"explicit_user_action_required": true,
|
"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 {
|
fn artifact_export_plan_summary(response: &Value, to: &Path) -> Value {
|
||||||
if response.get("type").and_then(Value::as_str) != Some("artifact_export_plan") {
|
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!({
|
return json!({
|
||||||
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
"status": response.get("type").and_then(Value::as_str).unwrap_or("coordinator_response"),
|
||||||
"explicit_user_action": true,
|
"explicit_user_action": true,
|
||||||
"local_path": to,
|
"local_path": to,
|
||||||
"local_bytes_written_by_cli": false,
|
"local_bytes_written_by_cli": false,
|
||||||
"default_durable_store_assumed": 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);
|
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]
|
#[test]
|
||||||
fn process_restart_and_cancel_reports_expose_control_boundaries() {
|
fn process_restart_and_cancel_reports_expose_control_boundaries() {
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
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() {
|
async function main() {
|
||||||
const nonInteractive = await runDisasmer([
|
const nonInteractive = await runDisasmer([
|
||||||
"run",
|
"run",
|
||||||
|
|
@ -120,6 +141,62 @@ async function main() {
|
||||||
report.run_start.machine_error.next_actions.includes("disasmer quota status")
|
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([
|
const confirmation = await runDisasmer([
|
||||||
"process",
|
"process",
|
||||||
"cancel",
|
"cancel",
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,12 @@ expect(
|
||||||
expect(
|
expect(
|
||||||
cli,
|
cli,
|
||||||
"CLI applies process exit code after printing command failure report",
|
"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(
|
expect(
|
||||||
cli,
|
cli,
|
||||||
|
|
@ -541,6 +546,8 @@ for (const [name, pattern] of [
|
||||||
["actual quota resource-category assertion", /machine_error\.resource_category, "api_calls"/],
|
["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 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/],
|
["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/],
|
["exit-code application in JSON", /process_exit_code_applied[\s\S]*true/],
|
||||||
]) {
|
]) {
|
||||||
expect(errorExitSmoke, name, pattern);
|
expect(errorExitSmoke, name, pattern);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue