Skip to content

Commit

Permalink
Merge pull request #214 from cgwalters/podman-pull-prep
Browse files Browse the repository at this point in the history
Podman pull prep
  • Loading branch information
cgwalters authored Dec 4, 2023
2 parents 17d1fc7 + fde6bdd commit b101d9d
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 70 deletions.
69 changes: 9 additions & 60 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ use camino::Utf8PathBuf;
use clap::Parser;
use fn_error_context::context;
use ostree::gio;
use ostree_container::store::LayeredImageState;
use ostree_container::store::PrepareResult;
use ostree_container::OstreeImageReference;
use ostree_ext::container as ostree_container;
use ostree_ext::container::SignatureSource;
use ostree_ext::keyfileext::KeyFileExt;
Expand Down Expand Up @@ -214,57 +212,6 @@ pub(crate) async fn get_locked_sysroot() -> Result<ostree_ext::sysroot::SysrootL
Ok(sysroot)
}

/// Wrapper for pulling a container image, wiring up status output.
async fn new_importer(
repo: &ostree::Repo,
imgref: &ostree_container::OstreeImageReference,
) -> Result<ostree_container::store::ImageImporter> {
let config = Default::default();
let mut imp = ostree_container::store::ImageImporter::new(repo, imgref, config).await?;
imp.require_bootable();
Ok(imp)
}

/// Wrapper for pulling a container image, wiring up status output.
#[context("Pulling")]
async fn pull(
repo: &ostree::Repo,
imgref: &ImageReference,
quiet: bool,
) -> Result<Box<LayeredImageState>> {
let imgref = &OstreeImageReference::from(imgref.clone());
let mut imp = new_importer(repo, imgref).await?;
let prep = match imp.prepare().await? {
PrepareResult::AlreadyPresent(c) => {
println!("No changes in {} => {}", imgref, c.manifest_digest);
return Ok(c);
}
PrepareResult::Ready(p) => p,
};
if let Some(warning) = prep.deprecated_warning() {
ostree_ext::cli::print_deprecated_warning(warning).await;
}
ostree_ext::cli::print_layer_status(&prep);
let printer = (!quiet).then(|| {
let layer_progress = imp.request_progress();
let layer_byte_progress = imp.request_layer_progress();
tokio::task::spawn(async move {
ostree_ext::cli::handle_layer_progress_print(layer_progress, layer_byte_progress).await
})
});
let import = imp.import(prep).await;
if let Some(printer) = printer {
let _ = printer.await;
}
let import = import?;
if let Some(msg) =
ostree_container::store::image_filtered_content_warning(repo, &imgref.imgref)?
{
eprintln!("{msg}")
}
Ok(import)
}

#[context("Querying root privilege")]
pub(crate) fn require_root() -> Result<()> {
let uid = rustix::process::getuid();
Expand Down Expand Up @@ -327,7 +274,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
let mut changed = false;
if opts.check {
let imgref = imgref.clone().into();
let mut imp = new_importer(repo, &imgref).await?;
let mut imp = crate::deploy::new_importer(repo, &imgref).await?;
match imp.prepare().await? {
PrepareResult::AlreadyPresent(_) => {
println!("No changes in: {}", imgref);
Expand All @@ -347,7 +294,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
}
}
} else {
let fetched = pull(&sysroot.repo(), imgref, opts.quiet).await?;
let fetched = crate::deploy::pull(&sysroot, imgref, opts.quiet).await?;
let staged_digest = staged_image.as_ref().map(|s| s.image_digest.as_str());
let fetched_digest = fetched.manifest_digest.as_str();
tracing::debug!("staged: {staged_digest:?}");
Expand All @@ -368,8 +315,11 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
crate::deploy::stage(sysroot, &osname, &fetched, &spec).await?;
changed = true;
if let Some(prev) = booted_image.as_ref() {
let diff = ostree_container::ManifestDiff::new(&prev.manifest, &fetched.manifest);
diff.print();
if let Some(fetched_manifest) = fetched.get_manifest(repo)? {
let diff =
ostree_container::ManifestDiff::new(&prev.manifest, &fetched_manifest);
diff.print();
}
}
}
}
Expand Down Expand Up @@ -424,7 +374,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
}
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;

let fetched = pull(repo, &target, opts.quiet).await?;
let fetched = crate::deploy::pull(sysroot, &target, opts.quiet).await?;

if !opts.retain {
// By default, we prune the previous ostree ref so it will go away after later upgrades
Expand All @@ -448,7 +398,6 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
async fn edit(opts: EditOpts) -> Result<()> {
prepare_for_write().await?;
let sysroot = &get_locked_sysroot().await?;
let repo = &sysroot.repo();
let (booted_deployment, _deployments, host) =
crate::status::get_status_require_booted(sysroot)?;
let new_host: Host = if let Some(filename) = opts.filename {
Expand All @@ -467,7 +416,7 @@ async fn edit(opts: EditOpts) -> Result<()> {
return Ok(());
}
let new_spec = RequiredHostSpec::from_spec(&new_host.spec)?;
let fetched = pull(repo, new_spec.image, opts.quiet).await?;
let fetched = crate::deploy::pull(sysroot, new_spec.image, opts.quiet).await?;

// TODO gc old layers here

Expand Down
97 changes: 87 additions & 10 deletions lib/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use anyhow::{Context, Result};

use fn_error_context::context;
use ostree::{gio, glib};
use ostree_container::store::LayeredImageState;
use ostree_container::OstreeImageReference;
use ostree_ext::container as ostree_container;
use ostree_ext::container::store::PrepareResult;
use ostree_ext::ostree;
use ostree_ext::ostree::Deployment;
use ostree_ext::sysroot::SysrootLock;
Expand All @@ -27,6 +27,13 @@ pub(crate) struct RequiredHostSpec<'a> {
pub(crate) image: &'a ImageReference,
}

/// State of a locally fetched image
pub(crate) struct ImageState {
pub(crate) manifest_digest: String,
pub(crate) version: Option<String>,
pub(crate) ostree_commit: String,
}

impl<'a> RequiredHostSpec<'a> {
/// Given a (borrowed) host specification, "unwrap" its internal
/// options, giving a spec that is required to have a base container image.
Expand All @@ -39,6 +46,81 @@ impl<'a> RequiredHostSpec<'a> {
}
}

impl From<ostree_container::store::LayeredImageState> for ImageState {
fn from(value: ostree_container::store::LayeredImageState) -> Self {
let version = value.version().map(|v| v.to_owned());
let ostree_commit = value.get_commit().to_owned();
Self {
manifest_digest: value.manifest_digest,
version,
ostree_commit,
}
}
}

impl ImageState {
/// Fetch the manifest corresponding to this image. May not be available in all backends.
pub(crate) fn get_manifest(
&self,
repo: &ostree::Repo,
) -> Result<Option<ostree_ext::oci_spec::image::ImageManifest>> {
ostree_container::store::query_image_commit(repo, &self.ostree_commit)
.map(|v| Some(v.manifest))
}
}

/// Wrapper for pulling a container image, wiring up status output.
pub(crate) async fn new_importer(
repo: &ostree::Repo,
imgref: &ostree_container::OstreeImageReference,
) -> Result<ostree_container::store::ImageImporter> {
let config = Default::default();
let mut imp = ostree_container::store::ImageImporter::new(repo, imgref, config).await?;
imp.require_bootable();
Ok(imp)
}

/// Wrapper for pulling a container image, wiring up status output.
#[context("Pulling")]
pub(crate) async fn pull(
sysroot: &SysrootLock,
imgref: &ImageReference,
quiet: bool,
) -> Result<Box<ImageState>> {
let repo = &sysroot.repo();
let imgref = &OstreeImageReference::from(imgref.clone());
let mut imp = new_importer(repo, imgref).await?;
let prep = match imp.prepare().await? {
PrepareResult::AlreadyPresent(c) => {
println!("No changes in {} => {}", imgref, c.manifest_digest);
return Ok(Box::new((*c).into()));
}
PrepareResult::Ready(p) => p,
};
if let Some(warning) = prep.deprecated_warning() {
ostree_ext::cli::print_deprecated_warning(warning).await;
}
ostree_ext::cli::print_layer_status(&prep);
let printer = (!quiet).then(|| {
let layer_progress = imp.request_progress();
let layer_byte_progress = imp.request_layer_progress();
tokio::task::spawn(async move {
ostree_ext::cli::handle_layer_progress_print(layer_progress, layer_byte_progress).await
})
});
let import = imp.import(prep).await;
if let Some(printer) = printer {
let _ = printer.await;
}
let import = import?;
if let Some(msg) =
ostree_container::store::image_filtered_content_warning(repo, &imgref.imgref)?
{
eprintln!("{msg}")
}
Ok(Box::new((*import).into()))
}

pub(crate) async fn cleanup(sysroot: &SysrootLock) -> Result<()> {
let repo = sysroot.repo();
let sysroot = sysroot.sysroot.clone();
Expand Down Expand Up @@ -90,16 +172,15 @@ async fn deploy(
sysroot: &SysrootLock,
merge_deployment: Option<&Deployment>,
stateroot: &str,
image: &LayeredImageState,
image: &ImageState,
origin: &glib::KeyFile,
) -> Result<()> {
let stateroot = Some(stateroot);
// Copy to move into thread
let base_commit = image.get_commit().to_owned();
let cancellable = gio::Cancellable::NONE;
let _new_deployment = sysroot.stage_tree_with_options(
stateroot,
&base_commit,
image.ostree_commit.as_str(),
Some(origin),
merge_deployment,
&Default::default(),
Expand All @@ -113,7 +194,7 @@ async fn deploy(
pub(crate) async fn stage(
sysroot: &SysrootLock,
stateroot: &str,
image: &LayeredImageState,
image: &ImageState,
spec: &RequiredHostSpec<'_>,
) -> Result<()> {
let merge_deployment = sysroot.merge_deployment(Some(stateroot));
Expand All @@ -134,11 +215,7 @@ pub(crate) async fn stage(
.await?;
crate::deploy::cleanup(sysroot).await?;
println!("Queued for next boot: {imgref}");
if let Some(version) = image
.configuration
.as_ref()
.and_then(ostree_container::version_for_config)
{
if let Some(version) = image.version.as_deref() {
println!(" Version: {version}");
}
println!(" Digest: {}", image.manifest_digest);
Expand Down

0 comments on commit b101d9d

Please sign in to comment.