|
1 | 1 | use std::collections::VecDeque;
|
| 2 | +use std::io::IsTerminal; |
| 3 | +use std::io::Write; |
2 | 4 |
|
3 | 5 | use anyhow::{Context, Result};
|
4 | 6 | use camino::Utf8Path;
|
@@ -305,19 +307,153 @@ pub(crate) async fn status(opts: super::cli::StatusOpts) -> Result<()> {
|
305 | 307 | let mut out = out.lock();
|
306 | 308 | let legacy_opt = if opts.json {
|
307 | 309 | OutputFormat::Json
|
| 310 | + } else if std::io::stdout().is_terminal() { |
| 311 | + OutputFormat::HumanReadable |
308 | 312 | } else {
|
309 | 313 | OutputFormat::Yaml
|
310 | 314 | };
|
311 | 315 | let format = opts.format.unwrap_or(legacy_opt);
|
312 | 316 | match format {
|
313 | 317 | OutputFormat::Json => serde_json::to_writer(&mut out, &host).map_err(anyhow::Error::new),
|
314 | 318 | OutputFormat::Yaml => serde_yaml::to_writer(&mut out, &host).map_err(anyhow::Error::new),
|
| 319 | + OutputFormat::HumanReadable => human_readable_output(&mut out, &host), |
315 | 320 | }
|
316 | 321 | .context("Writing to stdout")?;
|
317 | 322 |
|
318 | 323 | Ok(())
|
319 | 324 | }
|
320 | 325 |
|
| 326 | +fn human_readable_output(mut out: impl Write, host: &Host) -> Result<()> { |
| 327 | + for (status_string, status) in [ |
| 328 | + ("staged", &host.status.staged), |
| 329 | + ("booted", &host.status.booted), |
| 330 | + ("rollback", &host.status.rollback), |
| 331 | + ] { |
| 332 | + if let Some(host_status) = status { |
| 333 | + if let Some(image) = &host_status.image { |
| 334 | + writeln!( |
| 335 | + out, |
| 336 | + "Current {} image: {}", |
| 337 | + status_string, image.image.image |
| 338 | + )?; |
| 339 | + |
| 340 | + let version = image |
| 341 | + .version |
| 342 | + .as_deref() |
| 343 | + .unwrap_or("No image version defined"); |
| 344 | + let timestamp = image |
| 345 | + .timestamp |
| 346 | + .as_ref() |
| 347 | + .map(|t| t.to_string()) |
| 348 | + .unwrap_or_else(|| "No timestamp present".to_owned()); |
| 349 | + let transport = &image.image.transport; |
| 350 | + let digest = &image.image_digest; |
| 351 | + |
| 352 | + writeln!(out, " Image version: {version} ({timestamp})")?; |
| 353 | + writeln!(out, " Image transport: {transport}")?; |
| 354 | + writeln!(out, " Image digest: {digest}")?; |
| 355 | + } else { |
| 356 | + writeln!(out, "Current {status_string} state is native ostree")?; |
| 357 | + } |
| 358 | + } else { |
| 359 | + writeln!(out, "No {status_string} image present")?; |
| 360 | + } |
| 361 | + } |
| 362 | + Ok(()) |
| 363 | +} |
| 364 | + |
| 365 | +fn human_status_from_spec_fixture(spec_fixture: &str) -> Result<String> { |
| 366 | + let host: Host = serde_yaml::from_str(spec_fixture).unwrap(); |
| 367 | + let mut w = Vec::new(); |
| 368 | + human_readable_output(&mut w, &host).unwrap(); |
| 369 | + let w = String::from_utf8(w).unwrap(); |
| 370 | + Ok(w) |
| 371 | +} |
| 372 | + |
| 373 | +#[test] |
| 374 | +fn test_human_readable_base_spec() { |
| 375 | + // Tests Staged and Booted, null Rollback |
| 376 | + let w = human_status_from_spec_fixture(include_str!("fixtures/spec-staged-booted.yaml")) |
| 377 | + .expect("No spec found"); |
| 378 | + let expected = indoc::indoc! { r" |
| 379 | + Current staged image: quay.io/example/someimage:latest |
| 380 | + Image version: nightly (2023-10-14 19:22:15 UTC) |
| 381 | + Image transport: registry |
| 382 | + Image digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566 |
| 383 | + Current booted image: quay.io/example/someimage:latest |
| 384 | + Image version: nightly (2023-09-30 19:22:16 UTC) |
| 385 | + Image transport: registry |
| 386 | + Image digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34 |
| 387 | + No rollback image present |
| 388 | + "}; |
| 389 | + similar_asserts::assert_eq!(w, expected); |
| 390 | +} |
| 391 | + |
| 392 | +#[test] |
| 393 | +fn test_human_readable_rfe_spec() { |
| 394 | + // Basic rhel for edge bootc install with nothing |
| 395 | + let w = |
| 396 | + human_status_from_spec_fixture(include_str!("fixtures/spec-rfe-ostree-deployment.yaml")) |
| 397 | + .expect("No spec found"); |
| 398 | + let expected = indoc::indoc! { r" |
| 399 | + Current staged state is native ostree |
| 400 | + Current booted state is native ostree |
| 401 | + No rollback image present |
| 402 | + "}; |
| 403 | + similar_asserts::assert_eq!(w, expected); |
| 404 | +} |
| 405 | + |
| 406 | +#[test] |
| 407 | +fn test_human_readable_staged_spec() { |
| 408 | + // staged image, no boot/rollback |
| 409 | + let w = human_status_from_spec_fixture(include_str!("fixtures/spec-ostree-to-bootc.yaml")) |
| 410 | + .expect("No spec found"); |
| 411 | + let expected = indoc::indoc! { r" |
| 412 | + Current staged image: quay.io/centos-bootc/centos-bootc:stream9 |
| 413 | + Image version: stream9.20240807.0 (No timestamp present) |
| 414 | + Image transport: registry |
| 415 | + Image digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 |
| 416 | + Current booted state is native ostree |
| 417 | + No rollback image present |
| 418 | + "}; |
| 419 | + similar_asserts::assert_eq!(w, expected); |
| 420 | +} |
| 421 | + |
| 422 | +#[test] |
| 423 | +fn test_human_readable_booted_spec() { |
| 424 | + // booted image, no staged/rollback |
| 425 | + let w = human_status_from_spec_fixture(include_str!("fixtures/spec-only-booted.yaml")) |
| 426 | + .expect("No spec found"); |
| 427 | + let expected = indoc::indoc! { r" |
| 428 | + No staged image present |
| 429 | + Current booted image: quay.io/centos-bootc/centos-bootc:stream9 |
| 430 | + Image version: stream9.20240807.0 (No timestamp present) |
| 431 | + Image transport: registry |
| 432 | + Image digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 |
| 433 | + No rollback image present |
| 434 | + "}; |
| 435 | + similar_asserts::assert_eq!(w, expected); |
| 436 | +} |
| 437 | + |
| 438 | +#[test] |
| 439 | +fn test_human_readable_staged_rollback_spec() { |
| 440 | + // staged/rollback image, no booted |
| 441 | + let w = human_status_from_spec_fixture(include_str!("fixtures/spec-staged-rollback.yaml")) |
| 442 | + .expect("No spec found"); |
| 443 | + let expected = indoc::indoc! { r" |
| 444 | + Current staged image: quay.io/example/someimage:latest |
| 445 | + Image version: nightly (2023-10-14 19:22:15 UTC) |
| 446 | + Image transport: registry |
| 447 | + Image digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566 |
| 448 | + No booted image present |
| 449 | + Current rollback image: quay.io/example/someimage:latest |
| 450 | + Image version: nightly (2023-09-30 19:22:16 UTC) |
| 451 | + Image transport: registry |
| 452 | + Image digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34 |
| 453 | + "}; |
| 454 | + similar_asserts::assert_eq!(w, expected); |
| 455 | +} |
| 456 | + |
321 | 457 | #[test]
|
322 | 458 | fn test_convert_signatures() {
|
323 | 459 | use std::str::FromStr;
|
|
0 commit comments