Public dry run dryrun-01b2c7b06dd3
This commit is contained in:
parent
fe9da2782d
commit
7c05d077a1
3 changed files with 184 additions and 2 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"kind": "disasmer-filtered-public-tree",
|
||||
"source_commit": "7a9befca72b4465a27fbd8ab7a6f8d86f4387996",
|
||||
"release_name": "dryrun-7a9befca72b4",
|
||||
"source_commit": "01b2c7b06dd306582f1a83dce6d6646658854db7",
|
||||
"release_name": "dryrun-01b2c7b06dd3",
|
||||
"filtered_out": [
|
||||
"private/**",
|
||||
"experiments/**",
|
||||
|
|
|
|||
|
|
@ -1231,9 +1231,13 @@ fn artifact_download_report(args: ArtifactDownloadArgs) -> Result<Value> {
|
|||
"token_nonce": nonce,
|
||||
"now_epoch_seconds": 0,
|
||||
}))?;
|
||||
let download_session = artifact_download_session_summary(&response);
|
||||
return Ok(json!({
|
||||
"command": "artifact download",
|
||||
"coordinator": coordinator,
|
||||
"artifact": args.artifact,
|
||||
"max_bytes": args.max_bytes,
|
||||
"download_session": download_session,
|
||||
"response": response,
|
||||
"coordinator_session_requests": session.requests(),
|
||||
}));
|
||||
|
|
@ -1243,6 +1247,11 @@ fn artifact_download_report(args: ArtifactDownloadArgs) -> Result<Value> {
|
|||
"status": "requires_coordinator",
|
||||
"artifact": args.artifact,
|
||||
"max_bytes": args.max_bytes,
|
||||
"download_session": {
|
||||
"status": "requires_coordinator",
|
||||
"link_issued": false,
|
||||
"explicit_user_action_required": true,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -1259,10 +1268,14 @@ fn artifact_export_report(args: ArtifactExportArgs) -> Result<Value> {
|
|||
"direct_connectivity": true,
|
||||
"failure_reason": "",
|
||||
}))?;
|
||||
let export_plan = artifact_export_plan_summary(&response, &args.to);
|
||||
return Ok(json!({
|
||||
"command": "artifact export",
|
||||
"coordinator": coordinator,
|
||||
"artifact": args.artifact,
|
||||
"to": args.to,
|
||||
"receiver_node": args.receiver_node,
|
||||
"export_plan": export_plan,
|
||||
"response": response,
|
||||
"coordinator_session_requests": session.requests(),
|
||||
}));
|
||||
|
|
@ -1273,6 +1286,12 @@ fn artifact_export_report(args: ArtifactExportArgs) -> Result<Value> {
|
|||
"artifact": args.artifact,
|
||||
"to": args.to,
|
||||
"receiver_node": args.receiver_node,
|
||||
"export_plan": {
|
||||
"status": "requires_coordinator",
|
||||
"explicit_user_action": true,
|
||||
"local_bytes_written_by_cli": false,
|
||||
"default_durable_store_assumed": false,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -2329,6 +2348,72 @@ fn artifact_name_from_path(path: &str) -> String {
|
|||
path.rsplit('/').next().unwrap_or(path).to_owned()
|
||||
}
|
||||
|
||||
fn artifact_download_session_summary(response: &Value) -> Value {
|
||||
if response.get("type").and_then(Value::as_str) != Some("artifact_download_link") {
|
||||
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),
|
||||
});
|
||||
}
|
||||
|
||||
let link = response.get("link").unwrap_or(&Value::Null);
|
||||
json!({
|
||||
"status": "download_link_issued",
|
||||
"link_issued": true,
|
||||
"explicit_user_action_required": true,
|
||||
"coordinator_preflight": "completed_before_link_issued",
|
||||
"tenant": link.get("tenant").cloned().unwrap_or(Value::Null),
|
||||
"project": link.get("project").cloned().unwrap_or(Value::Null),
|
||||
"process": link.get("process").cloned().unwrap_or(Value::Null),
|
||||
"artifact": link.get("artifact").cloned().unwrap_or(Value::Null),
|
||||
"actor": link.get("actor").cloned().unwrap_or(Value::Null),
|
||||
"source": link.get("source").cloned().unwrap_or(Value::Null),
|
||||
"url_path": link.get("url_path").cloned().unwrap_or(Value::Null),
|
||||
"expires_at_epoch_seconds": link.get("expires_at_epoch_seconds").cloned().unwrap_or(Value::Null),
|
||||
"max_bytes": link.get("max_bytes").cloned().unwrap_or(Value::Null),
|
||||
"token_material_returned": false,
|
||||
"scoped_token_digest_present": link.get("scoped_token_digest").is_some(),
|
||||
"policy_context_digest_present": link.get("policy_context_digest").is_some(),
|
||||
"cross_tenant_usable": false,
|
||||
"default_durable_store_assumed": false,
|
||||
})
|
||||
}
|
||||
|
||||
fn artifact_export_plan_summary(response: &Value, to: &Path) -> Value {
|
||||
if response.get("type").and_then(Value::as_str) != Some("artifact_export_plan") {
|
||||
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),
|
||||
});
|
||||
}
|
||||
|
||||
let plan = response.get("plan").unwrap_or(&Value::Null);
|
||||
json!({
|
||||
"status": "transfer_plan_created",
|
||||
"explicit_user_action": true,
|
||||
"local_path": to,
|
||||
"local_bytes_written_by_cli": false,
|
||||
"writes_require_data_plane_followup": true,
|
||||
"default_durable_store_assumed": false,
|
||||
"source_node": response.get("source_node").cloned().unwrap_or(Value::Null),
|
||||
"receiver_node": response.get("receiver_node").cloned().unwrap_or(Value::Null),
|
||||
"transport": plan.get("transport").cloned().unwrap_or(Value::Null),
|
||||
"artifact": plan.pointer("/scope/object/Artifact").cloned().unwrap_or(Value::Null),
|
||||
"tenant": plan.pointer("/scope/tenant").cloned().unwrap_or(Value::Null),
|
||||
"project": plan.pointer("/scope/project").cloned().unwrap_or(Value::Null),
|
||||
"process": plan.pointer("/scope/process").cloned().unwrap_or(Value::Null),
|
||||
"coordinator_assisted_rendezvous": plan.get("coordinator_assisted_rendezvous").cloned().unwrap_or(Value::Null),
|
||||
"coordinator_bulk_relay_allowed": plan.get("coordinator_bulk_relay_allowed").cloned().unwrap_or(Value::Null),
|
||||
"authorization_digest_present": plan.get("authorization_digest").is_some(),
|
||||
})
|
||||
}
|
||||
|
||||
fn task_event_count(task_events: Option<&Value>) -> usize {
|
||||
task_events
|
||||
.and_then(|task_events| task_events.pointer("/response/events"))
|
||||
|
|
@ -4245,6 +4330,102 @@ mod tests {
|
|||
assert_eq!(artifacts["default_durable_store_assumed"], false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artifact_download_and_export_reports_expose_safe_session_boundaries() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap().to_string();
|
||||
let server = std::thread::spawn(move || {
|
||||
let download_response = concat!(
|
||||
r#"{"type":"artifact_download_link","link":{"artifact":"app.txt","source":{"RetainedNode":"node-a"},"url_path":"/artifacts/tenant/project/vp/app.txt","scoped_token_digest":"sha256:token","expires_at_epoch_seconds":60,"tenant":"tenant","project":"project","process":"vp","actor":{"User":"user"},"max_bytes":2048,"policy_context_digest":"sha256:policy"}}"#
|
||||
);
|
||||
let export_response = concat!(
|
||||
r#"{"type":"artifact_export_plan","source_node":"node-a","receiver_node":"node-b","plan":{"transport":"NativeQuic","scope":{"tenant":"tenant","project":"project","process":"vp","object":{"Artifact":"app.txt"},"authorization_subject":"artifact-export:node-a-to-node-b"},"source":{"node":"node-a","advertised_addr":"quic://node-a","public_key_fingerprint":"sha256:source"},"destination":{"node":"node-b","advertised_addr":"quic://node-b","public_key_fingerprint":"sha256:destination"},"authorization_digest":"sha256:auth","coordinator_assisted_rendezvous":true,"coordinator_bulk_relay_allowed":false}}"#
|
||||
);
|
||||
for expected in ["create_artifact_download_link", "export_artifact_to_node"] {
|
||||
let (mut stream, _) = listener.accept().unwrap();
|
||||
let mut reader = BufReader::new(stream.try_clone().unwrap());
|
||||
let mut line = String::new();
|
||||
reader.read_line(&mut line).unwrap();
|
||||
assert!(line.contains(&format!(r#""type":"{expected}""#)));
|
||||
assert!(line.contains(r#""tenant":"tenant""#));
|
||||
assert!(line.contains(r#""project":"project""#));
|
||||
assert!(line.contains(r#""artifact":"app.txt""#));
|
||||
if expected == "create_artifact_download_link" {
|
||||
assert!(line.contains(r#""max_bytes":2048"#));
|
||||
stream.write_all(download_response.as_bytes()).unwrap();
|
||||
} else {
|
||||
assert!(line.contains(r#""receiver_node":"node-b""#));
|
||||
stream.write_all(export_response.as_bytes()).unwrap();
|
||||
}
|
||||
stream.write_all(b"\n").unwrap();
|
||||
}
|
||||
});
|
||||
let scope = CliScopeArgs {
|
||||
coordinator: Some(addr),
|
||||
tenant: "tenant".to_owned(),
|
||||
project: "project".to_owned(),
|
||||
user: "user".to_owned(),
|
||||
json: false,
|
||||
};
|
||||
|
||||
let download = artifact_download_report(ArtifactDownloadArgs {
|
||||
scope: scope.clone(),
|
||||
artifact: "app.txt".to_owned(),
|
||||
max_bytes: 2048,
|
||||
})
|
||||
.unwrap();
|
||||
let export = artifact_export_report(ArtifactExportArgs {
|
||||
scope,
|
||||
artifact: "app.txt".to_owned(),
|
||||
to: PathBuf::from("/tmp/app.txt"),
|
||||
receiver_node: "node-b".to_owned(),
|
||||
})
|
||||
.unwrap();
|
||||
server.join().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
download["download_session"]["status"],
|
||||
"download_link_issued"
|
||||
);
|
||||
assert_eq!(download["download_session"]["tenant"], "tenant");
|
||||
assert_eq!(download["download_session"]["project"], "project");
|
||||
assert_eq!(download["download_session"]["process"], "vp");
|
||||
assert_eq!(
|
||||
download["download_session"]["source"]["RetainedNode"],
|
||||
"node-a"
|
||||
);
|
||||
assert_eq!(download["download_session"]["max_bytes"], 2048);
|
||||
assert_eq!(
|
||||
download["download_session"]["token_material_returned"],
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
download["download_session"]["scoped_token_digest_present"],
|
||||
true
|
||||
);
|
||||
assert_eq!(download["download_session"]["cross_tenant_usable"], false);
|
||||
assert_eq!(
|
||||
download["download_session"]["default_durable_store_assumed"],
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(export["export_plan"]["status"], "transfer_plan_created");
|
||||
assert_eq!(export["export_plan"]["source_node"], "node-a");
|
||||
assert_eq!(export["export_plan"]["receiver_node"], "node-b");
|
||||
assert_eq!(export["export_plan"]["artifact"], "app.txt");
|
||||
assert_eq!(export["export_plan"]["local_path"], "/tmp/app.txt");
|
||||
assert_eq!(export["export_plan"]["local_bytes_written_by_cli"], false);
|
||||
assert_eq!(
|
||||
export["export_plan"]["default_durable_store_assumed"],
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
export["export_plan"]["coordinator_bulk_relay_allowed"],
|
||||
false
|
||||
);
|
||||
assert_eq!(export["export_plan"]["authorization_digest_present"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_command_reuses_bundle_inspection_without_full_repo_upload() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ for (const [name, pattern] of [
|
|||
["quota local status coverage", /fn quota_status_uses_project_config_and_generic_public_limits\(\)/],
|
||||
["quota coordinator usage coverage", /fn quota_status_queries_public_coordinator_usage\(\)/],
|
||||
["task event summary coverage", /fn process_task_log_and_artifact_reports_summarize_task_events\(\)/],
|
||||
["artifact download/export report coverage", /fn artifact_download_and_export_reports_expose_safe_session_boundaries\(\)/],
|
||||
["build no full repo upload coverage", /fn build_command_reuses_bundle_inspection_without_full_repo_upload\(\)/],
|
||||
["safe coordinator-required plans", /fn node_enroll_and_process_commands_have_safe_plan_without_coordinator\(\)/],
|
||||
]) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue