Update to ostree-ext v0.1.2, add new ex-container command

This new `rpm-ostree ex-container` CLI is just code copied
from the `ostree-ext-cli container` binary code.  In the future
I may just add the CLI code as a library API too to simplify this.

For now, I don't want to try to add a new Rust CLI as an RPM
package for example.  This exposes it via rpm-ostree, and
in the future rpm-ostree may have some layering on top of this
anyways.
This commit is contained in:
Colin Walters 2021-05-11 17:57:43 -04:00
parent 38c25bfb75
commit 54a011df40
6 changed files with 147 additions and 0 deletions

2
Cargo.lock generated
View File

@ -1532,6 +1532,7 @@ dependencies = [
"env_logger", "env_logger",
"envsubst", "envsubst",
"fn-error-context", "fn-error-context",
"futures",
"gio", "gio",
"gio-sys", "gio-sys",
"glib", "glib",
@ -1566,6 +1567,7 @@ dependencies = [
"system-deps 3.1.1", "system-deps 3.1.1",
"systemd", "systemd",
"tempfile", "tempfile",
"tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]

View File

@ -35,6 +35,7 @@ cxx = "1.0.49"
envsubst = "0.2.0" envsubst = "0.2.0"
env_logger = "0.8.3" env_logger = "0.8.3"
fn-error-context = "0.1.2" fn-error-context = "0.1.2"
futures = "0.3.15"
gio = "0.9.1" gio = "0.9.1"
gio-sys = "0.10.1" gio-sys = "0.10.1"
gobject-sys = "0.10.0" gobject-sys = "0.10.0"
@ -70,6 +71,7 @@ systemd = "0.8.2"
tempfile = "3.2.0" tempfile = "3.2.0"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.2" tracing-subscriber = "0.2"
tokio = { version = "1.6.0", features = ["full"] }
[build-dependencies] [build-dependencies]
anyhow = "1.0" anyhow = "1.0"

104
rust/src/container.rs Normal file
View File

@ -0,0 +1,104 @@
//! CLI exposing `ostree-rs-ext container`
// SPDX-License-Identifier: Apache-2.0 OR MIT
use std::convert::TryInto;
use anyhow::{Context, Result};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
enum Opts {
/// Import an ostree commit embedded in a remote container image
Import {
/// Path to the repository
#[structopt(long)]
repo: String,
/// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest
imgref: String,
},
/// Print information about an exported ostree-container image.
Info {
/// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest
imgref: String,
},
/// Export an ostree commit to an OCI layout
Export {
/// Path to the repository
#[structopt(long)]
repo: String,
/// The ostree ref or commit to export
rev: String,
/// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest
imgref: String,
},
}
async fn container_import(repo: &str, imgref: &str) -> Result<()> {
let repo = &ostree::Repo::open_at(libc::AT_FDCWD, repo, gio::NONE_CANCELLABLE)?;
let imgref = imgref.try_into()?;
let (tx_progress, rx_progress) = tokio::sync::watch::channel(Default::default());
let target = indicatif::ProgressDrawTarget::stdout();
let style = indicatif::ProgressStyle::default_bar();
let pb = indicatif::ProgressBar::new_spinner();
pb.set_draw_target(target);
pb.set_style(style.template("{spinner} {prefix} {msg}"));
pb.enable_steady_tick(200);
pb.set_message("Downloading...");
let import = ostree_ext::container::import(repo, &imgref, Some(tx_progress));
tokio::pin!(import);
tokio::pin!(rx_progress);
loop {
tokio::select! {
_ = rx_progress.changed() => {
let n = rx_progress.borrow().processed_bytes;
pb.set_message(&format!("Processed: {}", indicatif::HumanBytes(n)));
}
import = &mut import => {
pb.finish();
println!("Imported: {}", import?.ostree_commit);
return Ok(())
}
}
}
}
async fn container_export(repo: &str, rev: &str, imgref: &str) -> Result<()> {
let repo = &ostree::Repo::open_at(libc::AT_FDCWD, repo, gio::NONE_CANCELLABLE)?;
let imgref = imgref.try_into()?;
let pushed = ostree_ext::container::export(repo, rev, &imgref).await?;
println!("{}", pushed);
Ok(())
}
async fn container_info(imgref: &str) -> Result<()> {
let imgref = imgref.try_into()?;
let info = ostree_ext::container::fetch_manifest_info(&imgref).await?;
println!("{} @{}", imgref, info.manifest_digest);
Ok(())
}
/// Main entrypoint for container
pub fn entrypoint(args: &[&str]) -> Result<()> {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.context("Failed to build tokio runtime")?
.block_on(async {
match Opts::from_iter(args.iter().skip(1)) {
Opts::Import { repo, imgref } => {
container_import(repo.as_str(), imgref.as_str()).await
}
Opts::Info { imgref } => container_info(imgref.as_str()).await,
Opts::Export { repo, rev, imgref } => {
container_export(repo.as_str(), rev.as_str(), imgref.as_str()).await
}
}
})?;
Ok(())
}

View File

@ -519,6 +519,7 @@ pub(crate) use bwrap::*;
mod client; mod client;
pub(crate) use client::*; pub(crate) use client::*;
mod cliwrap; mod cliwrap;
pub mod container;
pub use cliwrap::*; pub use cliwrap::*;
mod composepost; mod composepost;
pub mod countme; pub mod countme;

View File

@ -7,6 +7,7 @@ fn inner_main(args: &Vec<&str>) -> Result<()> {
match args.get(1).map(|s| *s) { match args.get(1).map(|s| *s) {
// Add custom Rust commands here, and also in `libmain.cxx` if user-visible. // Add custom Rust commands here, and also in `libmain.cxx` if user-visible.
Some("countme") => rpmostree_rust::countme::entrypoint(args), Some("countme") => rpmostree_rust::countme::entrypoint(args),
Some("ex-container") => rpmostree_rust::container::entrypoint(args),
_ => { _ => {
// Otherwise fall through to C++ main(). // Otherwise fall through to C++ main().
Ok(rpmostree_rust::ffi::rpmostree_main(&args)?) Ok(rpmostree_rust::ffi::rpmostree_main(&args)?)

View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# Copyright (C) 2021 Red Hat Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
set -euo pipefail
. ${KOLA_EXT_DATA}/libtest.sh
set -x
libtest_prepare_offline
booted_commit=$(rpm-ostree status --json | jq -r '.deployments[0].checksum')
rpm-ostree ex-container export --repo=/ostree/repo ${booted_commit} containers-storage:localhost/fcos
target_sha256=$(podman run --entrypoint bash --rm -i localhost/fcos -c 'sha256sum /usr/bin/rpm-ostree' | cut -f 1 -d ' ')
src_sha256=$(sha256sum /usr/bin/rpm-ostree | cut -f 1 -d ' ')
assert_streq "${src_sha256}" "${target_sha256}"
echo "ok container export"
# TODO test container import too, ideally via registry. But I don't want to
# make this test super flaky right now by pulling from the internet, and
# we need infrastructure to mock up serving via local registry.