use anyhow::{Context, anyhow};
use clap::Args as ClapArgs;
use serde::Serialize;
use std::fmt::{self, Display, Formatter};
use turnkey_client::generated::GetAppStatusRequest;
use turnkey_client::generated::external::data::v1::{AppStatus, DeploymentStatus};
use crate::client::fetch_tvc_app;
use crate::commands::app_status::{
ReplicaCounts, TimestampPayload, format_replica_counts, sanitize_app_status,
};
use crate::commands::display::format_egress_enabled;
use crate::outcome::Outcome;
use crate::output::StdCtx;
#[derive(Debug, ClapArgs)]
#[command(about, long_about = None)]
pub struct Args {
#[arg(short, long, env = "TVC_APP_ID")]
pub app_id: String,
}
pub async fn run(_ctx: &mut StdCtx, args: Args) -> anyhow::Result<Outcome> {
let auth = crate::client::build_client().await?;
let request = GetAppStatusRequest {
organization_id: auth.org_id.clone(),
app_id: args.app_id.clone(),
};
let response = auth
.client
.get_app_status(request)
.await
.context("failed to fetch app status")?;
let app_status = sanitize_app_status(
response
.app_status
.ok_or_else(|| anyhow!("no status returned for app: {}", args.app_id))?,
);
let app = fetch_tvc_app(&auth, &args.app_id).await?;
let AppStatus {
app_id,
deployments,
targeted_deployment_id,
} = app_status;
Ok(Outcome::AppStatus(AppStatusReport {
app_id,
targeted_deployment_id,
egress_enabled: app.enable_egress,
deployments: deployments
.into_iter()
.map(|deployment| {
let DeploymentStatus {
deployment_id,
ready_replicas,
desired_replicas,
last_updated_time,
} = deployment;
DeploymentReplicaStatus {
deployment_id,
replicas: ReplicaCounts {
ready: ready_replicas,
desired: desired_replicas,
},
last_updated: last_updated_time.map(Into::into),
}
})
.collect(),
}))
}
#[derive(Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AppStatusReport {
app_id: String,
targeted_deployment_id: String,
egress_enabled: bool,
deployments: Vec<DeploymentReplicaStatus>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct DeploymentReplicaStatus {
deployment_id: String,
replicas: ReplicaCounts,
last_updated: Option<TimestampPayload>,
}
impl Display for AppStatusReport {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
r#"App ID: {}
Targeted Deployment: {}
{}"#,
self.app_id,
self.targeted_deployment_id,
format_egress_enabled(self.egress_enabled)
)?;
if self.deployments.is_empty() {
f.write_str("\n\nNo deployments found.")?;
} else {
for deployment in &self.deployments {
write!(
f,
r#"
Deployment: {}
{}"#,
deployment.deployment_id,
format_replica_counts(deployment.replicas.ready, deployment.replicas.desired)
)?;
if let Some(updated) = &deployment.last_updated {
write!(
f,
"\n Last Updated: {}.{:0>9}s",
updated.seconds, updated.nanos
)?;
}
}
}
Ok(())
}
}