Skip to content

Commit 254c2a0

Browse files
committed
Add "rhsm" feature for integration with Red Hat Subscription Manager
This adds a new subcommand `bootc internals publish-rhsm-facts` which writes out facts data to /etc/rhsm/facts/bootc.json Signed-off-by: John Eckersberg <[email protected]>
1 parent 82d8939 commit 254c2a0

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

lib/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ static_assertions = { workspace = true }
5757
default = ["install"]
5858
# This feature enables `bootc install`. Disable if you always want to use an external installer.
5959
install = []
60+
# This featuares enables `bootc internals publish-rhsm-facts` to integrate with
61+
# Red Hat Subscription Manager
62+
rhsm = []
6063
# Implementation detail of man page generation.
6164
docgen = ["clap_mangen"]
6265

lib/src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,9 @@ pub(crate) enum InternalsOpts {
375375
// The stateroot
376376
stateroot: String,
377377
},
378+
#[cfg(feature = "rhsm")]
379+
/// Publish subscription-manager facts to /etc/rhsm/facts/bootc.json
380+
PublishRhsmFacts,
378381
}
379382

380383
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
@@ -1096,6 +1099,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
10961099
let rootfs = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
10971100
crate::install::completion::run_from_ostree(rootfs, &sysroot, &stateroot).await
10981101
}
1102+
#[cfg(feature = "rhsm")]
1103+
InternalsOpts::PublishRhsmFacts => crate::rhsm::publish_facts(&root).await,
10991104
},
11001105
#[cfg(feature = "docgen")]
11011106
Opt::Man(manopts) => crate::docgen::generate_manpages(&manopts.directory),

lib/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@ mod install;
4242
mod kernel;
4343
#[cfg(feature = "install")]
4444
pub(crate) mod mount;
45+
46+
#[cfg(feature = "rhsm")]
47+
mod rhsm;

lib/src/rhsm.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! Integration with Red Hat Subscription Manager
2+
3+
use anyhow::Result;
4+
use cap_std::fs::{Dir, OpenOptions};
5+
use cap_std_ext::cap_std;
6+
use fn_error_context::context;
7+
use serde::Serialize;
8+
9+
const FACTS_PATH: &str = "etc/rhsm/facts/bootc.json";
10+
11+
#[derive(Serialize)]
12+
struct RhsmFacts {
13+
#[serde(rename = "bootc.booted.image")]
14+
booted_image: String,
15+
#[serde(rename = "bootc.booted.version")]
16+
booted_version: String,
17+
#[serde(rename = "bootc.booted.digest")]
18+
booted_digest: String,
19+
#[serde(rename = "bootc.staged.image")]
20+
staged_image: String,
21+
#[serde(rename = "bootc.staged.version")]
22+
staged_version: String,
23+
#[serde(rename = "bootc.staged.digest")]
24+
staged_digest: String,
25+
#[serde(rename = "bootc.rollback.image")]
26+
rollback_image: String,
27+
#[serde(rename = "bootc.rollback.version")]
28+
rollback_version: String,
29+
#[serde(rename = "bootc.rollback.digest")]
30+
rollback_digest: String,
31+
#[serde(rename = "bootc.available.image")]
32+
available_image: String,
33+
#[serde(rename = "bootc.available.version")]
34+
available_version: String,
35+
#[serde(rename = "bootc.available.digest")]
36+
available_digest: String,
37+
}
38+
39+
impl From<crate::spec::HostStatus> for RhsmFacts {
40+
fn from(hoststatus: crate::spec::HostStatus) -> Self {
41+
let (booted_image, booted_version, booted_digest) = hoststatus
42+
.booted
43+
.as_ref()
44+
.and_then(|boot_entry| {
45+
boot_entry.image.as_ref().map(|imagestatus| {
46+
let image = imagestatus.image.image.clone();
47+
let version = imagestatus
48+
.version
49+
.as_ref()
50+
.map_or_else(|| String::from(""), |v| v.clone());
51+
let digest = imagestatus.image_digest.clone();
52+
53+
(image, digest, version)
54+
})
55+
})
56+
.unwrap_or_else(|| (String::from(""), String::from(""), String::from("")));
57+
58+
let (staged_image, staged_version, staged_digest) = hoststatus
59+
.staged
60+
.as_ref()
61+
.and_then(|boot_entry| {
62+
boot_entry.image.as_ref().map(|imagestatus| {
63+
let image = imagestatus.image.image.clone();
64+
let version = imagestatus
65+
.version
66+
.as_ref()
67+
.map_or_else(|| String::from(""), |v| v.clone());
68+
let digest = imagestatus.image_digest.clone();
69+
70+
(image, digest, version)
71+
})
72+
})
73+
.unwrap_or_else(|| (String::from(""), String::from(""), String::from("")));
74+
75+
let (rollback_image, rollback_version, rollback_digest) = hoststatus
76+
.rollback
77+
.as_ref()
78+
.and_then(|boot_entry| {
79+
boot_entry.image.as_ref().map(|imagestatus| {
80+
let image = imagestatus.image.image.clone();
81+
let version = imagestatus
82+
.version
83+
.as_ref()
84+
.map_or_else(|| String::from(""), |v| v.clone());
85+
let digest = imagestatus.image_digest.clone();
86+
87+
(image, digest, version)
88+
})
89+
})
90+
.unwrap_or_else(|| (String::from(""), String::from(""), String::from("")));
91+
92+
let (available_image, available_version, available_digest) = hoststatus
93+
.booted
94+
.as_ref()
95+
.and_then(|boot_entry| {
96+
boot_entry.cached_update.as_ref().map(|imagestatus| {
97+
let image = imagestatus.image.image.clone();
98+
let version = imagestatus
99+
.version
100+
.as_ref()
101+
.map_or_else(|| String::from(""), |v| v.clone());
102+
let digest = imagestatus.image_digest.clone();
103+
104+
(image, digest, version)
105+
})
106+
})
107+
.unwrap_or_else(|| (String::from(""), String::from(""), String::from("")));
108+
109+
Self {
110+
booted_image,
111+
booted_version,
112+
booted_digest,
113+
staged_image,
114+
staged_version,
115+
staged_digest,
116+
rollback_image,
117+
rollback_version,
118+
rollback_digest,
119+
available_image,
120+
available_version,
121+
available_digest,
122+
}
123+
}
124+
}
125+
126+
/// Publish facts for subscription-manager consumption
127+
#[context("Publishing facts")]
128+
pub(crate) async fn publish_facts(root: &Dir) -> Result<()> {
129+
let sysroot = super::cli::get_storage().await?;
130+
let booted_deployment = sysroot.booted_deployment();
131+
let (_deployments, host) = crate::status::get_status(&sysroot, booted_deployment.as_ref())?;
132+
133+
let facts = RhsmFacts::from(host.status);
134+
let mut bootc_facts_file = root.open_with(
135+
FACTS_PATH,
136+
OpenOptions::new().write(true).create(true).truncate(true),
137+
)?;
138+
serde_json::to_writer_pretty(&mut bootc_facts_file, &facts)?;
139+
Ok(())
140+
}

0 commit comments

Comments
 (0)