Skip to content

Commit eca9779

Browse files
committed
WIP: Add progress for install
It turns out to be *really* hard to make this work with my default dev setup of podman-machine...FIFOs don't work over virtiofs for unsurprising reasons, and podman doesn't do fd passing with podman-remote. I think the best fix is to add pipe fd passing with podman remote and have it act as a copying proxy. Signed-off-by: Colin Walters <[email protected]>
1 parent 6759010 commit eca9779

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

lib/src/cli.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use ostree_ext::keyfileext::KeyFileExt;
2222
use ostree_ext::ostree;
2323
use schemars::schema_for;
2424
use serde::{Deserialize, Serialize};
25+
use tokio::net::unix::pipe::Sender;
2526

2627
use crate::deploy::RequiredHostSpec;
2728
use crate::lints;
@@ -31,26 +32,38 @@ use crate::spec::ImageReference;
3132
use crate::utils::sigpolicy_from_opts;
3233

3334
/// Shared progress options
34-
#[derive(Debug, Parser, PartialEq, Eq)]
35+
#[derive(Debug, Clone, Parser, PartialEq, Eq, Serialize, Deserialize)]
3536
pub(crate) struct ProgressOptions {
3637
/// File descriptor number which must refer to an open pipe (anonymous or named).
3738
///
3839
/// Interactive progress will be written to this file descriptor as "JSON lines"
3940
/// format, where each value is separated by a newline.
40-
#[clap(long)]
41+
#[clap(long, conflicts_with = "progress_json")]
4142
pub(crate) json_fd: Option<RawProgressFd>,
43+
44+
/// Path to a FIFO file (named pipe).
45+
///
46+
/// Interactive progress will be written to this file descriptor as "JSON lines"
47+
/// format, where each value is separated by a newline.
48+
#[clap(long, conflicts_with = "json_fd")]
49+
pub(crate) progress_json: Option<Utf8PathBuf>,
4250
}
4351

4452
impl TryFrom<ProgressOptions> for ProgressWriter {
4553
type Error = anyhow::Error;
4654

4755
fn try_from(value: ProgressOptions) -> Result<Self> {
48-
let r = value
49-
.json_fd
50-
.map(TryInto::try_into)
51-
.transpose()?
52-
.unwrap_or_default();
53-
Ok(r)
56+
if let Some(v) = value.json_fd {
57+
v.try_into()
58+
} else if let Some(v) = value.progress_json {
59+
let f = std::fs::File::options()
60+
.write(true)
61+
.open(&v)
62+
.with_context(|| format!("Opening progress json fifo {v}"))?;
63+
Ok(Sender::from_file(f)?.into())
64+
} else {
65+
Ok(Default::default())
66+
}
5467
}
5568
}
5669

lib/src/install.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use serde::{Deserialize, Serialize};
4545

4646
use self::baseline::InstallBlockDeviceOpts;
4747
use crate::boundimage::{BoundImage, ResolvedBoundImage};
48+
use crate::cli::ProgressOptions;
4849
use crate::containerenv::ContainerExecutionInfo;
4950
use crate::lsm;
5051
use crate::mount::Filesystem;
@@ -220,6 +221,10 @@ pub(crate) struct InstallToDiskOpts {
220221
#[serde(flatten)]
221222
pub(crate) config_opts: InstallConfigOpts,
222223

224+
#[clap(flatten)]
225+
#[serde(flatten)]
226+
pub(crate) progress_opts: ProgressOptions,
227+
223228
/// Instead of targeting a block device, write to a file via loopback.
224229
#[clap(long)]
225230
#[serde(default)]
@@ -299,6 +304,9 @@ pub(crate) struct InstallToFilesystemOpts {
299304

300305
#[clap(flatten)]
301306
pub(crate) config_opts: InstallConfigOpts,
307+
308+
#[clap(flatten)]
309+
pub(crate) progress_opts: ProgressOptions,
302310
}
303311

304312
#[derive(Debug, Clone, clap::Parser, PartialEq, Eq)]
@@ -316,6 +324,9 @@ pub(crate) struct InstallToExistingRootOpts {
316324
#[clap(flatten)]
317325
pub(crate) config_opts: InstallConfigOpts,
318326

327+
#[clap(flatten)]
328+
pub(crate) progress_opts: ProgressOptions,
329+
319330
/// Accept that this is a destructive action and skip a warning timer.
320331
#[clap(long)]
321332
pub(crate) acknowledge_destructive: bool,
@@ -355,6 +366,7 @@ pub(crate) struct State {
355366
pub(crate) host_is_container: bool,
356367
/// The root filesystem of the running container
357368
pub(crate) container_root: Dir,
369+
pub(crate) progress: ProgressWriter,
358370
pub(crate) tempdir: TempDir,
359371
}
360372

@@ -739,7 +751,7 @@ async fn install_container(
739751
&spec_imgref,
740752
Some(&state.target_imgref),
741753
false,
742-
ProgressWriter::default(),
754+
state.progress.clone(),
743755
)
744756
.await?;
745757
repo.set_disable_fsync(false);
@@ -1159,6 +1171,7 @@ async fn prepare_install(
11591171
config_opts: InstallConfigOpts,
11601172
source_opts: InstallSourceOpts,
11611173
target_opts: InstallTargetOpts,
1174+
progress_opts: ProgressOptions,
11621175
) -> Result<Arc<State>> {
11631176
tracing::trace!("Preparing install");
11641177
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
@@ -1273,6 +1286,8 @@ async fn prepare_install(
12731286
.map(|p| std::fs::read_to_string(p).with_context(|| format!("Reading {p}")))
12741287
.transpose()?;
12751288

1289+
let progress = progress_opts.try_into()?;
1290+
12761291
// Create our global (read-only) state which gets wrapped in an Arc
12771292
// so we can pass it to worker threads too. Right now this just
12781293
// combines our command line options along with some bind mounts from the host.
@@ -1285,6 +1300,7 @@ async fn prepare_install(
12851300
root_ssh_authorized_keys,
12861301
container_root: rootfs,
12871302
tempdir,
1303+
progress,
12881304
host_is_container,
12891305
});
12901306

@@ -1471,7 +1487,13 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
14711487
} else if !target_blockdev_meta.file_type().is_block_device() {
14721488
anyhow::bail!("Not a block device: {}", block_opts.device);
14731489
}
1474-
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts).await?;
1490+
let state = prepare_install(
1491+
opts.config_opts,
1492+
opts.source_opts,
1493+
opts.target_opts,
1494+
opts.progress_opts,
1495+
)
1496+
.await?;
14751497

14761498
// This is all blocking stuff
14771499
let (mut rootfs, loopback) = {
@@ -1652,7 +1674,13 @@ pub(crate) async fn install_to_filesystem(
16521674
// IMPORTANT: and hence anything that is done before MUST BE IDEMPOTENT.
16531675
// IMPORTANT: In practice, we should only be gathering information before this point,
16541676
// IMPORTANT: and not performing any mutations at all.
1655-
let state = prepare_install(opts.config_opts, opts.source_opts, opts.target_opts).await?;
1677+
let state = prepare_install(
1678+
opts.config_opts,
1679+
opts.source_opts,
1680+
opts.target_opts,
1681+
opts.progress_opts,
1682+
)
1683+
.await?;
16561684
// And the last bit of state here is the fsopts, which we also destructure now.
16571685
let mut fsopts = opts.filesystem_opts;
16581686

@@ -1878,6 +1906,7 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
18781906
source_opts: opts.source_opts,
18791907
target_opts: opts.target_opts,
18801908
config_opts: opts.config_opts,
1909+
progress_opts: opts.progress_opts,
18811910
};
18821911

18831912
install_to_filesystem(opts, true).await

lib/src/progress_jsonl.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! see <https://jsonlines.org/>.
33
44
use anyhow::Result;
5-
use serde::Serialize;
5+
use serde::{Deserialize, Serialize};
66
use std::borrow::Cow;
77
use std::os::fd::{FromRawFd, OwnedFd, RawFd};
88
use std::str::FromStr;
@@ -131,7 +131,8 @@ pub enum Event<'t> {
131131
},
132132
}
133133

134-
#[derive(Debug, Clone, PartialEq, Eq)]
134+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
135+
#[serde(transparent)]
135136
pub(crate) struct RawProgressFd(RawFd);
136137

137138
impl FromStr for RawProgressFd {

0 commit comments

Comments
 (0)