Skip to content

Commit

Permalink
container: Add --previous-build-manifest to cli and parse previous bu…
Browse files Browse the repository at this point in the history
…ild info

rpmostree-refts: Get rpm changelogs

Prune rpm changelogs of packages to within the last year and use it as
frequency of update for the rpm.
  • Loading branch information
RishabhSaini committed May 19, 2023
1 parent 5af658e commit 62fecea
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ nix = "0.26.1"
openssl = "0.10.49"
once_cell = "1.16.0"
os-release = "0.1.0"
ostree-ext = { git = "https://github.com/ostreedev/ostree-rs-ext.git", branch = "main"}
ostree-ext = { git = "https://github.com/ostreedev/ostree-rs-ext.git"}
paste = "1.0"
phf = { version = "0.11", features = ["macros"] }
rand = "0.8.5"
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 @@ -249,10 +251,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 @@ -877,6 +877,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
31 changes: 31 additions & 0 deletions src/libpriv/rpmostree-refts.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* Boston, MA 02111-1307, USA.
*/

#include <vector>

#include "config.h"

#include "rpmostree-refts.h"
Expand All @@ -27,6 +29,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 +125,26 @@ 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);
if (!ncnum)
ncnum = 0;

rust::Vec<uint64_t> epochs;
while (ncnum > 0)
{
uint64_t nchange_date = 0;
rpmtdNext (nchanges_date);
nchange_date = rpmtdGetNumber (nchanges_date);
epochs.push_back (nchange_date);
--ncnum;
}
retval->_changelogs = epochs;
}
else
{
Expand Down
8 changes: 8 additions & 0 deletions src/libpriv/rpmostree-refts.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <gio/gio.h>
#include <libdnf/libdnf.h>
#include <memory>
#include <vector>

G_BEGIN_DECLS

Expand All @@ -52,6 +53,7 @@ struct PackageMeta
{
uint64_t _size;
uint64_t _buildtime;
rust::Vec<uint64_t> _changelogs;
std::string _src_pkg;

uint64_t
Expand All @@ -64,6 +66,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 62fecea

Please sign in to comment.