Skip to content

Commit

Permalink
container: Add --previous-build-manifest
Browse files Browse the repository at this point in the history
Prune rpm changelogs of packages to within the last year and use it as
frequency of update for the rpm.
  • Loading branch information
RishabhSaini authored and cgwalters committed May 22, 2023
1 parent 90e27d9 commit 3106948
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 9 deletions.
9 changes: 9 additions & 0 deletions rpmostree-cxxrs.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -3226,6 +3226,15 @@ extern "C"
return (self.*buildtime$) ();
}

void
rpmostreecxx$cxxbridge1$PackageMeta$changelogs (const ::rpmostreecxx::PackageMeta &self,
::rust::Vec< ::std::uint64_t> *return$) noexcept
{
::rust::Vec< ::std::uint64_t> (::rpmostreecxx::PackageMeta::*changelogs$) () const
= &::rpmostreecxx::PackageMeta::changelogs;
new (return$)::rust::Vec< ::std::uint64_t> ((self.*changelogs$) ());
}

const ::std::string *
rpmostreecxx$cxxbridge1$PackageMeta$src_pkg (const ::rpmostreecxx::PackageMeta &self) noexcept
{
Expand Down
14 changes: 14 additions & 0 deletions rust/src/compose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use std::fs::File;
use std::io::{BufWriter, Write};
use std::process::Command;

use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -250,10 +252,22 @@ pub(crate) fn compose_image(args: Vec<String>) -> CxxResult<()> {
let target_imgref = target_imgref.to_string();

let label_args = opt.labels.into_iter().map(|v| format!("--label={v}"));
// If we have a prior build, pass its manifest to the encapsulation command to allow reuse of packing structure.
let previous_arg = previous_meta
.as_ref()
.map(|previous_meta| {
let manifest_path = tempdir.join("previous-manifest.json");
let mut f = File::create(&manifest_path).map(BufWriter::new)?;
serde_json::to_writer(&mut f, &previous_meta.manifest).map_err(anyhow::Error::new)?;
f.flush()?;
anyhow::Ok(format!("--previous-build-manifest={manifest_path}"))
})
.transpose()?;

let s = self_command()
.args(&["compose", "container-encapsulate"])
.args(label_args)
.args(previous_arg)
.args(&[
"--repo",
repo.as_str(),
Expand Down
60 changes: 51 additions & 9 deletions rust/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use ostree_ext::objectsource::{
ContentID, ObjectMeta, ObjectMetaMap, ObjectMetaSet, ObjectSourceMeta,
};
use ostree_ext::prelude::*;
use ostree_ext::{gio, ostree};
use ostree_ext::{gio, oci_spec, ostree};

use crate::cxxrsutil::FFIGObjectReWrap;
use crate::progress::progress_task;
Expand Down Expand Up @@ -71,6 +71,11 @@ struct ContainerEncapsulateOpts {
/// Compare OCI layers of current build with another(imgref)
#[clap(name = "compare-with-build", long)]
compare_with_build: Option<String>,

/// Prevent a change in packing structure by taking a previous build metadata (oci config and
/// manifest)
#[clap(long)]
previous_build_manifest: Option<Utf8PathBuf>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -246,16 +251,18 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
skip: Default::default(),
rpmsize: Default::default(),
};
// Insert metadata for unpakaged content.
// Insert metadata for unpackaged content.
state.packagemeta.insert(ObjectSourceMeta {
identifier: Rc::clone(&state.unpackaged_id),
name: Rc::clone(&state.unpackaged_id),
srcid: Rc::clone(&state.unpackaged_id),
// Assume that content in here changes frequently.
change_time_offset: u32::MAX,
change_frequency: u32::MAX,
});

let mut lowest_change_time = None;
let mut highest_change_time = None;
let mut package_meta = HashMap::new();
for pkg in pkglist.iter() {
let name = pkg.child_value(0);
Expand All @@ -271,13 +278,22 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
} else {
lowest_change_time = Some((Rc::clone(&nevra), pkgmeta.buildtime()))
}
if let Some(hightime) = highest_change_time.as_mut() {
if *hightime < buildtime {
*hightime = buildtime;
}
} else {
highest_change_time = Some(pkgmeta.buildtime())
}
state.rpmsize += pkgmeta.size();
package_meta.insert(nevra, pkgmeta);
}

// SAFETY: There must be at least one package.
let (lowest_change_name, lowest_change_time) =
lowest_change_time.expect("Failed to find any packages");
let highest_change_time = highest_change_time.expect("Failed to find any packages");

// Walk over the packages, and generate the `packagemeta` mapping, which is basically a subset of
// package metadata abstracted for ostree. Note that right now, the package metadata includes
// both a "unique identifer" and a "human readable name", but for rpm-ostree we're just making
Expand All @@ -292,11 +308,26 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
// Convert to hours, because there's no strong use for caring about the relative difference of builds in terms
// of minutes or seconds.
let change_time_offset = change_time_offset_secs / (60 * 60);
let changelogs = pkgmeta.changelogs();
// Ignore the updates to packages more than a year away from the latest built package as its
// contribution becomes increasingly irrelevant to the likelihood of the package updating
// in the future
// TODO: Weighted Moving Averages (Weights decaying by year) to calculate the frequency
let pruned_changelogs: Vec<&u64> = changelogs
.iter()
.filter(|e| {
let curr_build = glib::DateTime::from_unix_utc(**e as i64).unwrap();
let highest_time_build =
glib::DateTime::from_unix_utc(highest_change_time as i64).unwrap();
highest_time_build.difference(&curr_build).as_days() <= 365_i64
})
.collect();
state.packagemeta.insert(ObjectSourceMeta {
identifier: Rc::clone(nevra),
name: Rc::clone(nevra),
name: Rc::from(libdnf_sys::hy_split_nevra(&nevra)?.name),
srcid: Rc::from(pkgmeta.src_pkg().to_str().unwrap()),
change_time_offset,
change_frequency: pruned_changelogs.len() as u32,
});
}

Expand All @@ -316,14 +347,18 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
.map_err(anyhow::Error::msg)?;
let initramfs = initramfs.downcast_ref::<ostree::RepoFile>().unwrap();
let checksum = initramfs.checksum();
let name = format!("initramfs (kernel {})", kernel_ver).into_boxed_str();
let name = Rc::from(name);
state.content.insert(checksum.to_string(), Rc::clone(&name));
let name = format!("initramfs");
let identifier = format!("{} (kernel {})", name, kernel_ver).into_boxed_str();
let identifier = Rc::from(identifier);
state
.content
.insert(checksum.to_string(), Rc::clone(&identifier));
state.packagemeta.insert(ObjectSourceMeta {
identifier: Rc::clone(&name),
name: Rc::clone(&name),
srcid: Rc::clone(&name),
identifier: Rc::clone(&identifier),
name: Rc::from(name),
srcid: Rc::clone(&identifier),
change_time_offset: u32::MAX,
change_frequency: u32::MAX,
});
state.skip.insert(path);
}
Expand Down Expand Up @@ -387,6 +422,12 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
})
.collect::<Result<_>>()?;

let package_structure = opt
.previous_build_manifest
.as_ref()
.map(|p| oci_spec::image::ImageManifest::from_file(&p).map_err(anyhow::Error::new))
.transpose()?;

let mut copy_meta_keys = opt.copy_meta_keys;
// Default to copying the input hash to support cheap change detection
copy_meta_keys.push("rpmostree.inputhash".to_string());
Expand All @@ -409,6 +450,7 @@ pub fn container_encapsulate(args: Vec<String>) -> CxxResult<()> {
repo,
rev.as_str(),
&config,
package_structure.as_ref(),
Some(opts),
Some(meta),
&opt.imgref,
Expand Down
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,7 @@ pub mod ffi {
// Methods on PackageMeta
fn size(self: &PackageMeta) -> u64;
fn buildtime(self: &PackageMeta) -> u64;
fn changelogs(self: &PackageMeta) -> Vec<u64>;
fn src_pkg(self: &PackageMeta) -> &CxxString;
}

Expand Down
25 changes: 25 additions & 0 deletions src/libpriv/rpmostree-refts.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
#include <rpm/rpmtag.h>
#include <string.h>

static inline void
cleanup_rpmtdFreeData (rpmtd *tdp)
{
rpmtd td = *tdp;
if (td)
rpmtdFreeData (td);
}
#define _cleanup_rpmtddata_ __attribute__ ((cleanup (cleanup_rpmtdFreeData)))

/*
* A wrapper for an `rpmts` that supports:
*
Expand Down Expand Up @@ -114,6 +123,22 @@ RpmTs::package_meta (const rust::Str name) const
retval->_size = headerGetNumber (h, RPMTAG_LONGARCHIVESIZE);
retval->_buildtime = headerGetNumber (h, RPMTAG_BUILDTIME);
retval->_src_pkg = headerGetString (h, RPMTAG_SOURCERPM);

// Get the changelogs
struct rpmtd_s nchanges_date_s;
_cleanup_rpmtddata_ rpmtd nchanges_date = NULL;
nchanges_date = &nchanges_date_s;
headerGet (h, RPMTAG_CHANGELOGTIME, nchanges_date, HEADERGET_MINMEM);
int ncnum = rpmtdCount (nchanges_date);
rust::Vec<uint64_t> epochs;
for (int i = 0; i < ncnum; i++)
{
uint64_t nchange_date = 0;
rpmtdNext (nchanges_date);
nchange_date = rpmtdGetNumber (nchanges_date);
epochs.push_back (nchange_date);
}
retval->_changelogs = std::move (epochs);
}
else
{
Expand Down
7 changes: 7 additions & 0 deletions src/libpriv/rpmostree-refts.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct PackageMeta
{
uint64_t _size;
uint64_t _buildtime;
rust::Vec<uint64_t> _changelogs;
std::string _src_pkg;

uint64_t
Expand All @@ -64,6 +65,12 @@ struct PackageMeta
{
return _buildtime;
};
rust::Vec<uint64_t>
changelogs () const
{
return _changelogs;
};

const std::string &
src_pkg () const
{
Expand Down

0 comments on commit 3106948

Please sign in to comment.