From 62bf6e801210a144afad1a51553d05b9e01e07f0 Mon Sep 17 00:00:00 2001 From: jonaro00 <54029719+jonaro00@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:05:49 +0100 Subject: [PATCH] feat(cargo-shuttle): redeploy command (#1916) * feat: redeploy api call * wip: redeploy command * fix * feat: track deployment status on redeploy and image deploy --- api-client/src/lib.rs | 10 ++++ cargo-shuttle/src/args.rs | 6 +++ cargo-shuttle/src/lib.rs | 97 ++++++++++++++++++++++++++------------- 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/api-client/src/lib.rs b/api-client/src/lib.rs index 9c086fb39..2d5788471 100644 --- a/api-client/src/lib.rs +++ b/api-client/src/lib.rs @@ -170,6 +170,16 @@ impl ShuttleApiClient { .await } + pub async fn redeploy_beta( + &self, + project: &str, + deployment_id: &str, + ) -> Result { + let path = format!("/projects/{project}/deployments/{deployment_id}/redeploy"); + + self.post_json(path, Option::<()>::None).await + } + pub async fn stop_service(&self, project: &str) -> Result { let path = format!("/projects/{project}/services/{project}"); diff --git a/cargo-shuttle/src/args.rs b/cargo-shuttle/src/args.rs index 68551815d..1528140b4 100644 --- a/cargo-shuttle/src/args.rs +++ b/cargo-shuttle/src/args.rs @@ -189,6 +189,12 @@ pub enum DeploymentCommand { /// ID of deployment to get status for id: Option, }, + /// Redeploy a previous deployment (if possible) + #[command(visible_alias = "re")] + Redeploy { + /// ID of deployment to redeploy + id: String, + }, /// Stop running deployment(s) Stop, } diff --git a/cargo-shuttle/src/lib.rs b/cargo-shuttle/src/lib.rs index 0d80c69f1..0fe1146cb 100644 --- a/cargo-shuttle/src/lib.rs +++ b/cargo-shuttle/src/lib.rs @@ -189,6 +189,7 @@ impl Shuttle { } else if matches!( args.cmd, Command::Deployment(DeploymentCommand::Stop) + | Command::Deployment(DeploymentCommand::Redeploy { .. }) | Command::Account | Command::Project(ProjectCommand::Link) | Command::Project(ProjectCommand::Update(..)) @@ -334,6 +335,7 @@ impl Shuttle { self.deployments_list(page, limit, table).await } DeploymentCommand::Status { id } => self.deployment_get(id).await, + DeploymentCommand::Redeploy { id } => self.deployment_redeploy(id).await, DeploymentCommand::Stop => self.stop_beta().await, }, Command::Stop => self.stop().await, @@ -1412,6 +1414,19 @@ impl Shuttle { Ok(()) } + async fn deployment_redeploy(&self, deployment_id: String) -> Result<()> { + let client = self.client.as_ref().unwrap(); + + let pid = self.ctx.project_id(); + let deployment = client.redeploy_beta(pid, &deployment_id).await?; + + // TODO?: Make it print logs on fail + self.track_deployment_status_beta(pid, &deployment.id) + .await?; + + Ok(()) + } + async fn resources_list(&self, table_args: TableArgs, show_secrets: bool) -> Result<()> { let client = self.client.as_ref().unwrap(); let resources = client @@ -2425,16 +2440,17 @@ impl Shuttle { // Beta: Image deployment mode if self.beta { if let Some(image) = args.image { + let pid = self.ctx.project_id(); let deployment_req_image_beta = DeploymentRequestImageBeta { image, secrets }; let deployment = client - .deploy_beta( - self.ctx.project_id(), - DeploymentRequestBeta::Image(deployment_req_image_beta), - ) + .deploy_beta(pid, DeploymentRequestBeta::Image(deployment_req_image_beta)) + .await?; + + // TODO?: Make it print logs on fail + self.track_deployment_status_beta(pid, &deployment.id) .await?; - println!("{}", deployment.to_string_colored()); return Ok(()); } } @@ -2570,36 +2586,22 @@ impl Shuttle { return Ok(()); } - let id = &deployment.id; - wait_with_spinner(2000, |_, pb| async move { - let deployment = client.get_deployment_beta(pid, id).await?; - - let state = deployment.state.clone(); - pb.set_message(deployment.to_string_summary_colored()); - let cleanup = move || { - println!("{}", deployment.to_string_colored()); - }; - match state { - DeploymentStateBeta::Pending - | DeploymentStateBeta::Building - | DeploymentStateBeta::InProgress => Ok(None), - DeploymentStateBeta::Running => Ok(Some(cleanup)), - DeploymentStateBeta::Stopped - | DeploymentStateBeta::Stopping - | DeploymentStateBeta::Unknown => Ok(Some(cleanup)), - DeploymentStateBeta::Failed => { - for log in client.get_deployment_logs_beta(pid, id).await?.logs { - if args.raw { - println!("{}", log.line); - } else { - println!("{log}"); - } - } - Ok(Some(cleanup)) + if self + .track_deployment_status_beta(pid, &deployment.id) + .await? + { + for log in client + .get_deployment_logs_beta(pid, &deployment.id) + .await? + .logs + { + if args.raw { + println!("{}", log.line); + } else { + println!("{log}"); } } - }) - .await?; + } return Ok(()); } @@ -2785,6 +2787,35 @@ impl Shuttle { Ok(()) } + /// Returns true if the deployment failed + async fn track_deployment_status_beta(&self, pid: &str, id: &str) -> Result { + let client = self.client.as_ref().unwrap(); + let failed = wait_with_spinner(2000, |_, pb| async move { + let deployment = client.get_deployment_beta(pid, id).await?; + + let state = deployment.state.clone(); + pb.set_message(deployment.to_string_summary_colored()); + let failed = state == DeploymentStateBeta::Failed; + let cleanup = move || { + println!("{}", deployment.to_string_colored()); + failed + }; + match state { + DeploymentStateBeta::Pending + | DeploymentStateBeta::Building + | DeploymentStateBeta::InProgress => Ok(None), + DeploymentStateBeta::Running + | DeploymentStateBeta::Stopped + | DeploymentStateBeta::Stopping + | DeploymentStateBeta::Unknown + | DeploymentStateBeta::Failed => Ok(Some(cleanup)), + } + }) + .await?; + + Ok(failed) + } + async fn project_start(&self, idle_minutes: u64) -> Result<()> { let client = self.client.as_ref().unwrap(); let config = &project::Config { idle_minutes };