use clap::Args;
use crate::{
cli::commands::release::{format, OutputFormat},
kubernetes::{KubeRsClient, KubernetesReleaseStorage, ReleaseStorage},
NylError, Result,
};
#[derive(Args, Debug)]
pub struct ShowArgs {
pub name: String,
#[arg(short, long)]
pub namespace: String,
#[arg(short, long)]
pub revision: Option<u32>,
#[arg(short, long)]
pub manifest: bool,
#[arg(short, long, default_value = "table")]
pub output: OutputFormat,
#[arg(long)]
pub context: Option<String>,
}
#[allow(clippy::too_many_lines)]
pub async fn execute(args: ShowArgs) -> Result<()> {
let config = KubeRsClient::load_kube_config(None, args.context.as_deref()).await?;
let client = kube::Client::try_from(config)?;
let storage = KubernetesReleaseStorage::new(client);
let release: Option<crate::kubernetes::ReleaseState> = if let Some(revision) = args.revision {
storage.get_release(&args.name, &args.namespace, revision).await?
} else {
storage.get_latest_release(&args.name, &args.namespace).await?
};
let release = release.ok_or_else(|| {
NylError::Config(format!(
"Release '{}' not found in namespace '{}'{}",
args.name,
args.namespace,
args.revision.map_or(String::new(), |r| format!(" (revision {})", r))
))
})?;
let revisions: Vec<u32> = storage.list_revisions(&args.name, &args.namespace).await?;
match args.output {
OutputFormat::Table => {
println!("Release: {}", release.release_name);
println!("Namespace: {}", release.release_namespace);
println!("Revision: {}", release.revision);
println!("Status: {:?}", release.status);
println!();
println!("Timestamps:");
println!(" Rendered: {}", format::format_timestamp(&release.rendered_at));
if let Some(applied_at) = &release.applied_at {
println!(" Applied: {}", format::format_timestamp(applied_at));
}
println!();
if let Some(error) = &release.error {
println!("Error: {}", error);
println!();
}
println!("Resources ({}):", release.resource_keys.len());
for key in &release.resource_keys {
match key.namespace.as_deref() {
Some(namespace) => {
println!(" - {} {}/{}", key.gvk.kind, namespace, key.name);
}
None => {
println!(" - {} <cluster>/{}", key.gvk.kind, key.name);
}
}
}
println!();
if revisions.len() > 1 {
let other_revisions: Vec<u32> = revisions.into_iter().filter(|r| *r != release.revision).collect();
if !other_revisions.is_empty() {
println!(
"Previous Revisions: {}",
other_revisions
.iter()
.map(|r| r.to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
}
if args.manifest {
println!();
println!("Manifest:");
println!("---");
print!("{}", release.manifest);
if !release.manifest.ends_with('\n') {
println!();
}
}
}
OutputFormat::Json => {
let json = if args.manifest {
serde_json::to_string_pretty(&release)?
} else {
let mut value = serde_json::to_value(&release)?;
if let Some(obj) = value.as_object_mut() {
obj.remove("manifest");
}
serde_json::to_string_pretty(&value)?
};
println!("{}", json);
}
OutputFormat::Yaml => {
if args.manifest {
let yaml = serde_norway::to_string(&release)?;
print!("{}", yaml);
} else {
let mut value = serde_json::to_value(&release)?;
if let Some(obj) = value.as_object_mut() {
obj.remove("manifest");
}
let yaml = serde_norway::to_string(&value)?;
print!("{}", yaml);
}
}
OutputFormat::Wide => {
let mut new_args = args;
new_args.output = OutputFormat::Table;
println!("Release: {}", release.release_name);
println!("Namespace: {}", release.release_namespace);
println!("Revision: {}", release.revision);
println!("Status: {:?}", release.status);
println!();
println!("Timestamps:");
println!(" Rendered: {}", format::format_timestamp(&release.rendered_at));
if let Some(applied_at) = &release.applied_at {
println!(" Applied: {}", format::format_timestamp(applied_at));
}
println!();
if let Some(error) = &release.error {
println!("Error: {}", error);
println!();
}
println!("Resources ({}):", release.resource_keys.len());
for key in &release.resource_keys {
match key.namespace.as_deref() {
Some(namespace) => {
println!(" - {} {}/{}", key.gvk.kind, namespace, key.name);
}
None => {
println!(" - {} <cluster>/{}", key.gvk.kind, key.name);
}
}
}
println!();
if revisions.len() > 1 {
let other_revisions: Vec<u32> = revisions.into_iter().filter(|r| *r != release.revision).collect();
if !other_revisions.is_empty() {
println!(
"Previous Revisions: {}",
other_revisions
.iter()
.map(|r: &u32| r.to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
}
if new_args.manifest {
println!();
println!("Manifest:");
println!("---");
print!("{}", release.manifest);
if !release.manifest.ends_with('\n') {
println!();
}
}
}
}
Ok(())
}