Attribute agent workflow dispatch
This commit is contained in:
parent
28779a2866
commit
c514ab7f81
5 changed files with 503 additions and 14 deletions
|
|
@ -558,6 +558,7 @@ enum CliSession {
|
|||
Anonymous,
|
||||
HumanSession,
|
||||
AgentPublicKey {
|
||||
agent: String,
|
||||
public_key_fingerprint: Digest,
|
||||
browser_interaction_required: bool,
|
||||
},
|
||||
|
|
@ -2285,11 +2286,13 @@ fn auth_state_value() -> Value {
|
|||
})
|
||||
}
|
||||
CliSession::AgentPublicKey {
|
||||
agent,
|
||||
public_key_fingerprint,
|
||||
browser_interaction_required,
|
||||
} => json!({
|
||||
"kind": "agent_public_key",
|
||||
"authenticated": true,
|
||||
"agent": agent,
|
||||
"source": "DISASMER_AGENT_PUBLIC_KEY",
|
||||
"public_key_fingerprint": public_key_fingerprint,
|
||||
"browser_interaction_required": browser_interaction_required,
|
||||
|
|
@ -3415,13 +3418,15 @@ fn coordinator_run_report(plan: RunPlan) -> Result<Value> {
|
|||
let coordinator = run_coordinator_endpoint(&plan)?;
|
||||
let process = "vp-current".to_owned();
|
||||
let mut session = JsonLineSession::connect(&coordinator)?;
|
||||
let response = session.request_allow_error(json!({
|
||||
let mut request = json!({
|
||||
"type": "start_process",
|
||||
"tenant": tenant.clone(),
|
||||
"project": project.clone(),
|
||||
"process": process.clone(),
|
||||
"restart": false,
|
||||
}))?;
|
||||
});
|
||||
add_workflow_actor_fields(&mut request, &plan.session, &user);
|
||||
let response = session.request_allow_error(request)?;
|
||||
let run_start = run_start_summary(&response);
|
||||
let status = run_start
|
||||
.get("status")
|
||||
|
|
@ -3435,6 +3440,7 @@ fn coordinator_run_report(plan: RunPlan) -> Result<Value> {
|
|||
"tenant": tenant,
|
||||
"project": project,
|
||||
"user": user,
|
||||
"workflow_actor": response.get("actor").cloned().unwrap_or(Value::Null),
|
||||
"coordinator": coordinator,
|
||||
"process": process,
|
||||
"run_start": run_start,
|
||||
|
|
@ -3464,6 +3470,7 @@ fn run_start_summary(response: &Value) -> Value {
|
|||
"accepted": true,
|
||||
"process": response.get("process").cloned().unwrap_or(Value::Null),
|
||||
"coordinator_epoch": response.get("epoch").cloned().unwrap_or(Value::Null),
|
||||
"actor": response.get("actor").cloned().unwrap_or(Value::Null),
|
||||
"restart": false,
|
||||
"single_active_process_boundary": true,
|
||||
"next_actions": [
|
||||
|
|
@ -3712,6 +3719,7 @@ fn session_from_env() -> CliSession {
|
|||
if let Some(public_key) = std::env::var_os("DISASMER_AGENT_PUBLIC_KEY") {
|
||||
let public_key = public_key.to_string_lossy();
|
||||
return CliSession::AgentPublicKey {
|
||||
agent: std::env::var("DISASMER_AGENT_ID").unwrap_or_else(|_| "agent".to_owned()),
|
||||
public_key_fingerprint: Digest::sha256(public_key.as_bytes()),
|
||||
browser_interaction_required: false,
|
||||
};
|
||||
|
|
@ -3722,6 +3730,28 @@ fn session_from_env() -> CliSession {
|
|||
CliSession::Anonymous
|
||||
}
|
||||
|
||||
fn add_workflow_actor_fields(request: &mut Value, session: &CliSession, fallback_user: &str) {
|
||||
let Value::Object(map) = request else {
|
||||
return;
|
||||
};
|
||||
match session {
|
||||
CliSession::AgentPublicKey {
|
||||
agent,
|
||||
public_key_fingerprint,
|
||||
..
|
||||
} => {
|
||||
map.insert("actor_agent".to_owned(), json!(agent));
|
||||
map.insert(
|
||||
"agent_public_key_fingerprint".to_owned(),
|
||||
json!(public_key_fingerprint),
|
||||
);
|
||||
}
|
||||
CliSession::HumanSession | CliSession::Anonymous => {
|
||||
map.insert("actor_user".to_owned(), json!(fallback_user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn attach_plan(args: AttachArgs) -> NodeAttachPlan {
|
||||
let mut capabilities = NodeCapabilities::detect_current();
|
||||
for cap in &args.caps {
|
||||
|
|
@ -3989,6 +4019,7 @@ mod tests {
|
|||
args,
|
||||
PathBuf::from("/repo"),
|
||||
CliSession::AgentPublicKey {
|
||||
agent: "agent-ci".to_owned(),
|
||||
public_key_fingerprint: Digest::sha256("agent-key"),
|
||||
browser_interaction_required: false,
|
||||
},
|
||||
|
|
@ -4003,12 +4034,83 @@ mod tests {
|
|||
assert_eq!(
|
||||
plan.session,
|
||||
CliSession::AgentPublicKey {
|
||||
agent: "agent-ci".to_owned(),
|
||||
public_key_fingerprint: Digest::sha256("agent-key"),
|
||||
browser_interaction_required: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_with_agent_public_key_sends_attributable_workflow_actor() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
write_project_config(
|
||||
temp.path(),
|
||||
&ProjectConfig {
|
||||
tenant: "tenant-live".to_owned(),
|
||||
project: "project-live".to_owned(),
|
||||
user: "user-live".to_owned(),
|
||||
coordinator: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let fingerprint = Digest::sha256("agent-key");
|
||||
let expected_fingerprint = fingerprint.to_string();
|
||||
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 (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(r#""type":"start_process""#));
|
||||
assert!(line.contains(r#""tenant":"tenant-live""#));
|
||||
assert!(line.contains(r#""project":"project-live""#));
|
||||
assert!(line.contains(r#""actor_agent":"agent-ci""#));
|
||||
assert!(line.contains(&format!(
|
||||
r#""agent_public_key_fingerprint":"{expected_fingerprint}""#
|
||||
)));
|
||||
assert!(!line.contains(r#""actor_user""#));
|
||||
stream
|
||||
.write_all(
|
||||
format!(
|
||||
r#"{{"type":"process_started","process":"vp-current","epoch":7,"actor":{{"kind":"agent","user":"user-live","agent":"agent-ci","credential_kind":"public_key","public_key_fingerprint":"{expected_fingerprint}","authenticated_without_browser":true,"scopes":["project:read","project:run"]}}}}"#
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
stream.write_all(b"\n").unwrap();
|
||||
});
|
||||
|
||||
let report = run_report(
|
||||
RunArgs {
|
||||
entry: Some("build".to_owned()),
|
||||
project: Some(temp.path().to_path_buf()),
|
||||
coordinator: Some(format!("http://{addr}")),
|
||||
local: false,
|
||||
json: false,
|
||||
},
|
||||
PathBuf::from("/unused"),
|
||||
CliSession::AgentPublicKey {
|
||||
agent: "agent-ci".to_owned(),
|
||||
public_key_fingerprint: fingerprint,
|
||||
browser_interaction_required: false,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
server.join().unwrap();
|
||||
|
||||
assert_eq!(report["status"], "started");
|
||||
assert_eq!(report["workflow_actor"]["kind"], "agent");
|
||||
assert_eq!(report["workflow_actor"]["agent"], "agent-ci");
|
||||
assert_eq!(
|
||||
report["workflow_actor"]["authenticated_without_browser"],
|
||||
true
|
||||
);
|
||||
assert_eq!(report["run_start"]["actor"]["kind"], "agent");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_local_flag_overrides_logged_in_hosted_selection() {
|
||||
let Cli {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue