Skip to content

Commit 2c4ca36

Browse files
authored
Merge pull request #655 from cgwalters/install-progress
install: Disable fsync() in repo when pulling && improved pull progress
2 parents 9459a66 + d8b5df2 commit 2c4ca36

File tree

3 files changed

+93
-31
lines changed

3 files changed

+93
-31
lines changed

lib/src/cli.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
540540
}
541541
}
542542
} else {
543-
let fetched = crate::deploy::pull(sysroot, imgref, opts.quiet).await?;
543+
let fetched = crate::deploy::pull(repo, imgref, opts.quiet).await?;
544544
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;
545545
let staged_digest = staged_image.as_ref().map(|s| s.image_digest.as_str());
546546
let fetched_digest = fetched.manifest_digest.as_str();
@@ -637,7 +637,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
637637
}
638638
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;
639639

640-
let fetched = crate::deploy::pull(sysroot, &target, opts.quiet).await?;
640+
let fetched = crate::deploy::pull(repo, &target, opts.quiet).await?;
641641
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;
642642

643643
if !opts.retain {
@@ -671,6 +671,8 @@ async fn rollback(_opts: RollbackOpts) -> Result<()> {
671671
#[context("Editing spec")]
672672
async fn edit(opts: EditOpts) -> Result<()> {
673673
let sysroot = &get_locked_sysroot().await?;
674+
let repo = &sysroot.repo();
675+
674676
let (booted_deployment, _deployments, host) =
675677
crate::status::get_status_require_booted(sysroot)?;
676678
let new_host: Host = if let Some(filename) = opts.filename {
@@ -697,8 +699,7 @@ async fn edit(opts: EditOpts) -> Result<()> {
697699
return crate::deploy::rollback(sysroot).await;
698700
}
699701

700-
let fetched = crate::deploy::pull(sysroot, new_spec.image, opts.quiet).await?;
701-
let repo = &sysroot.repo();
702+
let fetched = crate::deploy::pull(repo, new_spec.image, opts.quiet).await?;
702703
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;
703704

704705
// TODO gc old layers here

lib/src/deploy.rs

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use fn_error_context::context;
1313
use ostree::{gio, glib};
1414
use ostree_container::OstreeImageReference;
1515
use ostree_ext::container as ostree_container;
16-
use ostree_ext::container::store::PrepareResult;
16+
use ostree_ext::container::store::{ImportProgress, PrepareResult};
17+
use ostree_ext::oci_spec::image::Descriptor;
1718
use ostree_ext::ostree;
1819
use ostree_ext::ostree::Deployment;
1920
use ostree_ext::sysroot::SysrootLock;
@@ -112,34 +113,76 @@ pub(crate) fn check_bootc_label(config: &ostree_ext::oci_spec::image::ImageConfi
112113
}
113114
}
114115

116+
fn descriptor_of_progress(p: &ImportProgress) -> &Descriptor {
117+
match p {
118+
ImportProgress::OstreeChunkStarted(l) => l,
119+
ImportProgress::OstreeChunkCompleted(l) => l,
120+
ImportProgress::DerivedLayerStarted(l) => l,
121+
ImportProgress::DerivedLayerCompleted(l) => l,
122+
}
123+
}
124+
125+
fn prefix_of_progress(p: &ImportProgress) -> &'static str {
126+
match p {
127+
ImportProgress::OstreeChunkStarted(_) | ImportProgress::OstreeChunkCompleted(_) => {
128+
"ostree chunk"
129+
}
130+
ImportProgress::DerivedLayerStarted(_) | ImportProgress::DerivedLayerCompleted(_) => {
131+
"layer"
132+
}
133+
}
134+
}
135+
115136
/// Write container fetch progress to standard output.
116137
async fn handle_layer_progress_print(
117138
mut layers: tokio::sync::mpsc::Receiver<ostree_container::store::ImportProgress>,
118139
mut layer_bytes: tokio::sync::watch::Receiver<Option<ostree_container::store::LayerProgress>>,
119-
total_layers: usize,
120-
n_layers_fetched: &mut usize,
140+
n_layers_to_fetch: usize,
121141
) {
122-
let style = indicatif::ProgressStyle::default_bar();
123-
let pb = indicatif::ProgressBar::new(100);
124-
pb.set_style(
125-
style
126-
.template("{prefix} {bytes} [{bar:20}] ({eta}) {msg}")
142+
let start = std::time::Instant::now();
143+
let mut total_read = 0u64;
144+
let bar = indicatif::MultiProgress::new();
145+
let layers_bar = bar.add(indicatif::ProgressBar::new(
146+
n_layers_to_fetch.try_into().unwrap(),
147+
));
148+
let byte_bar = bar.add(indicatif::ProgressBar::new(0));
149+
// let byte_bar = indicatif::ProgressBar::new(0);
150+
// byte_bar.set_draw_target(indicatif::ProgressDrawTarget::hidden());
151+
layers_bar.set_style(
152+
indicatif::ProgressStyle::default_bar()
153+
.template("{prefix} {bar} {pos}/{len} {wide_msg}")
127154
.unwrap(),
128155
);
156+
layers_bar.set_prefix("Fetching layers");
157+
layers_bar.set_message("");
158+
byte_bar.set_prefix("Fetching");
159+
byte_bar.set_style(
160+
indicatif::ProgressStyle::default_bar()
161+
.template(
162+
" └ {prefix} {bar} {binary_bytes}/{binary_total_bytes} ({binary_bytes_per_sec}) {wide_msg}",
163+
)
164+
.unwrap()
165+
);
129166
loop {
130167
tokio::select! {
131168
// Always handle layer changes first.
132169
biased;
133170
layer = layers.recv() => {
134171
if let Some(l) = layer {
172+
let layer = descriptor_of_progress(&l);
173+
let layer_size = u64::try_from(layer.size()).unwrap();
135174
if l.is_starting() {
136-
pb.set_position(0);
175+
byte_bar.reset_elapsed();
176+
byte_bar.reset_eta();
177+
byte_bar.set_length(layer_size);
178+
let layer_type = prefix_of_progress(&l);
179+
let short_digest = &layer.digest()[0..21];
180+
byte_bar.set_message(format!("{layer_type} {short_digest}"));
137181
} else {
138-
pb.finish();
139-
*n_layers_fetched += 1;
182+
byte_bar.set_position(layer_size);
183+
layers_bar.inc(1);
184+
total_read = total_read.saturating_add(layer_size);
140185
}
141-
pb.set_prefix(format!("[{}/{}]", *n_layers_fetched, total_layers));
142-
pb.set_message(ostree_ext::cli::layer_progress_format(&l));
143186
} else {
144187
// If the receiver is disconnected, then we're done
145188
break
@@ -152,23 +195,35 @@ async fn handle_layer_progress_print(
152195
}
153196
let bytes = layer_bytes.borrow();
154197
if let Some(bytes) = &*bytes {
155-
pb.set_length(bytes.total);
156-
pb.set_position(bytes.fetched);
198+
byte_bar.set_position(bytes.fetched);
157199
}
158200
}
159-
160201
}
161202
}
203+
byte_bar.finish_and_clear();
204+
layers_bar.finish_and_clear();
205+
if let Err(e) = bar.clear() {
206+
tracing::warn!("clearing bar: {e}");
207+
}
208+
let end = std::time::Instant::now();
209+
let elapsed = end.duration_since(start);
210+
let persec = total_read as f64 / elapsed.as_secs_f64();
211+
let persec = indicatif::HumanBytes(persec as u64);
212+
println!(
213+
"Fetched layers: {} in {} ({}/s)",
214+
indicatif::HumanBytes(total_read),
215+
indicatif::HumanDuration(elapsed),
216+
persec,
217+
);
162218
}
163219

164220
/// Wrapper for pulling a container image, wiring up status output.
165221
#[context("Pulling")]
166222
pub(crate) async fn pull(
167-
sysroot: &SysrootLock,
223+
repo: &ostree::Repo,
168224
imgref: &ImageReference,
169225
quiet: bool,
170226
) -> Result<Box<ImageState>> {
171-
let repo = &sysroot.repo();
172227
let ostree_imgref = &OstreeImageReference::from(imgref.clone());
173228
let mut imp = new_importer(repo, ostree_imgref).await?;
174229
let prep = match imp.prepare().await? {
@@ -183,19 +238,14 @@ pub(crate) async fn pull(
183238
ostree_ext::cli::print_deprecated_warning(warning).await;
184239
}
185240
ostree_ext::cli::print_layer_status(&prep);
241+
let layers_to_fetch = prep.layers_to_fetch().collect::<Result<Vec<_>>>()?;
242+
let n_layers_to_fetch = layers_to_fetch.len();
186243
let printer = (!quiet).then(|| {
187244
let layer_progress = imp.request_progress();
188245
let layer_byte_progress = imp.request_layer_progress();
189-
let total_layers = prep.layers_to_fetch().count();
190-
let mut n_fetched = 0usize;
191246
tokio::task::spawn(async move {
192-
handle_layer_progress_print(
193-
layer_progress,
194-
layer_byte_progress,
195-
total_layers,
196-
&mut n_fetched,
197-
)
198-
.await
247+
handle_layer_progress_print(layer_progress, layer_byte_progress, n_layers_to_fetch)
248+
.await
199249
})
200250
});
201251
let import = imp.import(prep).await;

lib/src/install.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use serde::{Deserialize, Serialize};
4040
use self::baseline::InstallBlockDeviceOpts;
4141
use crate::containerenv::ContainerExecutionInfo;
4242
use crate::mount::Filesystem;
43+
use crate::spec::ImageReference;
4344
use crate::task::Task;
4445
use crate::utils::sigpolicy_from_opts;
4546

@@ -651,6 +652,16 @@ async fn initialize_ostree_root_from_self(
651652
imgref: src_imageref,
652653
};
653654

655+
// Pull the container image into the target root filesystem. Since this is
656+
// an install path, we don't need to fsync() individual layers.
657+
{
658+
let spec_imgref = ImageReference::from(src_imageref.clone());
659+
let repo = &sysroot.repo();
660+
repo.set_disable_fsync(true);
661+
crate::deploy::pull(repo, &spec_imgref, false).await?;
662+
repo.set_disable_fsync(false);
663+
}
664+
654665
// Load the kargs from the /usr/lib/bootc/kargs.d from the running root,
655666
// which should be the same as the filesystem we'll deploy.
656667
let kargsd = crate::kargs::get_kargs_in_root(container_rootfs, std::env::consts::ARCH)?;

0 commit comments

Comments
 (0)