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: beta deployment response, current deployment & logs #1782

Merged
merged 3 commits into from
May 23, 2024
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
2 changes: 1 addition & 1 deletion cargo-shuttle/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ pub enum DeploymentCommand {
/// View status of a deployment
Status {
/// ID of deployment to get status for
id: String,
id: Option<String>,
},
}

Expand Down
12 changes: 6 additions & 6 deletions cargo-shuttle/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl ShuttleApiClient {
&self,
project: &str,
deployment_req: DeploymentRequestBeta,
) -> Result<deployment::EcsResponse> {
) -> Result<deployment::ResponseBeta> {
let path = format!("/projects/{project}/deployments");
let deployment_req = rmp_serde::to_vec(&deployment_req)
.context("serialize DeploymentRequest as a MessagePack byte vector")?;
Expand Down Expand Up @@ -317,25 +317,25 @@ impl ShuttleApiClient {
pub async fn get_deployments_beta(
&self,
project: &str,
) -> Result<Vec<deployment::EcsResponse>> {
) -> Result<Vec<deployment::ResponseBeta>> {
let path = format!("/projects/{project}/deployments");

self.get(path).await
}
pub async fn _get_current_deployment_beta(
pub async fn get_current_deployment_beta(
&self,
project: &str,
) -> Result<deployment::EcsResponse> {
) -> Result<deployment::ResponseBeta> {
let path = format!("/projects/{project}/deployments/current");

self.get(path).await
}

pub async fn deployment_status(
pub async fn get_deployment_beta(
&self,
project: &str,
deployment_id: &str,
) -> Result<deployment::EcsResponse> {
) -> Result<deployment::ResponseBeta> {
let path = format!("/projects/{project}/deployments/{deployment_id}");

self.get(path).await
Expand Down
37 changes: 20 additions & 17 deletions cargo-shuttle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,11 @@ impl Shuttle {
if matches!(
args.cmd,
Command::Project(ProjectCommand::Stop { .. } | ProjectCommand::Restart { .. })
| Command::Status
) {
unimplemented!("This command is deprecated on the beta platform");
}
if matches!(args.cmd, Command::Stop | Command::Clean | Command::Status) {
if matches!(args.cmd, Command::Stop | Command::Clean) {
unimplemented!("This command is not yet implemented on the beta platform");
}
eprintln!("INFO: Using beta platform API");
Expand Down Expand Up @@ -220,9 +221,7 @@ impl Shuttle {
Command::Deployment(DeploymentCommand::List { page, limit, raw }) => {
self.deployments_list(page, limit, raw).await
}
Command::Deployment(DeploymentCommand::Status { id }) => {
self.deployment_get(id.as_str()).await
}
Command::Deployment(DeploymentCommand::Status { id }) => self.deployment_get(id).await,
Command::Resource(ResourceCommand::List { raw, show_secrets }) => {
self.resources_list(raw, show_secrets).await
}
Expand Down Expand Up @@ -855,13 +854,7 @@ impl Shuttle {
let id = if let Some(id) = args.id {
id
} else {
// TODO: fix the endpoint and use:
// let depl = client.get_current_deployment_beta(proj_name).await?;
let depls = client.get_deployments_beta(proj_name).await?;
let depl = depls
.first()
.expect("at least one deployment in this project");
depl.id.clone()
client.get_current_deployment_beta(proj_name).await?.id
};
client.get_deployment_logs_beta(proj_name, &id).await?.logs
};
Expand Down Expand Up @@ -1013,20 +1006,30 @@ impl Shuttle {
Ok(CommandOutcome::Ok)
}

async fn deployment_get(&self, deployment_id: &str) -> Result<CommandOutcome> {
async fn deployment_get(&self, deployment_id: Option<String>) -> Result<CommandOutcome> {
let client = self.client.as_ref().unwrap();

if self.beta {
let deployment = client
.deployment_status(self.ctx.project_name(), deployment_id)
.await
.map_err(suggestions::deployment::get_deployment_status_failure)?;
let deployment = match deployment_id {
Some(id) => {
client
.get_deployment_beta(self.ctx.project_name(), &id)
.await
}
None => {
client
.get_current_deployment_beta(self.ctx.project_name())
.await
}
}
.map_err(suggestions::deployment::get_deployment_status_failure)?;
deployment.colored_println();
} else {
let deployment_id = deployment_id.expect("deployment id required on alpha platform");
let deployment = client
.get_deployment_details(
self.ctx.project_name(),
&Uuid::from_str(deployment_id).map_err(|err| {
&Uuid::from_str(&deployment_id).map_err(|err| {
anyhow!("Provided deployment id is not a valid UUID: {err}")
})?,
)
Expand Down
2 changes: 2 additions & 0 deletions common/src/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub enum EcsState {
Stopped,
Stopping,
Failed,
/// Fallback
Unknown,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to introduce that ? When can it happen ?

Copy link
Member Author

@jonaro00 jonaro00 May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made it a fallback when we are parsing an ECS string response into the enum. We don't (yet) cover states that we don't expect to see.

}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
110 changes: 22 additions & 88 deletions common/src/models/deployment.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use chrono::{DateTime, Utc};
use chrono::{DateTime, Local, Utc};
use comfy_table::{
modifiers::UTF8_ROUND_CORNERS,
presets::{NOTHING, UTF8_BORDERS_ONLY, UTF8_FULL},
Expand Down Expand Up @@ -30,16 +30,13 @@ pub struct Response {
}

#[derive(Deserialize, Serialize)]
pub struct EcsResponse {
pub struct ResponseBeta {
pub id: String,
pub latest_deployment_state: EcsState,
pub running_id: Option<String>,
pub state: EcsState,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub uri: Option<String>,
pub git_commit_id: Option<String>,
pub git_commit_msg: Option<String>,
pub git_branch: Option<String>,
pub git_dirty: Option<bool>,
/// URIs where this deployment can currently be reached (only relevant for Running)
pub uris: Vec<String>,
}

impl Display for Response {
Expand All @@ -60,55 +57,22 @@ impl Display for Response {
}
}

impl EcsResponse {
impl ResponseBeta {
pub fn colored_println(&self) {
let running_deployment = self
.running_id
.as_ref()
.map(|id| {
format!(
"\nRunning deployment: '{}' - {} {}",
id,
"running".to_string().with(
crossterm::style::Color::from_str(EcsState::Running.get_color()).unwrap()
),
self.uri
.as_ref()
.map(|inner| format!("({inner})"))
.unwrap_or("".to_string())
)
})
.unwrap_or_default();

// Stringify the state.
let latest_state = format!(
let state = format!(
"{}",
self.latest_deployment_state
self.state
.to_string()
// Unwrap is safe because Color::from_str returns the color white if the argument is not a Color.
.with(
crossterm::style::Color::from_str(self.latest_deployment_state.get_color())
.unwrap()
)
.with(crossterm::style::Color::from_str(self.state.get_color()).unwrap())
);

let state_with_uri = match self.running_id {
None if EcsState::Running == self.latest_deployment_state
|| EcsState::InProgress == self.latest_deployment_state =>
{
let uri = self
.uri
.as_ref()
.map(|inner| format!(" ({inner})"))
.unwrap_or_default();
format!("{latest_state}{uri}")
}
_ => latest_state,
};

// TODO: make this look nicer
println!(
"Current deployment: '{}' - {}{running_deployment}",
self.id, state_with_uri
"Deployment {} - {}\n{}",
self.id.as_str().bold(),
state,
self.uris.join("\n"),
)
}
}
Expand Down Expand Up @@ -141,60 +105,30 @@ impl EcsState {
EcsState::Stopped => "dark_blue",
EcsState::Stopping => "blue",
EcsState::Failed => "red",
EcsState::Unknown => "grey",
}
}
}

pub fn deployments_table_beta(deployments: &[EcsResponse]) -> String {
pub fn deployments_table_beta(deployments: &[ResponseBeta]) -> String {
let mut table = Table::new();
table
.load_preset(UTF8_BORDERS_ONLY)
.set_content_arrangement(ContentArrangement::Disabled)
.set_header(vec![
Cell::new("Deployment ID"),
Cell::new("Status"),
Cell::new("Last updated"),
Cell::new("Branch"),
Cell::new("Commit"),
Cell::new("Date"),
]);

for deploy in deployments.iter() {
let truncated_commit_id = deploy
.git_commit_id
.as_ref()
.map_or(String::from(GIT_OPTION_NONE_TEXT), |val| {
val.chars().take(7).collect()
});

let truncated_commit_msg = deploy
.git_commit_msg
.as_ref()
.map_or(String::from(GIT_OPTION_NONE_TEXT), |val| {
val.chars().take(24).collect()
});

let datetime: DateTime<Local> = DateTime::from(deploy.created_at);
table.add_row(vec![
Cell::new(&deploy.id).add_attribute(Attribute::Bold),
Cell::new(&deploy.latest_deployment_state)
Cell::new(&deploy.state)
// Unwrap is safe because Color::from_str returns the color white if str is not a Color.
.fg(Color::from_str(deploy.latest_deployment_state.get_color()).unwrap()),
Cell::new(deploy.updated_at.format("%Y-%m-%dT%H:%M:%SZ")),
Cell::new(
deploy
.git_branch
.as_ref()
.unwrap_or(&GIT_OPTION_NONE_TEXT.to_owned()),
),
Cell::new(format!(
"{}{} {}",
truncated_commit_id,
if deploy.git_dirty.is_some_and(|d| d) {
"*"
} else {
""
},
truncated_commit_msg,
)),
.fg(Color::from_str(deploy.state.get_color()).unwrap()),
Cell::new(datetime.to_rfc3339_opts(chrono::SecondsFormat::Secs, false)),
]);
}

Expand Down