Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/cli/cmd/job/logs.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use super::Context;
use crate::cli::complete::complete_job_name;
use crate::{cli::sink::Error as SinkError, httpclient};
use clap::{Parser, ValueHint};
use std::time::Duration;
use tokio::signal;
use tokio::time::sleep;

use clap::{Parser, ValueHint};

use clap_complete::ArgValueCompleter;
use snafu::{ResultExt, Snafu};

/// Listing logs of a jobs.
///
/// List the logs of a job.
#[derive(Parser, Debug)]
pub struct Input {
/// The job name/id to get logs for.
#[arg(value_hint=ValueHint::Other)]
#[arg(value_hint=ValueHint::Other, add = ArgValueCompleter::new(complete_job_name))]
pub job_id: String,

/// Periodically retrieves logs, it will stop when the job finished.
Expand Down
5 changes: 3 additions & 2 deletions src/cli/cmd/job/stop.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::{data::simple_message::SimpleMessage, httpclient};
use crate::{cli::complete::complete_job_name, data::simple_message::SimpleMessage, httpclient};

use super::Context;
use crate::cli::sink::Error as SinkError;

use clap::{Parser, ValueHint};

use clap_complete::ArgValueCompleter;
use snafu::{ResultExt, Snafu};

/// Stop a job.
Expand All @@ -13,7 +14,7 @@ use snafu::{ResultExt, Snafu};
#[derive(Parser, Debug)]
pub struct Input {
/// The launcher to use for launching the job.
#[arg(value_hint=ValueHint::Other)]
#[arg(value_hint=ValueHint::Other, add = ArgValueCompleter::new(complete_job_name))]
pub job_id: String,
}

Expand Down
64 changes: 63 additions & 1 deletion src/cli/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
data::project_id::ProjectId,
httpclient::{
Client,
data::{SessionLauncher, SessionMode},
data::{SessionLauncher, SessionMode, SessionStartResponse},
},
};

Expand Down Expand Up @@ -78,6 +78,35 @@ async fn make_launcher_completion_candidate(
cc.help(Some(help))
}

async fn make_job_name_completion_candidate(
client: &Client,
session: &SessionStartResponse,
) -> CompletionCandidate {
let mut help = StyledStr::new();
let cc = CompletionCandidate::new(session.name.clone());

if let Ok(Some(launcher)) = client.get_launcher(&session.launcher_id).await {
help.push_str(&launcher.name);
help.push_str("/");
help.push_str(session.status.state.to_str());
} else {
help.push_str(&session.launcher_id);
help.push_str("/");
help.push_str(session.status.state.to_str());
eprintln!("Cannot get launcher for {}", &session.launcher_id);
}

let Ok(Some(project)) = client.get_project_by_id(&session.project_id).await else {
eprintln!("Cannot get project details for: {}", session.project_id);
return cc.help(Some(help));
};

help.push_str(" - ");
help.push_str(&project.name);

cc.help(Some(help))
}

async fn resolve_project_id(client: &Client, id: ProjectId) -> Option<String> {
match client.get_project(&id).await {
Ok(Some(p)) => Some(p.id),
Expand Down Expand Up @@ -128,3 +157,36 @@ pub fn complete_job_launcher_id(current: &ffi::OsStr) -> Vec<CompletionCandidate
result
})
}

/// Complete a job name
pub fn complete_job_name(current: &ffi::OsStr) -> Vec<CompletionCandidate> {
make_sync_completer(current, async |client, opts| {
let jobs = match client
.list_sessions(Some(SessionMode::NonInteractive))
.await
{
Err(msg) => {
eprintln!("Completions failed: Error getting list of jobs: {}", msg);
return vec![];
}
Ok(res) => res,
};
let mut result: Vec<CompletionCandidate> = vec![];
let project_ctx = opts.get_project_context().ok().flatten();
let project_id = match project_ctx {
Some(id) => resolve_project_id(&client, id).await,
None => None,
};
for job in jobs.0.iter().filter(|e| match &project_id {
Some(id) => id == &e.project_id,
None => true,
}) {
let cc = make_job_name_completion_candidate(&client, job).await;
result.push(cc);
}
if result.is_empty() {
eprintln!("No job launchers found.");
}
result
})
}
5 changes: 5 additions & 0 deletions src/httpclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ impl Client {
Ok(result)
}

pub async fn get_launcher(&self, id: &str) -> Result<Option<SessionLauncher>, Error> {
let path = format!("/api/data/session_launchers/{}", id);
self.json_get_option::<SessionLauncher>(&path).await
}

pub async fn clear_token(&self) -> Result<(), Error> {
self.keystore.clear_async().await.context(KeystoreSnafu)
}
Expand Down
14 changes: 10 additions & 4 deletions src/httpclient/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,22 @@ pub enum SessionState {
Succeeded,
}

impl fmt::Display for SessionState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
impl SessionState {
pub fn to_str(&self) -> &'static str {
match self {
SessionState::Running => "Running",
SessionState::Starting => "Starting",
SessionState::Stopping => "Stopping",
SessionState::Failed => "Failed",
SessionState::Hibernated => "Hibernated",
SessionState::Succeeded => "Succeeded",
};
}
}
}

impl fmt::Display for SessionState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = self.to_str();
f.write_str(name)
}
}
Expand Down
Loading