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

cli: Add a human readable format to status #738

Merged
merged 1 commit into from
Sep 18, 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: 2 additions & 0 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub(crate) struct EditOpts {
#[derive(Debug, Clone, ValueEnum, PartialEq, Eq)]
#[clap(rename_all = "lowercase")]
pub(crate) enum OutputFormat {
/// Output in Human Readable format.
HumanReadable,
/// Output in YAML format.
Yaml,
/// Output in JSON format.
Expand Down
28 changes: 28 additions & 0 deletions lib/src/fixtures/spec-only-booted.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: org.containers.bootc/v1alpha1
kind: BootcHost
metadata:
name: host
spec:
image:
image: quay.io/centos-bootc/centos-bootc:stream9
transport: registry
bootOrder: default
status:
staged: null
booted:
image:
image:
image: quay.io/centos-bootc/centos-bootc:stream9
transport: registry
version: stream9.20240807.0
timestamp: null
imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38
cachedUpdate: null
incompatible: false
pinned: false
ostree:
checksum: 439f6bd2e2361bee292c1f31840d798c5ac5ba76483b8021dc9f7b0164ac0f48
deploySerial: 0
rollback: null
rollbackQueued: false
type: bootcHost
37 changes: 37 additions & 0 deletions lib/src/fixtures/spec-ostree-to-bootc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: org.containers.bootc/v1alpha1
kind: BootcHost
metadata:
name: host
spec:
image:
image: quay.io/centos-bootc/centos-bootc:stream9
transport: registry
bootOrder: default
status:
staged:
image:
image:
image: quay.io/centos-bootc/centos-bootc:stream9
transport: registry
version: stream9.20240807.0
timestamp: null
imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38
cachedUpdate: null
incompatible: false
pinned: false
store: ostreeContainer
ostree:
checksum: 05cbf6dcae32e7a1c5a0774a648a073a5834a305ca92204b53fb6c281fe49db1
deploySerial: 0
booted:
image: null
cachedUpdate: null
incompatible: false
pinned: false
store: null
ostree:
checksum: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
deploySerial: 0
rollback: null
rollbackQueued: false
type: null
29 changes: 29 additions & 0 deletions lib/src/fixtures/spec-rfe-ostree-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: org.containers.bootc/v1alpha1
kind: BootcHost
metadata:
name: host
spec:
image: null
bootOrder: default
status:
staged:
image: null
cachedUpdate: null
incompatible: true
pinned: false
store: null
ostree:
checksum: 1c24260fdd1be20f72a4a97a75c582834ee3431fbb0fa8e4f482bb219d633a45
deploySerial: 0
booted:
image: null
cachedUpdate: null
incompatible: false
pinned: false
store: null
ostree:
checksum: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
deploySerial: 0
rollback: null
rollbackQueued: false
type: null
40 changes: 40 additions & 0 deletions lib/src/fixtures/spec-staged-booted.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
apiVersion: org.containers.bootc/v1alpha1
kind: BootcHost
metadata:
name: host
spec:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
status:
staged:
image:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
version: nightly
timestamp: 2023-10-14T19:22:15Z
imageDigest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566
incompatible: false
pinned: false
ostree:
checksum: 3c6dad657109522e0b2e49bf44b5420f16f0b438b5b9357e5132211cfbad135d
deploySerial: 0
booted:
image:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
version: nightly
timestamp: 2023-09-30T19:22:16Z
imageDigest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34
incompatible: false
pinned: false
ostree:
checksum: 26836632adf6228d64ef07a26fd3efaf177104efd1f341a2cf7909a3e4e2c72c
deploySerial: 0
rollback: null
isContainer: false
40 changes: 40 additions & 0 deletions lib/src/fixtures/spec-staged-rollback.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
apiVersion: org.containers.bootc/v1alpha1
kind: BootcHost
metadata:
name: host
spec:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
status:
staged:
image:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
version: nightly
timestamp: 2023-10-14T19:22:15Z
imageDigest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566
incompatible: false
pinned: false
ostree:
checksum: 3c6dad657109522e0b2e49bf44b5420f16f0b438b5b9357e5132211cfbad135d
deploySerial: 0
booted: null
rollback:
image:
image:
image: quay.io/example/someimage:latest
transport: registry
signature: insecure
version: nightly
timestamp: 2023-09-30T19:22:16Z
imageDigest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34
incompatible: false
pinned: false
ostree:
checksum: 26836632adf6228d64ef07a26fd3efaf177104efd1f341a2cf7909a3e4e2c72c
deploySerial: 0
isContainer: false
136 changes: 136 additions & 0 deletions lib/src/status.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::collections::VecDeque;
use std::io::IsTerminal;
use std::io::Write;

use anyhow::{Context, Result};
use camino::Utf8Path;
Expand Down Expand Up @@ -305,19 +307,153 @@ pub(crate) async fn status(opts: super::cli::StatusOpts) -> Result<()> {
let mut out = out.lock();
let legacy_opt = if opts.json {
OutputFormat::Json
} else if std::io::stdout().is_terminal() {
OutputFormat::HumanReadable
} else {
OutputFormat::Yaml
};
let format = opts.format.unwrap_or(legacy_opt);
match format {
OutputFormat::Json => serde_json::to_writer(&mut out, &host).map_err(anyhow::Error::new),
OutputFormat::Yaml => serde_yaml::to_writer(&mut out, &host).map_err(anyhow::Error::new),
OutputFormat::HumanReadable => human_readable_output(&mut out, &host),
}
.context("Writing to stdout")?;

Ok(())
}

fn human_readable_output(mut out: impl Write, host: &Host) -> Result<()> {
for (status_string, status) in [
("staged", &host.status.staged),
("booted", &host.status.booted),
("rollback", &host.status.rollback),
] {
if let Some(host_status) = status {
if let Some(image) = &host_status.image {
writeln!(
out,
"Current {} image: {}",
status_string, image.image.image
)?;

let version = image
.version
.as_deref()
.unwrap_or("No image version defined");
let timestamp = image
.timestamp
.as_ref()
.map(|t| t.to_string())
.unwrap_or_else(|| "No timestamp present".to_owned());
let transport = &image.image.transport;
let digest = &image.image_digest;

writeln!(out, " Image version: {version} ({timestamp})")?;
writeln!(out, " Image transport: {transport}")?;
writeln!(out, " Image digest: {digest}")?;
} else {
writeln!(out, "Current {status_string} state is native ostree")?;
}
} else {
writeln!(out, "No {status_string} image present")?;
}
}
Ok(())
}

fn human_status_from_spec_fixture(spec_fixture: &str) -> Result<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't we get an unused function warning?

It may help to put everything here under a

#[cfg(test)]
mod tests {
...
}

as is done elsewhere in the code

let host: Host = serde_yaml::from_str(spec_fixture).unwrap();
let mut w = Vec::new();
human_readable_output(&mut w, &host).unwrap();
let w = String::from_utf8(w).unwrap();
Ok(w)
}

#[test]
fn test_human_readable_base_spec() {
// Tests Staged and Booted, null Rollback
let w = human_status_from_spec_fixture(include_str!("fixtures/spec-staged-booted.yaml"))
.expect("No spec found");
let expected = indoc::indoc! { r"
Current staged image: quay.io/example/someimage:latest
Image version: nightly (2023-10-14 19:22:15 UTC)
Image transport: registry
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about omitting this when it's registry? That's the 95% case I think.

Image digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566
Current booted image: quay.io/example/someimage:latest
Image version: nightly (2023-09-30 19:22:16 UTC)
Image transport: registry
Image digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34
No rollback image present
"};
similar_asserts::assert_eq!(w, expected);
}

#[test]
fn test_human_readable_rfe_spec() {
// Basic rhel for edge bootc install with nothing
djach7 marked this conversation as resolved.
Show resolved Hide resolved
let w =
human_status_from_spec_fixture(include_str!("fixtures/spec-rfe-ostree-deployment.yaml"))
.expect("No spec found");
let expected = indoc::indoc! { r"
Current staged state is native ostree
Current booted state is native ostree
No rollback image present
"};
similar_asserts::assert_eq!(w, expected);
}

#[test]
fn test_human_readable_staged_spec() {
// staged image, no boot/rollback
let w = human_status_from_spec_fixture(include_str!("fixtures/spec-ostree-to-bootc.yaml"))
.expect("No spec found");
let expected = indoc::indoc! { r"
Current staged image: quay.io/centos-bootc/centos-bootc:stream9
Image version: stream9.20240807.0 (No timestamp present)
Image transport: registry
Image digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38
Current booted state is native ostree
No rollback image present
"};
similar_asserts::assert_eq!(w, expected);
}

#[test]
fn test_human_readable_booted_spec() {
// booted image, no staged/rollback
let w = human_status_from_spec_fixture(include_str!("fixtures/spec-only-booted.yaml"))
.expect("No spec found");
let expected = indoc::indoc! { r"
No staged image present
Current booted image: quay.io/centos-bootc/centos-bootc:stream9
Image version: stream9.20240807.0 (No timestamp present)
Image transport: registry
Image digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38
No rollback image present
djach7 marked this conversation as resolved.
Show resolved Hide resolved
"};
similar_asserts::assert_eq!(w, expected);
}

#[test]
fn test_human_readable_staged_rollback_spec() {
// staged/rollback image, no booted
let w = human_status_from_spec_fixture(include_str!("fixtures/spec-staged-rollback.yaml"))
.expect("No spec found");
let expected = indoc::indoc! { r"
Current staged image: quay.io/example/someimage:latest
Image version: nightly (2023-10-14 19:22:15 UTC)
Image transport: registry
Image digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566
No booted image present
Current rollback image: quay.io/example/someimage:latest
Image version: nightly (2023-09-30 19:22:16 UTC)
Image transport: registry
Image digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34
"};
similar_asserts::assert_eq!(w, expected);
}

#[test]
fn test_convert_signatures() {
use std::str::FromStr;
Expand Down