Skip to content

Commit 29d0b99

Browse files
committed
WIP: Retrieve bound images when staging new image
Signed-off-by: Chris Kyrouac <[email protected]>
1 parent 5e9279d commit 29d0b99

File tree

5 files changed

+156
-14
lines changed

5 files changed

+156
-14
lines changed

lib/src/boundimage.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use anyhow::Result;
2+
3+
use ostree_ext::ostree::Deployment;
4+
use ostree_ext::sysroot::SysrootLock;
5+
6+
use std::fs;
7+
use std::path::Path;
8+
9+
use crate::task::Task;
10+
11+
use tempfile::TempDir;
12+
13+
const BOOTC_QUADLET_DIR: &'static str = "/etc/containers/systemd/bootc";
14+
const QUADLET_BINARY: &'static str = "/usr/lib/systemd/system-generators/podman-system-generator";
15+
const SYSTEMD_DIR: &'static str = "/etc/systemd/system";
16+
17+
pub(crate) struct BoundImageManager {
18+
quadlet_unit_dir: String,
19+
units: Vec<String>,
20+
temp_dir: TempDir,
21+
}
22+
23+
impl BoundImageManager {
24+
pub(crate) fn new(deployment: &Deployment, sysroot: &SysrootLock) -> Result<BoundImageManager> {
25+
let deployment_dir = sysroot.deployment_dirpath(&deployment);
26+
let quadlet_unit_dir = format!("/{deployment_dir}/{BOOTC_QUADLET_DIR}");
27+
28+
let temp_dir = TempDir::new()?;
29+
let bound_image_manager = BoundImageManager {
30+
quadlet_unit_dir,
31+
units: Vec::new(),
32+
temp_dir,
33+
};
34+
Ok(bound_image_manager)
35+
}
36+
37+
pub(crate) fn run(&mut self) -> Result<()> {
38+
if Path::new(&self.quadlet_unit_dir).exists() {
39+
self.run_quadlet()?;
40+
self.move_units()?;
41+
self.restart_systemd()?;
42+
self.start_new_services()?;
43+
}
44+
45+
Ok(())
46+
}
47+
48+
// Run podman-system-generator to generate the systemd units
49+
// the output is written to a temporary directory
50+
// in order to track the generated units.
51+
// The generated units need to be moved to /etc/systemd/system
52+
// to be started by systemd.
53+
fn run_quadlet(&self) -> Result<()> {
54+
Task::new(
55+
format!("Running quadlet on {:#}", self.quadlet_unit_dir),
56+
QUADLET_BINARY,
57+
)
58+
.arg(self.temp_dir.path())
59+
.env(&"QUADLET_UNIT_DIRS".to_string(), &self.quadlet_unit_dir)
60+
.run()?;
61+
62+
Ok(())
63+
}
64+
65+
fn move_units(&mut self) -> Result<()> {
66+
let entries = fs::read_dir(self.temp_dir.path())?;
67+
for bound_image in entries {
68+
let bound_image = bound_image?;
69+
let bound_image_path = bound_image.path();
70+
let unit_name = bound_image_path.file_name().unwrap().to_str().unwrap();
71+
72+
//move the unit file from the bootc subdirectory to the root systemd directory
73+
let systemd_dst = format!("{SYSTEMD_DIR}/{unit_name}");
74+
if !Path::new(systemd_dst.as_str()).exists() {
75+
fs::copy(&bound_image_path, systemd_dst)?;
76+
}
77+
78+
self.units.push(unit_name.to_string());
79+
}
80+
81+
Ok(())
82+
}
83+
84+
fn restart_systemd(&self) -> Result<()> {
85+
Task::new_and_run("Reloading systemd", "/usr/bin/systemctl", ["daemon-reload"])?;
86+
Ok(())
87+
}
88+
89+
fn start_new_services(&self) -> Result<()> {
90+
//TODO: do this in parallel
91+
for unit in &self.units {
92+
Task::new_and_run(
93+
format!("Starting target: {:#}", unit),
94+
"/usr/bin/systemctl",
95+
["start", unit],
96+
)?;
97+
}
98+
Ok(())
99+
}
100+
}
101+
102+
impl Drop for BoundImageManager {
103+
//remove the generated units from the root systemd directory
104+
//and stop them to remove the services from systemd
105+
fn drop(&mut self) {
106+
for unit in &self.units {
107+
//TODO: error handling
108+
let _ = fs::remove_file(format!("{SYSTEMD_DIR}/{unit}"));
109+
let _ = Task::new_and_run(
110+
format!("Starting target: {:#}", unit),
111+
"/usr/bin/systemctl",
112+
["stop", unit],
113+
);
114+
}
115+
}
116+
}

lib/src/deploy.rs

+17-11
Original file line numberDiff line numberDiff line change
@@ -278,20 +278,21 @@ async fn deploy(
278278
image: &ImageState,
279279
origin: &glib::KeyFile,
280280
opts: Option<ostree::SysrootDeployTreeOpts<'_>>,
281-
) -> Result<()> {
281+
) -> Result<Deployment> {
282282
let stateroot = Some(stateroot);
283283
let opts = opts.unwrap_or_default();
284284
// Copy to move into thread
285285
let cancellable = gio::Cancellable::NONE;
286-
let _new_deployment = sysroot.stage_tree_with_options(
287-
stateroot,
288-
image.ostree_commit.as_str(),
289-
Some(origin),
290-
merge_deployment,
291-
&opts,
292-
cancellable,
293-
)?;
294-
Ok(())
286+
return sysroot
287+
.stage_tree_with_options(
288+
stateroot,
289+
image.ostree_commit.as_str(),
290+
Some(origin),
291+
merge_deployment,
292+
&opts,
293+
cancellable,
294+
)
295+
.map_err(Into::into);
295296
}
296297

297298
#[context("Generating origin")]
@@ -317,7 +318,7 @@ pub(crate) async fn stage(
317318
) -> Result<()> {
318319
let merge_deployment = sysroot.merge_deployment(Some(stateroot));
319320
let origin = origin_from_imageref(spec.image)?;
320-
crate::deploy::deploy(
321+
let deployment = crate::deploy::deploy(
321322
sysroot,
322323
merge_deployment.as_ref(),
323324
stateroot,
@@ -326,6 +327,11 @@ pub(crate) async fn stage(
326327
opts,
327328
)
328329
.await?;
330+
331+
//TODO: does this need to be mutable?
332+
let mut bound_image_manager = crate::boundimage::BoundImageManager::new(&deployment, sysroot)?;
333+
bound_image_manager.run()?;
334+
329335
crate::deploy::cleanup(sysroot).await?;
330336
println!("Queued for next boot: {:#}", spec.image);
331337
if let Some(version) = image.version.as_deref() {

lib/src/install.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,22 @@ async fn install_to_filesystem_impl(state: &State, rootfs: &mut RootSetup) -> Re
12041204
anyhow::Ok(())
12051205
})
12061206
.context("Writing aleph version")?;
1207+
1208+
// TODO: add code to run quadlet/systemd against the bootc-bound-image directory
1209+
// let bound = query_bound_state(&inst.deployment)?;
1210+
// bound.print();
1211+
// if !bound.is_empty() {
1212+
// println!();
1213+
// Task::new("Mounting deployment /var", "mount")
1214+
// .args(["--bind", ".", "/var"])
1215+
// .cwd(&inst.var)?
1216+
// .run()?;
1217+
// // podman needs this
1218+
// Task::new("Initializing /var/tmp", "systemd-tmpfiles")
1219+
// .args(["--create", "--boot", "--prefix=/var/tmp"])
1220+
// .verbose()
1221+
// .run()?;
1222+
// crate::deploy::fetch_bound_state(&bound).await?;
12071223
}
12081224

12091225
crate::bootloader::install_via_bootupd(&rootfs.device, &rootfs.rootfs, &state.config_opts)?;

lib/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#![allow(clippy::needless_borrows_for_generic_args)]
1919

2020
pub mod cli;
21+
mod boundimage;
2122
pub(crate) mod deploy;
2223
pub(crate) mod generator;
2324
pub(crate) mod journal;

lib/src/task.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use std::{
2-
ffi::OsStr,
3-
io::{Seek, Write},
4-
process::{Command, Stdio},
2+
ffi::OsStr, io::{Seek, Write}, process::{Command, Stdio}
53
};
64

75
use anyhow::{Context, Result};
@@ -76,6 +74,11 @@ impl Task {
7674
self
7775
}
7876

77+
pub(crate) fn env(mut self, k: &str, v: &str) -> Self {
78+
self.cmd.env(k, v);
79+
self
80+
}
81+
7982
pub(crate) fn args<S: AsRef<OsStr>>(mut self, args: impl IntoIterator<Item = S>) -> Self {
8083
self.cmd.args(args);
8184
self

0 commit comments

Comments
 (0)