Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ex rebuild: Support override-replace-query #3372

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ pub mod ffi {
fn get_checksum(&self, repo: Pin<&mut OstreeRepo>) -> Result<String>;
fn get_ostree_ref(&self) -> String;
fn get_repo_packages(&self) -> &[RepoPackage];
fn get_override_replace_query(&self) -> &[RepoPackage];
fn clear_repo_packages(&mut self);
fn prettyprint_json_stdout(&self);
fn print_deprecation_warnings(&self);
Expand Down
41 changes: 37 additions & 4 deletions rust/src/treefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,13 @@ impl Treefile {
self.parsed.repo_packages.as_deref().unwrap_or_default()
}

pub(crate) fn get_override_replace_query(&self) -> &[RepoPackage] {
self.parsed
.override_replace_query
.as_deref()
.unwrap_or_default()
}

pub(crate) fn clear_repo_packages(&mut self) {
self.parsed.repo_packages.take();
}
Expand Down Expand Up @@ -1025,7 +1032,8 @@ impl TreefileExternals {
Ok(())
}

fn assert_nonempty(&self) {
// Panic if there is externally referenced data.
fn assert_empty(&self) {
// can't use the Default trick here because we can't auto-derive Eq because of `File`
assert!(self.postprocess_script.is_none());
assert!(self.add_files.is_empty());
Expand Down Expand Up @@ -1203,16 +1211,18 @@ pub(crate) enum RpmdbBackend {
/// cases. Everything else is specific to either case and so lives in their respective flattened
/// field.
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub(crate) struct TreeComposeConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) packages: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "repo-packages")]
pub(crate) repo_packages: Option<Vec<RepoPackage>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) modules: Option<ModulesConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) cliwrap: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) override_replace_query: Option<Vec<RepoPackage>>,

#[serde(flatten)]
pub(crate) derive: DeriveConfigFields,
Expand All @@ -1221,6 +1231,20 @@ pub(crate) struct TreeComposeConfig {
pub(crate) base: BaseComposeConfigFields,
}

impl std::ops::Deref for TreeComposeConfig {
type Target = BaseComposeConfigFields;

fn deref(&self) -> &Self::Target {
&self.base
}
}

impl std::ops::DerefMut for TreeComposeConfig {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}

/// These fields are only useful when composing a new ostree commit.
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
Expand Down Expand Up @@ -1504,7 +1528,7 @@ impl TreeComposeConfig {
fn substitute_vars(mut self) -> Result<Self> {
let mut substvars: collections::HashMap<String, String> = collections::HashMap::new();
// Substitute ${basearch} and ${releasever}
if let Some(arch) = &self.base.basearch {
if let Some(arch) = &self.basearch {
substvars.insert("basearch".to_string(), arch.clone());
}
if let Some(releasever) = &self.base.releasever {
Expand Down Expand Up @@ -1809,11 +1833,18 @@ pub(crate) mod tests {
override-remove:
- foo
- bar
override-replace-query:
- repo: foo
packages:
- bar
- baz
"});
let v = treefile.derive.override_remove.unwrap();
assert_eq!(v.len(), 2);
assert_eq!(v[0], "foo");
assert_eq!(v[1], "bar");
let q = &treefile.override_replace_query.unwrap()[0];
assert_eq!(q.repo, "foo");
}

#[test]
Expand Down Expand Up @@ -2281,6 +2312,7 @@ pub(crate) fn treefile_new_compose(
pub(crate) fn treefile_new_client(filename: &str, basearch: &str) -> CxxResult<Box<Treefile>> {
let r = treefile_new(filename, basearch, -1)?;
r.error_if_base()?;
dbg!(&r);
Ok(r)
}

Expand Down Expand Up @@ -2315,7 +2347,8 @@ pub(crate) fn treefile_new_client_from_etc(basearch: &str) -> CxxResult<Box<Tree
for tf in tfs {
let new_cfg = treefile_parse_and_process(tf, basearch)?;
new_cfg.config.base.error_if_nonempty()?;
new_cfg.externals.assert_nonempty();
new_cfg.config.derive.error_if_nonempty()?;
new_cfg.externals.assert_empty();
let mut new_cfg = new_cfg.config;
treefile_merge(&mut new_cfg, &mut cfg);
cfg = new_cfg;
Expand Down
6 changes: 0 additions & 6 deletions src/libpriv/rpmostree-container.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ rpmostree_container_rebuild (rpmostreecxx::Treefile &treefile,
GCancellable *cancellable,
GError **error)
{
auto packages = treefile.get_packages();

/* for now, we only support package installs */
if (packages.empty())
return TRUE;

g_autoptr(RpmOstreeContext) ctx = rpmostree_context_new_container ();
rpmostree_context_set_treefile (ctx, treefile);

Expand Down
52 changes: 45 additions & 7 deletions src/libpriv/rpmostree-core.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,7 @@ gv_nevra_hash (gconstpointer v)
static gboolean
check_goal_solution (RpmOstreeContext *self,
GPtrArray *removed_pkgnames,
GPtrArray *replaced_pkgnames,
GPtrArray *replaced_nevras,
GError **error)
{
Expand Down Expand Up @@ -1557,7 +1558,8 @@ check_goal_solution (RpmOstreeContext *self,
auto old_pkg = static_cast<DnfPackage *>(old->pdata[0]);

/* did we expect this nevra to replace a base pkg? */
if (rpmostree_str_ptrarray_contains (replaced_nevras, nevra))
if (rpmostree_str_ptrarray_contains (replaced_nevras, nevra) ||
rpmostree_str_ptrarray_contains (replaced_pkgnames, dnf_package_get_name (pkg)))
g_hash_table_insert (self->pkgs_to_replace, gv_nevra_from_pkg (pkg),
gv_nevra_from_pkg (old_pkg));
else
Expand Down Expand Up @@ -1601,7 +1603,9 @@ check_goal_solution (RpmOstreeContext *self,
{
g_autoptr(GVariant) nevra_v = g_variant_get_child_value (newv, 0);
const char *nevra = g_variant_get_string (nevra_v, NULL);
if (!rpmostree_str_ptrarray_contains (replaced_nevras, nevra))
const char *name = NULL;
g_variant_get_child (newv, 1, "&s", &name);
if (!(rpmostree_str_ptrarray_contains (replaced_nevras, nevra) || rpmostree_str_ptrarray_contains (replaced_pkgnames, name)))
g_ptr_array_add (forbidden, g_strdup (nevra));
}

Expand Down Expand Up @@ -1997,14 +2001,50 @@ rpmostree_context_prepare (RpmOstreeContext *self,
}

/* First, handle packages to remove */
g_autoptr(GPtrArray) removed_pkgnames = g_ptr_array_new ();
g_autoptr(GPtrArray) removed_pkgnames = g_ptr_array_new_with_free_func (g_free);
for (auto &pkgname_v: packages_override_remove)
{
const char *pkgname = pkgname_v.c_str();
if (!dnf_context_remove (dnfctx, pkgname, error))
return FALSE;

g_ptr_array_add (removed_pkgnames, (gpointer)pkgname);
g_ptr_array_add (removed_pkgnames, g_strdup(pkgname));
}

// Then, repo packages to remove and replace
g_autoptr(DnfPackageSet) pinned_pkgs = dnf_packageset_new (sack);
Map *pinned_pkgs_map = dnf_packageset_get_map (pinned_pkgs);
auto override_packages_query = self->treefile_rs->get_override_replace_query();
for (auto & repo_pkg : override_packages_query)
{
auto repo = std::string(repo_pkg.get_repo());
auto pkgs = repo_pkg.get_packages();
for (auto & pkg_r : pkgs)
{
// We need to copy here because we only have an immutable reference to Rust
auto pkg = std::string(pkg_r);
const char *pkgname = pkg.c_str();
if (!dnf_context_remove (dnfctx, pkgname, error))
return FALSE;
// Then copy here *again*; TODO: switch this to a vec which owns the string
char *copied = g_strdup (pkgname);
g_ptr_array_add (replaced_pkgnames, copied);

g_auto(HySubject) subject = hy_subject_create(pkg.c_str());
/* this is basically like a slightly customized dnf_context_install() */
HyNevra nevra = NULL;
hy_autoquery HyQuery query =
hy_subject_get_best_solution(subject, sack, NULL, &nevra, FALSE, TRUE, TRUE, TRUE, FALSE);
Comment on lines +2035 to +2037
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, instead of this, I think we should be querying strictly by package name. repo-packages does it this way because it wants to provide the same semantics as packages (which supports e.g. provides and filepaths). But for overrides I think we can be stricter and require the exact package name to override from the base.

So then from the query we'll be getting specific NEVRAs we can add to replaced_nevras as usual.

hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, repo.c_str());
g_autoptr(DnfPackageSet) pset = hy_query_run_set (query);
if (dnf_packageset_count (pset) == 0)
return glnx_throw (error, "No matches for '%s' in repo '%s'", pkg.c_str(), repo.c_str());
g_auto(HySelector) selector = hy_selector_create (sack);
hy_selector_pkg_set (selector, pset);
if (!hy_goal_install_selector (goal, selector, error))
return FALSE;
map_or (pinned_pkgs_map, dnf_packageset_get_map (pset));
}
}

/* Then, handle local packages to install */
Expand All @@ -2017,8 +2057,6 @@ rpmostree_context_prepare (RpmOstreeContext *self,
return FALSE;

/* Now repo-packages */
g_autoptr(DnfPackageSet) pinned_pkgs = dnf_packageset_new (sack);
Map *pinned_pkgs_map = dnf_packageset_get_map (pinned_pkgs);
auto repo_pkgs = self->treefile_rs->get_repo_packages();
for (auto & repo_pkg : repo_pkgs)
{
Expand Down Expand Up @@ -2123,7 +2161,7 @@ rpmostree_context_prepare (RpmOstreeContext *self,
auto task = rpmostreecxx::progress_begin_task("Resolving dependencies");
/* XXX: consider a --allow-uninstall switch? */
if (!dnf_goal_depsolve (goal, actions, error) ||
!check_goal_solution (self, removed_pkgnames, replaced_nevras, error))
!check_goal_solution (self, removed_pkgnames, replaced_pkgnames, replaced_nevras, error))
return FALSE;
g_clear_pointer (&self->pkgs, (GDestroyNotify)g_ptr_array_unref);
self->pkgs = dnf_goal_get_packages (goal,
Expand Down