Skip to content

Commit 58154eb

Browse files
Dym03spirali
authored andcommitted
Add reason to cancelation of a job
1 parent 3be98e8 commit 58154eb

File tree

17 files changed

+160
-29
lines changed

17 files changed

+160
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
the operation is immediately a part of the written journal.
3333
* `hq worker info` can now be used with a selector, to display information about multiple workers at once.
3434
* Note that this is a breaking change for the JSON format, as it now outputs the worker infos as an array of objects. Before it was a single object.
35+
* `hq job cancel` now adds new possibility to add a reason to the cancelation of the job using `--reason <cancel-reason>`
36+
* This is connected with this information added in `hq job list --verbose` and `hq job info`
3537

3638
### Changes
3739

crates/hyperqueue/src/bin/hq.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ async fn command_job_list(gsettings: &GlobalSettings, opts: JobListOpts) -> anyh
9999
(opts.filter, false)
100100
};
101101

102-
output_job_list(gsettings, &mut connection, filter, show_open).await
102+
output_job_list(gsettings, &mut connection, filter, show_open, opts.verbose).await
103103
}
104104

105105
async fn command_job_summary(gsettings: &GlobalSettings) -> anyhow::Result<()> {
@@ -128,7 +128,7 @@ async fn command_job_cat(gsettings: &GlobalSettings, opts: JobCatOpts) -> anyhow
128128

129129
async fn command_job_cancel(gsettings: &GlobalSettings, opts: JobCancelOpts) -> anyhow::Result<()> {
130130
let mut connection = get_client_session(gsettings.server_directory()).await?;
131-
cancel_job(gsettings, &mut connection, opts.selector).await
131+
cancel_job(gsettings, &mut connection, opts.selector, opts.reason).await
132132
}
133133

134134
async fn command_job_close(gsettings: &GlobalSettings, opts: JobCloseOpts) -> anyhow::Result<()> {

crates/hyperqueue/src/client/commands/job.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use clap::Parser;
22

33
use crate::client::globalsettings::GlobalSettings;
44
use crate::client::job::get_worker_map;
5+
use crate::client::output::cli::CANCEL_REASON_MAX_LEN;
56
use crate::client::output::outputs::OutputStream;
67
use crate::client::output::resolve_task_paths;
78
use crate::client::status::{Status, job_status};
@@ -26,6 +27,10 @@ pub struct JobListOpts {
2627
/// You can use multiple states separated by a comma.
2728
#[arg(long, value_delimiter(','), value_enum)]
2829
pub filter: Vec<Status>,
30+
31+
/// Display additional information <Cancel Reason>
32+
#[arg(long)]
33+
pub verbose: bool,
2934
}
3035

3136
#[derive(Parser)]
@@ -35,11 +40,26 @@ pub struct JobInfoOpts {
3540
pub selector: IdSelector,
3641
}
3742

43+
fn check_max_reason_len(s: &str) -> Result<String, String> {
44+
if s.len() <= CANCEL_REASON_MAX_LEN {
45+
Ok(s.to_string())
46+
} else {
47+
Err(format!(
48+
"Cancel reason must be shorter than {} you entered {}.",
49+
CANCEL_REASON_MAX_LEN,
50+
s.len()
51+
))
52+
}
53+
}
54+
3855
#[derive(Parser)]
3956
pub struct JobCancelOpts {
4057
/// Select job(s) to cancel
4158
#[arg(value_parser = parse_last_all_range)]
4259
pub selector: IdSelector,
60+
/// Reason for the cancelation
61+
#[arg(long, value_parser = check_max_reason_len)]
62+
pub reason: Option<String>,
4363
}
4464

4565
#[derive(Parser)]
@@ -123,6 +143,7 @@ pub async fn output_job_list(
123143
session: &mut ClientSession,
124144
job_filters: Vec<Status>,
125145
show_open: bool,
146+
verbose: bool,
126147
) -> anyhow::Result<()> {
127148
let message = FromClientMessage::JobInfo(
128149
JobInfoRequest {
@@ -138,12 +159,12 @@ pub async fn output_job_list(
138159
if !job_filters.is_empty() {
139160
response
140161
.jobs
141-
.retain(|j| (show_open && j.is_open) || job_filters.contains(&job_status(j)));
162+
.retain(|j| (show_open && j.is_open()) || job_filters.contains(&job_status(j)));
142163
}
143164
response.jobs.sort_unstable_by_key(|j| j.id);
144165
gsettings
145166
.printer()
146-
.print_job_list(response.jobs, total_count);
167+
.print_job_list(response.jobs, total_count, verbose);
147168
Ok(())
148169
}
149170

@@ -253,9 +274,11 @@ pub async fn cancel_job(
253274
_gsettings: &GlobalSettings,
254275
session: &mut ClientSession,
255276
selector: IdSelector,
277+
reason: Option<String>,
256278
) -> anyhow::Result<()> {
257279
let mut responses = rpc_call!(session.connection(), FromClientMessage::Cancel(CancelRequest {
258280
selector,
281+
reason,
259282
}), ToClientMessage::CancelJobResponse(r) => r)
260283
.await?;
261284
responses.sort_unstable_by_key(|x| x.0);

crates/hyperqueue/src/client/commands/wait.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub async fn wait_for_jobs(
5252
) -> anyhow::Result<()> {
5353
let mut unfinished_jobs = Set::new();
5454
for job in jobs {
55-
if !is_terminated(job) || (wait_for_close && job.is_open) {
55+
if !is_terminated(job) || (wait_for_close && job.is_open()) {
5656
unfinished_jobs.insert(job.id);
5757
}
5858
}

crates/hyperqueue/src/client/output/cli.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub const TASK_COLOR_INVALID: Colorization = Colorization::BrightRed;
5959
const TERMINAL_WIDTH: usize = 80;
6060
const ERROR_TRUNCATE_LENGTH_LIST: usize = 16;
6161
const ERROR_TRUNCATE_LENGTH_INFO: usize = 237;
62+
pub const CANCEL_REASON_MAX_LEN: usize = 200;
6263

6364
pub struct CliOutput {
6465
color_policy: ColorChoice,
@@ -475,15 +476,15 @@ impl Output for CliOutput {
475476
}
476477
}
477478

478-
fn print_job_list(&self, jobs: Vec<JobInfo>, total_jobs: usize) {
479+
fn print_job_list(&self, jobs: Vec<JobInfo>, total_jobs: usize, verbose: bool) {
479480
let job_count = jobs.len();
480481
let mut has_opened = false;
481482
let rows: Vec<_> = jobs
482483
.into_iter()
483484
.map(|t| {
484485
let status = status_to_cell(&job_status(&t));
485-
vec![
486-
if t.is_open {
486+
let mut row = vec![
487+
if t.is_open() {
487488
has_opened = true;
488489
format!("*{}", t.id).cell()
489490
} else {
@@ -493,16 +494,25 @@ impl Output for CliOutput {
493494
truncate_middle(&t.name, 50).cell(),
494495
status,
495496
t.n_tasks.cell(),
496-
]
497+
];
498+
if verbose {
499+
row.push(t.cancel_reason.unwrap_or_default().cell())
500+
}
501+
row
497502
})
498503
.collect();
499504

500-
let header = vec![
505+
let mut header = vec![
501506
"ID".cell().bold(true),
502507
"Name".cell().bold(true),
503508
"State".cell().bold(true),
504509
"Tasks".cell().bold(true),
505510
];
511+
512+
if verbose {
513+
header.push("Cancel Reason".cell().bold(true));
514+
}
515+
506516
self.print_horizontal_table(rows, header);
507517

508518
if has_opened {
@@ -560,8 +570,12 @@ impl Output for CliOutput {
560570
let state_label = "State".cell().bold(true);
561571
rows.push(vec![state_label, status]);
562572

573+
let cancel_reason_label = "Cancel Reason".cell().bold(true);
574+
let cancel_reason = info.cancel_reason.clone().unwrap_or_default().cell();
575+
rows.push(vec![cancel_reason_label, cancel_reason]);
576+
563577
let state_label = "Session".cell().bold(true);
564-
rows.push(vec![state_label, session_to_cell(info.is_open)]);
578+
rows.push(vec![state_label, session_to_cell(info.is_open())]);
565579

566580
let mut n_tasks = info.n_tasks.to_string();
567581

crates/hyperqueue/src/client/output/json.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ impl Output for JsonOutput {
9696
}))
9797
}
9898

99-
fn print_job_list(&self, jobs: Vec<JobInfo>, _total_jobs: usize) {
99+
fn print_job_list(&self, jobs: Vec<JobInfo>, _total_jobs: usize, _verbose: bool) {
100+
// TODO! I think JSON should have all the information, so verbose shouldn't be used here
100101
self.print(
101102
jobs.into_iter()
102103
.map(|info| format_job_info(&info))
@@ -391,6 +392,7 @@ fn format_job_info(info: &JobInfo) -> Value {
391392
n_tasks,
392393
counters,
393394
is_open,
395+
cancel_reason,
394396
running_tasks: _,
395397
} = info;
396398

@@ -405,7 +407,8 @@ fn format_job_info(info: &JobInfo) -> Value {
405407
"failed": counters.n_failed_tasks,
406408
"canceled": counters.n_canceled_tasks,
407409
"waiting": counters.n_waiting_tasks(*n_tasks)
408-
})
410+
}),
411+
"cancel_reason": cancel_reason,
409412
})
410413
}
411414

crates/hyperqueue/src/client/output/outputs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub trait Output {
4545
fn print_job_submitted(&self, job: JobDetail);
4646

4747
fn print_job_open(&self, job_id: JobId);
48-
fn print_job_list(&self, jobs: Vec<JobInfo>, total_jobs: usize);
48+
fn print_job_list(&self, jobs: Vec<JobInfo>, total_jobs: usize, verbose: bool);
4949
fn print_job_summary(&self, jobs: Vec<JobInfo>);
5050
fn print_job_detail(&self, jobs: Vec<JobDetail>, worker_map: &WorkerMap, server_uid: &str);
5151
fn print_job_wait(

crates/hyperqueue/src/client/output/quiet.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ impl Output for Quiet {
7575
println!("{job_id}");
7676
}
7777

78-
fn print_job_list(&self, jobs: Vec<JobInfo>, _total_jobs: usize) {
78+
fn print_job_list(&self, jobs: Vec<JobInfo>, _total_jobs: usize, _verbose: bool) {
79+
// TODO! How to deal with verbose here -> Is it wanted or not. Quite maybe shoudn't use it
7980
for task in jobs {
8081
let status = job_status(&task);
8182
println!("{} {}", task.id, format_status(&status))

crates/hyperqueue/src/client/status.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn job_status(info: &JobInfo) -> Status {
2727
Status::Canceled
2828
} else {
2929
assert_eq!(info.counters.n_finished_tasks, info.n_tasks);
30-
if info.is_open {
30+
if info.is_open() {
3131
Status::Opened
3232
} else {
3333
Status::Finished

crates/hyperqueue/src/server/client/mod.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,9 @@ pub async fn client_rpc_loop<
271271
handle_worker_stop(&state_ref, senders, msg.selector)
272272
}
273273
FromClientMessage::Cancel(msg) => {
274-
let response = handle_job_cancel(&state_ref, senders, &msg.selector).await;
274+
let response =
275+
handle_job_cancel(&state_ref, senders, &msg.selector, &msg.reason)
276+
.await;
275277
if !response.is_error() {
276278
senders.events.flush_journal().await;
277279
};
@@ -664,6 +666,7 @@ async fn handle_job_cancel(
664666
state_ref: &StateRef,
665667
senders: &Senders,
666668
selector: &IdSelector,
669+
reason: &Option<String>,
667670
) -> ToClientMessage {
668671
let job_ids: Vec<JobId> = match selector {
669672
IdSelector::All => state_ref
@@ -679,7 +682,7 @@ async fn handle_job_cancel(
679682

680683
let mut responses: Vec<(JobId, CancelJobResponse)> = Vec::new();
681684
for job_id in job_ids {
682-
let response = cancel_job(state_ref, senders, job_id).await;
685+
let response = cancel_job(state_ref, senders, job_id, reason).await;
683686
responses.push((job_id, response));
684687
}
685688
ToClientMessage::CancelJobResponse(responses)
@@ -723,8 +726,13 @@ async fn handle_job_close(
723726
ToClientMessage::CloseJobResponse(responses)
724727
}
725728

726-
async fn cancel_job(state_ref: &StateRef, senders: &Senders, job_id: JobId) -> CancelJobResponse {
727-
let task_ids = match state_ref.get().get_job(job_id) {
729+
async fn cancel_job(
730+
state_ref: &StateRef,
731+
senders: &Senders,
732+
job_id: JobId,
733+
reason: &Option<String>,
734+
) -> CancelJobResponse {
735+
let task_ids = match state_ref.get_mut().get_job_mut(job_id) {
728736
None => {
729737
return CancelJobResponse::InvalidJob;
730738
}
@@ -747,6 +755,7 @@ async fn cancel_job(state_ref: &StateRef, senders: &Senders, job_id: JobId) -> C
747755
.map(|task_id| task_id.job_task_id())
748756
.collect();
749757
job.set_cancel_state(task_ids, senders);
758+
job.cancel(reason.clone());
750759
CancelJobResponse::Canceled(job_task_ids, already_finished)
751760
} else {
752761
CancelJobResponse::Canceled(vec![], 0)

0 commit comments

Comments
 (0)