Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cargo-shuttle): add suggestions in case of cmd failures #1245

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
139 changes: 119 additions & 20 deletions cargo-shuttle/src/lib.rs
Expand Up @@ -3,6 +3,7 @@ mod client;
mod config;
mod init;
mod provisioner_server;
mod suggestions;

use std::collections::{BTreeMap, HashMap};
use std::ffi::OsString;
Expand Down Expand Up @@ -395,7 +396,9 @@ impl Shuttle {

async fn logout(&mut self, logout_args: LogoutArgs) -> Result<()> {
if logout_args.reset_api_key {
self.reset_api_key(&self.client()?).await?;
self.reset_api_key(&self.client()?)
.await
.map_err(suggestions::api_key::reset_api_key_failed)?;
println!("Successfully reset the API key.");
println!(" -> Go to {SHUTTLE_LOGIN_URL} to get a new one.\n");
}
Expand All @@ -417,7 +420,10 @@ impl Shuttle {

async fn stop(&self, client: &Client) -> Result<()> {
let proj_name = self.ctx.project_name();
let mut service = client.stop_service(proj_name).await?;
let mut service = client
.stop_service(proj_name)
.await
.map_err(suggestions::deployment::stop_deployment_failure)?;

let progress_bar = create_spinner();
loop {
Expand Down Expand Up @@ -460,7 +466,10 @@ impl Shuttle {
}

async fn secrets(&self, client: &Client) -> Result<()> {
let secrets = client.get_secrets(self.ctx.project_name()).await?;
let secrets = client
.get_secrets(self.ctx.project_name())
.await
.map_err(suggestions::resources::get_secrets_failure)?;
let table = secret::get_table(&secrets);

println!("{table}");
Expand All @@ -469,7 +478,17 @@ impl Shuttle {
}

async fn clean(&self, client: &Client) -> Result<()> {
let lines = client.clean_project(self.ctx.project_name()).await?;
let lines = client
.clean_project(self.ctx.project_name())
.await
.map_err(|err| {
suggestions::project::project_request_failure(
err,
"Project clean failed",
true,
"cleaning your project or checking its status fail repeteadly",
)
})?;

for line in lines {
println!("{line}");
Expand All @@ -494,7 +513,15 @@ impl Shuttle {

if latest {
// Find latest deployment (not always an active one)
let deployments = client.get_deployments(proj_name, 0, 1).await?;
let deployments = client
.get_deployments(proj_name, 0, 1)
.await
.map_err(|err| {
suggestions::logs::get_logs_failure(
err,
"Fetching the latest deployment failed",
)
})?;
let most_recent = deployments.first().context(format!(
"Could not find any deployments for '{proj_name}'. Try passing a deployment ID manually",
))?;
Expand All @@ -511,7 +538,12 @@ impl Shuttle {
};

if follow {
let mut stream = client.get_logs_ws(self.ctx.project_name(), &id).await?;
let mut stream = client
.get_logs_ws(self.ctx.project_name(), &id)
.await
.map_err(|err| {
suggestions::logs::get_logs_failure(err, "Connecting to the logs stream failed")
})?;

while let Some(Ok(msg)) = stream.next().await {
if let tokio_tungstenite::tungstenite::Message::Text(line) = msg {
Expand All @@ -521,7 +553,12 @@ impl Shuttle {
}
}
} else {
let logs = client.get_logs(self.ctx.project_name(), &id).await?;
let logs = client
.get_logs(self.ctx.project_name(), &id)
.await
.map_err(|err| {
suggestions::logs::get_logs_failure(err, "Fetching the deployment failed")
})?;

for log in logs.into_iter() {
println!("{log}");
Expand All @@ -538,7 +575,10 @@ impl Shuttle {
}

let proj_name = self.ctx.project_name();
let deployments = client.get_deployments(proj_name, page, limit).await?;
let deployments = client
.get_deployments(proj_name, page, limit)
.await
.map_err(suggestions::deployment::get_deployments_list_failure)?;
let table = get_deployments_table(&deployments, proj_name.as_str(), page);

println!("{table}");
Expand All @@ -550,7 +590,8 @@ impl Shuttle {
async fn deployment_get(&self, client: &Client, deployment_id: Uuid) -> Result<()> {
let deployment = client
.get_deployment_details(self.ctx.project_name(), &deployment_id)
.await?;
.await
.map_err(suggestions::deployment::get_deployment_status_failure)?;

println!("{deployment}");

Expand All @@ -560,7 +601,8 @@ impl Shuttle {
async fn resources_list(&self, client: &Client) -> Result<()> {
let resources = client
.get_service_resources(self.ctx.project_name())
.await?;
.await
.map_err(suggestions::resources::get_service_resources_failure)?;
let table = get_resources_table(&resources, self.ctx.project_name().as_str());

println!("{table}");
Expand Down Expand Up @@ -1176,11 +1218,18 @@ impl Shuttle {

let deployment = client
.deploy(self.ctx.project_name(), deployment_req)
.await?;
.await
.map_err(suggestions::deploy::deploy_request_failure)?;

let mut stream = client
.get_logs_ws(self.ctx.project_name(), &deployment.id)
.await?;
.await
.map_err(|err| {
suggestions::deploy::deployment_setup_failure(
err,
"Connecting to the deployment logs failed",
)
})?;

loop {
let message = stream.next().await;
Expand Down Expand Up @@ -1219,7 +1268,13 @@ impl Shuttle {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
stream = client
.get_logs_ws(self.ctx.project_name(), &deployment.id)
.await?;
.await
.map_err(|err| {
suggestions::deploy::deployment_setup_failure(
err,
"Connecting to the deployment logs failed",
)
})?;
}
}

Expand All @@ -1230,7 +1285,13 @@ impl Shuttle {

let deployment = client
.get_deployment_details(self.ctx.project_name(), &deployment.id)
.await?;
.await
.map_err(|err| {
suggestions::deploy::deployment_setup_failure(
err,
"Assessing deployment state failed",
)
})?;

// A deployment will only exist if there is currently one in the running state
if deployment.state != shuttle_common::deployment::State::Running {
Expand Down Expand Up @@ -1306,15 +1367,28 @@ impl Shuttle {
self.ctx.project_name(),
client,
)
.await?;
.await
.map_err(|err| {
suggestions::project::project_request_failure(
err,
"Project creation failed",
true,
"the project creation or retrieving the status fails repeteadly",
)
})?;

println!("Run `cargo shuttle deploy --allow-dirty` to deploy your Shuttle service.");

Ok(())
}

async fn project_recreate(&self, client: &Client, idle_minutes: u64) -> Result<()> {
self.project_delete(client).await?;
self.project_create(client, idle_minutes).await?;
self.project_delete(client)
.await
.map_err(suggestions::project::project_restart_failure)?;
self.project_create(client, idle_minutes)
.await
.map_err(suggestions::project::project_restart_failure)?;

Ok(())
}
Expand All @@ -1325,7 +1399,14 @@ impl Shuttle {
return Ok(());
}

let projects = client.get_projects_list(page, limit).await?;
let projects = client.get_projects_list(page, limit).await.map_err(|err| {
suggestions::project::project_request_failure(
err,
"Getting projects list failed",
false,
"getting the projects list fails repeteadly",
)
})?;
let projects_table = project::get_table(&projects, page);

println!("{projects_table}");
Expand All @@ -1349,7 +1430,17 @@ impl Shuttle {
)
.await?;
} else {
let project = client.get_project(self.ctx.project_name()).await?;
let project = client
.get_project(self.ctx.project_name())
.await
.map_err(|err| {
suggestions::project::project_request_failure(
err,
"Getting project status failed",
false,
"getting project status failed repeteadly",
)
})?;
println!("{project}");
}

Expand All @@ -1368,7 +1459,15 @@ impl Shuttle {
self.ctx.project_name(),
client,
)
.await?;
.await
.map_err(|err| {
suggestions::project::project_request_failure(
err,
"Project destroy failed",
true,
"deleting the project or getting project status fails repeteadly",
)
})?;
println!("Run `cargo shuttle project start` to recreate project environment on Shuttle.");

Ok(())
Expand Down
19 changes: 19 additions & 0 deletions cargo-shuttle/src/suggestions/api_key.rs
@@ -0,0 +1,19 @@
//! Suggestions to be shown to users encountering errors while using cargo-shuttle.
// TODO: Ideally, the suggestions would be inferred from the status codes returned by
// the gateway in case of requests to it, or errors thrown by the client doing work
// on the users machines. This is a naive way of handling the errors that should suggest
// retrying common commands or reach out on our Discord server in case failures persist.

use crossterm::style::Stylize;

// --------------------------
// API key related

/// Used when logging out and resetting API key fails
pub fn reset_api_key_failed(err: anyhow::Error) -> anyhow::Error {
println!();
println!("{}", "Logging out failed".red());
println!();
println!("If trying to log out and reset the API key at the same time fails repeteadly, please check Shuttle status at https://status.shuttle.rs or open a help thread on the Discord server.");
err
}
52 changes: 52 additions & 0 deletions cargo-shuttle/src/suggestions/deploy.rs
@@ -0,0 +1,52 @@
use crossterm::style::Stylize;

/// Used when the deploy request doesn't succeed.
pub fn deploy_request_failure(err: anyhow::Error) -> anyhow::Error {
println!();
println!("{}", "Deploy request failed".red());
println!();
println!("Please check your project status and deployments:");
println!();
println!("1. cargo shuttle project status");
println!();
println!("2. cargo shuttle deployment list");
println!();
println!(
"If deploying fails repeteadly, please try restarting your project before deploying again or contacting the team on the Discord server:"
);
println!();
println!("cargo shuttle project restart");
err
}

/// Especially used for cases where the deployment fails after the
/// deploy request went through (e.g. following the deployment logs, checking
/// the deployment state).
pub fn deployment_setup_failure(err: anyhow::Error, title: &str) -> anyhow::Error {
println!();
println!("{}", title.dark_red());
println!();
println!(
"Please check your project status and if the last deployment is recent and is running:"
);
println!();
println!("1. cargo shuttle project status");
println!();
println!("2. cargo shuttle deployment list");
println!();
println!("You should be able to get the logs of the deployment by running:");
println!();
println!("cargo shuttle logs");
println!();
println!("Or follow the logs of the deployment by running:");
println!();
println!("cargo shuttle logs --follow");
println!("If the last deployment is not recent or is not running, please try deploying again or contacting the team on the Discord server:");
println!();
println!("cargo shuttle deploy");
println!();
println!("Or restart the project before deploying again:");
println!();
println!("cargo shuttle project restart");
err
}
52 changes: 52 additions & 0 deletions cargo-shuttle/src/suggestions/deployment.rs
@@ -0,0 +1,52 @@
use crossterm::style::Stylize;

/// Used in case of deployment list request failure.
pub fn get_deployments_list_failure(err: anyhow::Error) -> anyhow::Error {
println!();
println!("{}", "Fetching the deployments list failed".red());
println!();
println!("Please check your project status:");
println!();
println!("cargo shuttle project status");
println!(
"If getting the deployment list fails repeteadly, please try restarting your project before getting the deployment list again or contacting the team on the Discord server:"
);
println!();
println!("cargo shuttle project restart");
err
}

/// Used in case of deployment list request failures.
pub fn get_deployment_status_failure(err: anyhow::Error) -> anyhow::Error {
println!();
println!("{}", "Fetching the deployments status failed".red());
println!();
println!("Please check your project status:");
println!();
println!("cargo shuttle project status");
println!();
println!(
"If getting the deployment state fails repeteadly, please try restarting your project before getting the deployment status again or contacting the team on the Discord server:"
);
println!();
println!("cargo shuttle project restart");
err
}

pub fn stop_deployment_failure(err: anyhow::Error) -> anyhow::Error {
println!();
println!("{}", "Stopping the running deployment failed".red());
println!();
println!("Please check your project status and whether you have a running deployment:");
println!();
println!("1. cargo shuttle project status");
println!();
println!("2. cargo shuttle status");
println!();
println!(
"If stopping the running deployment repeteadly, please try restarting your project before stopping the deployment again or contacting the team on the Discord server:"
);
println!();
println!("cargo shuttle project restart");
err
}