Skip to content

Commit 5f6622c

Browse files
committed
feat(status): display pinned deployments
Part of #904 Displays pinned deployments as part of "bootc status". Includes unit tests to ensure correct parsing of the pinned deployments, and that they are displayed in human readable formats correctly. Signed-off-by: Robert Sturla <[email protected]>
1 parent df879ed commit 5f6622c

File tree

3 files changed

+142
-7
lines changed

3 files changed

+142
-7
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
apiVersion: org.containers.bootc/v1alpha1
2+
kind: BootcHost
3+
metadata:
4+
name: host
5+
spec:
6+
image:
7+
image: quay.io/centos-bootc/centos-bootc:stream9
8+
transport: registry
9+
bootOrder: default
10+
status:
11+
staged: null
12+
booted:
13+
image:
14+
image:
15+
image: quay.io/centos-bootc/centos-bootc:stream9
16+
transport: registry
17+
architecture: arm64
18+
version: stream9.20240807.0
19+
timestamp: null
20+
imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38
21+
cachedUpdate: null
22+
incompatible: false
23+
pinned: true
24+
ostree:
25+
checksum: 439f6bd2e2361bee292c1f31840d798c5ac5ba76483b8021dc9f7b0164ac0f48
26+
deploySerial: 0
27+
pinned:
28+
- image:
29+
image:
30+
image: quay.io/centos-bootc/centos-bootc:stream9
31+
transport: registry
32+
version: stream9.20240807.0
33+
timestamp: null
34+
imageDigest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b37
35+
architecture: arm64
36+
cachedUpdate: null
37+
incompatible: false
38+
pinned: true
39+
ostree:
40+
checksum: 99b2cc3b6edce9ebaef6a6076effa5ee3e1dcff3523016ffc94a1b27c6c67e12
41+
deploySerial: 0
42+
rollback: null
43+
rollbackQueued: false
44+
type: bootcHost

lib/src/spec.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,26 @@ pub struct HostStatus {
157157
/// Set to true if the rollback entry is queued for the next boot.
158158
#[serde(default)]
159159
pub rollback_queued: bool,
160+
/// All pinned images
161+
pub pinned: Option<Vec<BootEntry>>,
160162

161163
/// The detected type of system
162164
#[serde(rename = "type")]
163165
pub ty: Option<HostType>,
164166
}
165167

168+
impl HostStatus {
169+
/// Check if the slot is pinned
170+
pub fn is_pinned(&self, slot: Slot) -> bool {
171+
match slot {
172+
Slot::Staged => self.staged.as_ref().map_or(false, |s| s.pinned),
173+
Slot::Booted => self.booted.as_ref().map_or(false, |s| s.pinned),
174+
Slot::Rollback => self.rollback.as_ref().map_or(false, |s| s.pinned),
175+
Slot::Pinned => true,
176+
}
177+
}
178+
}
179+
166180
impl Host {
167181
/// Create a new host
168182
pub fn new(spec: HostSpec) -> Self {
@@ -196,6 +210,11 @@ impl Host {
196210
self.status.staged = None;
197211
self.status.booted = None;
198212
}
213+
Slot::Pinned => {
214+
self.status.staged = None;
215+
self.status.booted = None;
216+
self.status.rollback = None;
217+
}
199218
}
200219
}
201220
}

lib/src/status.rs

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ pub(crate) fn get_status(
253253
.map(|d| boot_entry_from_deployment(sysroot, d))
254254
.transpose()
255255
.context("Rollback deployment")?;
256+
let pinned = deployments
257+
.other
258+
.iter()
259+
.filter_map(|d| {
260+
d.is_pinned()
261+
.then(|| boot_entry_from_deployment(sysroot, d).ok())
262+
})
263+
.collect::<Option<Vec<_>>>()
264+
.filter(|v| !v.is_empty());
256265
let spec = staged
257266
.as_ref()
258267
.or(booted.as_ref())
@@ -280,6 +289,7 @@ pub(crate) fn get_status(
280289
booted,
281290
rollback,
282291
rollback_queued,
292+
pinned,
283293
ty,
284294
};
285295
Ok((deployments, host))
@@ -336,6 +346,7 @@ pub enum Slot {
336346
Staged,
337347
Booted,
338348
Rollback,
349+
Pinned,
339350
}
340351

341352
impl std::fmt::Display for Slot {
@@ -344,6 +355,7 @@ impl std::fmt::Display for Slot {
344355
Slot::Staged => "staged",
345356
Slot::Booted => "booted",
346357
Slot::Rollback => "rollback",
358+
Slot::Pinned => "pinned",
347359
};
348360
f.write_str(s)
349361
}
@@ -359,10 +371,11 @@ fn write_row_name(mut out: impl Write, s: &str, prefix_len: usize) -> Result<()>
359371
}
360372

361373
/// Write the data for a container image based status.
362-
fn human_render_imagestatus(
374+
fn human_render_image_deployment(
363375
mut out: impl Write,
364376
slot: Slot,
365377
image: &crate::spec::ImageStatus,
378+
host_status: &crate::spec::HostStatus,
366379
) -> Result<()> {
367380
let transport = &image.image.transport;
368381
let imagename = &image.image.image;
@@ -377,6 +390,7 @@ fn human_render_imagestatus(
377390
Slot::Staged => " Staged image".into(),
378391
Slot::Booted => format!("{} Booted image", crate::glyph::Glyph::BlackCircle),
379392
Slot::Rollback => " Rollback image".into(),
393+
Slot::Pinned => " Pinned image".into(),
380394
};
381395
let prefix_len = prefix.chars().count();
382396
writeln!(out, "{prefix}: {imageref}")?;
@@ -407,20 +421,37 @@ fn human_render_imagestatus(
407421
writeln!(out, "{timestamp}")?;
408422
}
409423

424+
if host_status.is_pinned(slot) {
425+
write_row_name(&mut out, "Pinned", prefix_len)?;
426+
writeln!(out, "yes")?;
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_deployment(
433+
mut out: impl Write,
434+
slot: Slot,
435+
ostree_commit: &str,
436+
host_status: &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(),
417441
Slot::Booted => format!("{} Booted ostree", crate::glyph::Glyph::BlackCircle),
418442
Slot::Rollback => " Rollback ostree".into(),
443+
Slot::Pinned => " Pinned ostree".into(),
419444
};
420445
let prefix_len = prefix.len();
421446
writeln!(out, "{prefix}")?;
422447
write_row_name(&mut out, "Commit", prefix_len)?;
423448
writeln!(out, "{ostree_commit}")?;
449+
450+
if host_status.is_pinned(slot) {
451+
write_row_name(&mut out, "Pinned", prefix_len)?;
452+
writeln!(out, "yes")?;
453+
}
454+
424455
Ok(())
425456
}
426457

@@ -438,14 +469,36 @@ fn human_readable_output_booted(mut out: impl Write, host: &Host) -> Result<()>
438469
writeln!(out)?;
439470
}
440471
if let Some(image) = &host_status.image {
441-
human_render_imagestatus(&mut out, slot_name, image)?;
472+
human_render_image_deployment(&mut out, slot_name, image, &host.status)?;
442473
} else if let Some(ostree) = host_status.ostree.as_ref() {
443-
human_render_ostree(&mut out, slot_name, &ostree.checksum)?;
474+
human_render_ostree_deployment(
475+
&mut out,
476+
slot_name,
477+
&ostree.checksum,
478+
&host.status,
479+
)?;
444480
} else {
445481
writeln!(out, "Current {slot_name} state is unknown")?;
446482
}
447483
}
448484
}
485+
486+
if let Some(pinned) = &host.status.pinned {
487+
writeln!(out)?;
488+
for entry in pinned {
489+
if let Some(image) = &entry.image {
490+
human_render_image_deployment(&mut out, Slot::Pinned, image, &host.status)?;
491+
} else if let Some(ostree) = entry.ostree.as_ref() {
492+
human_render_ostree_deployment(
493+
&mut out,
494+
Slot::Pinned,
495+
&ostree.checksum,
496+
&host.status,
497+
)?;
498+
}
499+
}
500+
}
501+
449502
Ok(())
450503
}
451504

@@ -480,7 +533,7 @@ mod tests {
480533
Staged image: quay.io/example/someimage:latest
481534
Digest: sha256:16dc2b6256b4ff0d2ec18d2dbfb06d117904010c8cf9732cdb022818cf7a7566 (arm64)
482535
Version: nightly (2023-10-14T19:22:15Z)
483-
536+
484537
● Booted image: quay.io/example/someimage:latest
485538
Digest: sha256:736b359467c9437c1ac915acaae952aad854e07eb4a16a94999a48af08c83c34 (arm64)
486539
Version: nightly (2023-09-30T19:22:16Z)
@@ -498,7 +551,7 @@ mod tests {
498551
let expected = indoc::indoc! { r"
499552
Staged ostree
500553
Commit: 1c24260fdd1be20f72a4a97a75c582834ee3431fbb0fa8e4f482bb219d633a45
501-
554+
502555
● Booted ostree
503556
Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
504557
"};
@@ -514,7 +567,7 @@ mod tests {
514567
Staged image: quay.io/centos-bootc/centos-bootc:stream9
515568
Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 (s390x)
516569
Version: stream9.20240807.0
517-
570+
518571
● Booted ostree
519572
Commit: f9fa3a553ceaaaf30cf85bfe7eed46a822f7b8fd7e14c1e3389cbc3f6d27f791
520573
"};
@@ -534,6 +587,25 @@ mod tests {
534587
similar_asserts::assert_eq!(w, expected);
535588
}
536589

590+
#[test]
591+
fn test_human_readable_booted_pinned_spec() {
592+
// booted image, no staged/rollback
593+
let w = human_status_from_spec_fixture(include_str!("fixtures/spec-booted-pinned.yaml"))
594+
.expect("No spec found");
595+
let expected = indoc::indoc! { r"
596+
● Booted image: quay.io/centos-bootc/centos-bootc:stream9
597+
Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b38 (arm64)
598+
Version: stream9.20240807.0
599+
Pinned: yes
600+
601+
Pinned image: quay.io/centos-bootc/centos-bootc:stream9
602+
Digest: sha256:47e5ed613a970b6574bfa954ab25bb6e85656552899aa518b5961d9645102b37 (arm64)
603+
Version: stream9.20240807.0
604+
Pinned: yes
605+
"};
606+
similar_asserts::assert_eq!(w, expected);
607+
}
608+
537609
#[test]
538610
fn test_human_readable_staged_rollback_spec() {
539611
// staged/rollback image, no booted

0 commit comments

Comments
 (0)