Skip to content

Commit 7361768

Browse files
committed
feat(status): display usroverlay state in status
Closes #474 Displays the status of the usroverlay within bootc status. Although not ideal, handles the displaying of the host's filesystem within the image/ostree functions since otherwise it may require the introduction of another "host" block within the status output (i.e. having a "host", "staged", "booted" and "rollback"). Signed-off-by: Robert Sturla <[email protected]>
1 parent 5f1cc22 commit 7361768

File tree

2 files changed

+83
-8
lines changed

2 files changed

+83
-8
lines changed

lib/src/spec.rs

+42-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
33
use std::fmt::Display;
44

5-
use ostree_ext::container::OstreeImageReference;
65
use ostree_ext::oci_spec::image::Digest;
6+
use ostree_ext::{container::OstreeImageReference, ostree::DeploymentUnlockedState};
77
use schemars::JsonSchema;
88
use serde::{Deserialize, Serialize};
99

@@ -53,6 +53,41 @@ pub enum Store {
5353
OstreeContainer,
5454
}
5555

56+
#[derive(
57+
clap::ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema,
58+
)]
59+
#[serde(rename_all = "camelCase")]
60+
/// The filesystem overlay type
61+
pub enum FilesystemOverlay {
62+
/// Readonly overlay mode
63+
Readonly,
64+
/// Read-write overlay mode
65+
ReadWrite,
66+
}
67+
68+
impl FilesystemOverlay {
69+
/// Convert from the ostree deployment state
70+
pub fn from_ostree_deployment_state(state: &DeploymentUnlockedState) -> Option<Self> {
71+
match state {
72+
DeploymentUnlockedState::None => Some(Self::Readonly),
73+
DeploymentUnlockedState::Development
74+
| DeploymentUnlockedState::Transient
75+
| DeploymentUnlockedState::Hotfix => Some(Self::ReadWrite),
76+
// Default to readonly when unknown since it is the default
77+
// for bootc deployments.
78+
_ => Some(Self::Readonly),
79+
}
80+
}
81+
82+
/// Convert the FilesystemOverlay value to a human-readable string
83+
pub fn to_human_string(&self) -> String {
84+
match self {
85+
Self::Readonly => "read-only".to_string(),
86+
Self::ReadWrite => "read-write".to_string(),
87+
}
88+
}
89+
}
90+
5691
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, JsonSchema)]
5792
#[serde(rename_all = "camelCase")]
5893
/// The host specification
@@ -62,6 +97,9 @@ pub struct HostSpec {
6297
/// If set, and there is a rollback deployment, it will be set for the next boot.
6398
#[serde(default)]
6499
pub boot_order: BootOrder,
100+
/// Matches the `ostree admin unlock` state
101+
#[serde(default)]
102+
pub usr_overlay: Option<FilesystemOverlay>,
65103
}
66104

67105
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
@@ -157,6 +195,9 @@ pub struct HostStatus {
157195
/// Set to true if the rollback entry is queued for the next boot.
158196
#[serde(default)]
159197
pub rollback_queued: bool,
198+
/// Matches the `ostree admin unlock` state
199+
#[serde(default)]
200+
pub usr_overlay: Option<FilesystemOverlay>,
160201

161202
/// The detected type of system
162203
#[serde(rename = "type")]

lib/src/status.rs

+41-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use ostree_ext::oci_spec;
1515
use ostree_ext::ostree;
1616

1717
use crate::cli::OutputFormat;
18+
use crate::spec::FilesystemOverlay;
1819
use crate::spec::{BootEntry, BootOrder, Host, HostSpec, HostStatus, HostType};
1920
use crate::spec::{ImageReference, ImageSignature};
2021
use crate::store::{CachedImageStatus, ContainerImageStore, Storage};
@@ -225,6 +226,12 @@ pub(crate) fn get_status(
225226
} else {
226227
BootOrder::Default
227228
};
229+
let usr_overlay = FilesystemOverlay::from_ostree_deployment_state(
230+
&booted_deployment
231+
.as_ref()
232+
.expect("Expected a booted deployment")
233+
.unlocked(),
234+
);
228235
tracing::debug!("Rollback queued={rollback_queued:?}");
229236
let other = {
230237
related_deployments.extend(other_deployments);
@@ -260,6 +267,7 @@ pub(crate) fn get_status(
260267
.map(|img| HostSpec {
261268
image: Some(img.image.clone()),
262269
boot_order,
270+
usr_overlay,
263271
})
264272
.unwrap_or_default();
265273

@@ -280,6 +288,7 @@ pub(crate) fn get_status(
280288
booted,
281289
rollback,
282290
rollback_queued,
291+
usr_overlay,
283292
ty,
284293
};
285294
Ok((deployments, host))
@@ -331,7 +340,7 @@ pub(crate) async fn status(opts: super::cli::StatusOpts) -> Result<()> {
331340
Ok(())
332341
}
333342

334-
#[derive(Debug)]
343+
#[derive(Debug, PartialEq)]
335344
pub enum Slot {
336345
Staged,
337346
Booted,
@@ -363,6 +372,7 @@ fn human_render_imagestatus(
363372
mut out: impl Write,
364373
slot: Slot,
365374
image: &crate::spec::ImageStatus,
375+
host: &crate::spec::HostStatus,
366376
) -> Result<()> {
367377
let transport = &image.image.transport;
368378
let imagename = &image.image.image;
@@ -407,10 +417,24 @@ fn human_render_imagestatus(
407417
writeln!(out, "{timestamp}")?;
408418
}
409419

420+
if let Some(usr_overlay) = &host.usr_overlay {
421+
// Only show the usr filesystem overlay state if we are booted and not the default
422+
// read-only mode.
423+
if slot == Slot::Booted && *usr_overlay != FilesystemOverlay::Readonly {
424+
write_row_name(&mut out, "Usr overlay", prefix_len)?;
425+
writeln!(out, "{}", usr_overlay.to_human_string())?;
426+
}
427+
}
428+
410429
Ok(())
411430
}
412431

413-
fn human_render_ostree(mut out: impl Write, slot: Slot, ostree_commit: &str) -> Result<()> {
432+
fn human_render_ostree(
433+
mut out: impl Write,
434+
slot: Slot,
435+
ostree_commit: &str,
436+
host: &crate::spec::HostStatus,
437+
) -> Result<()> {
414438
// TODO consider rendering more ostree stuff here like rpm-ostree status does
415439
let prefix = match slot {
416440
Slot::Staged => " Staged ostree".into(),
@@ -421,6 +445,16 @@ fn human_render_ostree(mut out: impl Write, slot: Slot, ostree_commit: &str) ->
421445
writeln!(out, "{prefix}")?;
422446
write_row_name(&mut out, "Commit", prefix_len)?;
423447
writeln!(out, "{ostree_commit}")?;
448+
449+
if let Some(usr_overlay) = &host.usr_overlay {
450+
// Only show the usr filesystem overlay state if we are booted and not the default
451+
// read-only mode.
452+
if slot == Slot::Booted && *usr_overlay != FilesystemOverlay::Readonly {
453+
write_row_name(&mut out, "Usr overlay", prefix_len)?;
454+
writeln!(out, "{}", usr_overlay.to_human_string())?;
455+
}
456+
}
457+
424458
Ok(())
425459
}
426460

@@ -438,9 +472,9 @@ fn human_readable_output_booted(mut out: impl Write, host: &Host) -> Result<()>
438472
writeln!(out)?;
439473
}
440474
if let Some(image) = &host_status.image {
441-
human_render_imagestatus(&mut out, slot_name, image)?;
475+
human_render_imagestatus(&mut out, slot_name, image, &host.status)?;
442476
} else if let Some(ostree) = host_status.ostree.as_ref() {
443-
human_render_ostree(&mut out, slot_name, &ostree.checksum)?;
477+
human_render_ostree(&mut out, slot_name, &ostree.checksum, &host.status)?;
444478
} else {
445479
writeln!(out, "Current {slot_name} state is unknown")?;
446480
}
@@ -480,7 +514,7 @@ mod tests {
480514
Staged image: quay.io/example/someimage:latest
481515
Digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566 (arm64)
482516
Version: nightly (2023-10-14T19:22:15Z)
483-
517+
484518
● Booted image: quay.io/example/someimage:latest
485519
Digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34 (arm64)
486520
Version: nightly (2023-09-30T19:22:16Z)
@@ -498,7 +532,7 @@ mod tests {
498532
let expected = indoc::indoc! { r"
499533
Staged ostree
500534
Commit: 1c24260fdd1be20f72a4a97a75c582834ee3431fbb0fa8e4f482bb219d633a45
501-
535+
502536
● Booted ostree
503537
Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
504538
"};
@@ -514,7 +548,7 @@ mod tests {
514548
Staged image: quay.io/centos-bootc/centos-bootc:stream9
515549
Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 (s390x)
516550
Version: stream9.20240807.0
517-
551+
518552
● Booted ostree
519553
Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
520554
"};

0 commit comments

Comments
 (0)