Skip to content

Commit a17dfc6

Browse files
authored
add support for stopping jobs (#3)
* add support for stopping jobs * rename stop to cancel
1 parent 9f9d58d commit a17dfc6

8 files changed

Lines changed: 111 additions & 22 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coman/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ tera = "1.20.1"
6767
inquire = "0.9.1"
6868
oci-distribution = "0.11.0"
6969
docker_credential = "1.3.2"
70+
chrono = "0.4.42"
7071

7172
[build-dependencies]
7273
anyhow = "1.0.90"

coman/src/cli.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum CscsJobCommands {
4545
#[clap(short, long, trailing_var_arg = true)]
4646
command: Option<Vec<String>>,
4747
},
48+
#[clap(alias("c"))]
49+
Cancel { job_id: i64 },
4850
}
4951
#[derive(Subcommand, Debug)]
5052
pub enum CscsSystemCommands {

coman/src/cscs/api_client.rs

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use chrono::prelude::*;
12
use color_eyre::eyre::{Context, Result};
23
use firecrest_client::{
34
client::FirecrestClient,
45
compute_api::{
5-
get_compute_system_job, get_compute_system_job_metadata, get_compute_system_jobs,
6-
post_compute_system_job,
6+
cancel_compute_system_job, get_compute_system_job, get_compute_system_job_metadata,
7+
get_compute_system_jobs, post_compute_system_job,
78
},
89
filesystem_api::{
910
post_filesystem_ops_mkdir, post_filesystem_ops_upload, put_filesystem_ops_chmod,
@@ -80,27 +81,46 @@ pub enum JobStatus {
8081
Cancelled,
8182
Failed,
8283
}
84+
impl From<String> for JobStatus {
85+
fn from(value: String) -> Self {
86+
match value.as_str() {
87+
"RUNNING" => JobStatus::Running,
88+
"FAILED" => JobStatus::Failed,
89+
"COMPLETED" => JobStatus::Finished,
90+
"CANCELLED" => JobStatus::Cancelled,
91+
"PENDING" => JobStatus::Pending,
92+
other => panic!("got job status: {}", other),
93+
}
94+
}
95+
}
8396
#[derive(Debug, Eq, Clone, PartialEq, PartialOrd, Ord, tabled::Tabled)]
8497
pub struct Job {
8598
pub id: usize,
8699
pub name: String,
87100
pub status: JobStatus,
88101
pub user: String,
102+
#[tabled(display("display_option_datetime"))]
103+
pub start_date: Option<DateTime<Local>>,
104+
#[tabled(display("display_option_datetime"))]
105+
pub end_date: Option<DateTime<Local>>,
89106
}
90107
impl From<JobModelOutput> for Job {
91108
fn from(value: JobModelOutput) -> Self {
92109
Self {
93110
id: value.job_id as usize,
94111
name: value.name,
95-
status: match value.status.state.as_str() {
96-
"RUNNING" => JobStatus::Running,
97-
"FAILED" => JobStatus::Failed,
98-
"COMPLETED" => JobStatus::Finished,
99-
"CANCELLED" => JobStatus::Cancelled,
100-
"PENDING" => JobStatus::Pending,
101-
other => panic!("got job status: {}", other),
102-
},
112+
status: value.status.state.into(),
103113
user: value.user.unwrap_or("".to_string()),
114+
start_date: value.time.start.map(|s| {
115+
DateTime::from_timestamp_secs(s)
116+
.unwrap()
117+
.with_timezone(&Local)
118+
}),
119+
end_date: value.time.end.map(|e| {
120+
DateTime::from_timestamp_secs(e)
121+
.unwrap()
122+
.with_timezone(&Local)
123+
}),
104124
}
105125
}
106126
}
@@ -109,6 +129,10 @@ impl From<JobModelOutput> for Job {
109129
pub struct JobDetail {
110130
pub id: usize,
111131
pub name: String,
132+
#[tabled(display("display_option_datetime"))]
133+
pub start_date: Option<DateTime<Local>>,
134+
#[tabled(display("display_option_datetime"))]
135+
pub end_date: Option<DateTime<Local>>,
112136
pub status: JobStatus,
113137
pub status_reason: String,
114138
pub exit_code: i64,
@@ -117,18 +141,28 @@ pub struct JobDetail {
117141
pub stderr: String,
118142
pub stdin: String,
119143
}
144+
fn display_option_datetime(value: &Option<DateTime<Local>>) -> String {
145+
match value {
146+
Some(dt) => dt.to_string(),
147+
None => "".to_owned(),
148+
}
149+
}
120150
impl From<(JobModelOutput, JobMetadataModel)> for JobDetail {
121151
fn from(value: (JobModelOutput, JobMetadataModel)) -> Self {
122152
Self {
123153
id: value.0.job_id as usize,
124154
name: value.0.name,
125-
status: match value.0.status.state.as_str() {
126-
"RUNNING" => JobStatus::Running,
127-
"FAILED" => JobStatus::Failed,
128-
"COMPLETED" => JobStatus::Finished,
129-
"CANCELLED" => JobStatus::Cancelled,
130-
other => panic!("got job status: {}", other),
131-
},
155+
start_date: value.0.time.start.map(|s| {
156+
DateTime::from_timestamp_secs(s)
157+
.unwrap()
158+
.with_timezone(&Local)
159+
}),
160+
end_date: value.0.time.end.map(|e| {
161+
DateTime::from_timestamp_secs(e)
162+
.unwrap()
163+
.with_timezone(&Local)
164+
}),
165+
status: value.0.status.state.into(),
132166
status_reason: value.0.status.state_reason.unwrap_or("".to_owned()),
133167
exit_code: value.0.status.exit_code.unwrap_or(0),
134168
user: value.0.user.unwrap_or("".to_string()),
@@ -288,6 +322,13 @@ impl CscsApi {
288322
Ok(Some((job, job_metadata).into()))
289323
}
290324

325+
pub async fn cancel_job(&self, system_name: &str, job_id: i64) -> Result<()> {
326+
cancel_compute_system_job(&self.client, system_name, job_id)
327+
.await
328+
.wrap_err("couldn't delete job")?;
329+
Ok(())
330+
}
331+
291332
pub async fn mkdir(&self, system_name: &str, path: PathBuf) -> Result<()> {
292333
let _ = post_filesystem_ops_mkdir(&self.client, system_name, path)
293334
.await

coman/src/cscs/cli.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ use std::path::PathBuf;
44

55
use crate::{
66
cscs::{
7-
handlers::{cscs_job_details, cscs_job_list, cscs_start_job, cscs_system_list},
8-
oauth2::{
9-
CLIENT_ID_SECRET_NAME, CLIENT_SECRET_SECRET_NAME, client_credentials_login,
7+
handlers::{
8+
cscs_job_cancel, cscs_job_details, cscs_job_list, cscs_start_job, cscs_system_list,
109
},
10+
oauth2::{CLIENT_ID_SECRET_NAME, CLIENT_SECRET_SECRET_NAME, client_credentials_login},
1111
},
1212
util::{
1313
keyring::{Secret, get_secret, store_secret},
@@ -60,6 +60,18 @@ pub(crate) async fn cli_cscs_job_detail(job_id: i64) -> Result<()> {
6060
let data = &[
6161
("Id", job.id.to_string()),
6262
("Name", job.name),
63+
(
64+
"Start Date",
65+
job.start_date
66+
.map(|dt| dt.to_string())
67+
.unwrap_or("".to_owned()),
68+
),
69+
(
70+
"End Date",
71+
job.end_date
72+
.map(|dt| dt.to_string())
73+
.unwrap_or("".to_owned()),
74+
),
6375
("Status", job.status.to_string()),
6476
("Status Reason", job.status_reason),
6577
("Exit Code", job.exit_code.to_string()),
@@ -85,6 +97,10 @@ pub(crate) async fn cli_cscs_job_start(
8597
cscs_start_job(script_file, image, command).await
8698
}
8799

100+
pub(crate) async fn cli_cscs_job_cancel(job_id: i64) -> Result<()> {
101+
cscs_job_cancel(job_id).await
102+
}
103+
88104
pub(crate) async fn cli_cscs_system_list() -> Result<()> {
89105
match cscs_system_list().await {
90106
Ok(systems) => {

coman/src/cscs/handlers.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ pub async fn cscs_job_details(job_id: i64) -> Result<Option<JobDetail>> {
8282
}
8383
}
8484

85+
pub async fn cscs_job_cancel(job_id: i64) -> Result<()> {
86+
match get_access_token().await {
87+
Ok(access_token) => {
88+
let api_client = CscsApi::new(access_token.0).unwrap();
89+
let config = Config::new().unwrap();
90+
api_client
91+
.cancel_job(&config.cscs.current_system, job_id)
92+
.await
93+
}
94+
Err(e) => Err(e),
95+
}
96+
}
97+
8598
pub async fn cscs_start_job(
8699
script_file: Option<PathBuf>,
87100
image: Option<DockerImageUrl>,

coman/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use crate::{
2222
components::{global_listener::GlobalListener, toolbar::Toolbar, workload_list::WorkloadList},
2323
cscs::{
2424
cli::{
25-
cli_cscs_job_detail, cli_cscs_job_list, cli_cscs_job_start, cli_cscs_login,
26-
cli_cscs_system_list,
25+
cli_cscs_job_cancel, cli_cscs_job_detail, cli_cscs_job_list, cli_cscs_job_start,
26+
cli_cscs_login, cli_cscs_system_list,
2727
},
2828
ports::{AsyncDeviceFlowPort, AsyncFetchWorkloadsPort},
2929
},
@@ -62,6 +62,7 @@ async fn main() -> Result<()> {
6262
image,
6363
command,
6464
} => cli_cscs_job_start(script_file, image, command).await?,
65+
cli::CscsJobCommands::Cancel { job_id } => cli_cscs_job_cancel(job_id).await?,
6566
},
6667
cli::CscsCommands::System { command } => match command {
6768
cli::CscsSystemCommands::List => cli_cscs_system_list().await?,

firecrest_client/src/compute_api.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,17 @@ pub async fn get_compute_system_job_metadata(
102102
let model: GetJobMetadataResponse = serde_json::from_str(response.as_str())?;
103103
Ok(model)
104104
}
105+
106+
pub async fn cancel_compute_system_job(
107+
client: &FirecrestClient,
108+
system_name: &str,
109+
job_id: i64,
110+
) -> Result<()> {
111+
let _ = client
112+
.delete(
113+
format!("compute/{system_name}/jobs/{job_id}").as_str(),
114+
None,
115+
)
116+
.await?;
117+
Ok(())
118+
}

0 commit comments

Comments
 (0)