Skip to content

Commit

Permalink
WIP: Add support for composing with modules
Browse files Browse the repository at this point in the history
Still pretty rough. Need to flesh out UX a bit more with client-side in
mind.
  • Loading branch information
jlebon committed Apr 19, 2021
1 parent 2ad8543 commit 78fbdc6
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 13 deletions.
3 changes: 3 additions & 0 deletions docs/treefile.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ It supports the following parameters:
An example use case for this is for Fedora CoreOS, which will blacklist the `python` and `python3`
packages to ensure that nothing included in the OS starts depending on it in the future.

* `modules`: Array of strings, mandatory: Set of installed
modules in `NAME[:STREAM][/PROFILE]` format.

* `ostree-layers`: Array of strings, optional: After all packages are unpacked,
check out these OSTree refs, which must already be in the destination repository.
Any conflicts with packages will be an error.
Expand Down
2 changes: 1 addition & 1 deletion libdnf
12 changes: 12 additions & 0 deletions rust/src/treefile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ fn treefile_parse_stream<R: io::Read>(
}
}

// to be consistent, we also support whitespace-separated modules
if let Some(modules) = treefile.modules.take() {
treefile.modules = Some(whitespace_split_packages(&modules)?);
}

treefile.packages = Some(pkgs);
treefile = treefile.migrate_legacy_fields()?;
Ok(treefile)
Expand Down Expand Up @@ -362,6 +367,7 @@ fn treefile_merge(dest: &mut TreeComposeConfig, src: &mut TreeComposeConfig) {
packages,
bootstrap_packages,
exclude_packages,
modules,
ostree_layers,
ostree_override_layers,
install_langs,
Expand Down Expand Up @@ -1024,6 +1030,8 @@ pub(crate) struct TreeComposeConfig {
// Core content
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) packages: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) modules: Option<Vec<String>>,
// Deprecated option
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) bootstrap_packages: Option<Vec<String>>,
Expand Down Expand Up @@ -1250,6 +1258,9 @@ pub(crate) mod tests {
- grub2 grub2-tools
packages-s390x:
- zipl
modules:
- nodejs:15
- swig:3.0/complete sway:rolling
"#};

// This one has "comments" (hence unknown keys)
Expand All @@ -1272,6 +1283,7 @@ pub(crate) mod tests {
treefile = treefile.substitute_vars().unwrap();
assert!(treefile.treeref.unwrap() == "exampleos/x86_64/blah");
assert!(treefile.packages.unwrap().len() == 7);
assert!(treefile.modules.unwrap().len() == 3);
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions src/app/rpmostree-composeutil.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ rpmostree_composeutil_get_treespec (RpmOstreeContext *ctx,

if (!treespec_bind_array (treedata, treespec, "packages", NULL, TRUE, error))
return NULL;
if (!treespec_bind_array (treedata, treespec, "modules", NULL, FALSE, error))
return NULL;
if (!treespec_bind_array (treedata, treespec, "exclude-packages", NULL, FALSE, error))
return NULL;
if (!treespec_bind_array (treedata, treespec, "repos", NULL, FALSE, error))
Expand Down
41 changes: 29 additions & 12 deletions src/libpriv/rpmostree-core.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ rpmostree_treespec_new_from_keyfile (GKeyFile *keyfile,
#undef BIND_STRING

add_canonicalized_string_array (&builder, "packages", NULL, keyfile);
add_canonicalized_string_array (&builder, "modules", NULL, keyfile);
add_canonicalized_string_array (&builder, "exclude-packages", NULL, keyfile);
add_canonicalized_string_array (&builder, "cached-packages", NULL, keyfile);
add_canonicalized_string_array (&builder, "removed-base-packages", NULL, keyfile);
Expand Down Expand Up @@ -438,6 +439,16 @@ rpmostree_context_new_compose (int userroot_dfd,
dnf_context_set_lock_dir (ret->dnfctx, lockdir);
}

/* This normally comes from PLATFORM_ID in /etc/os-release, but obviously
* can't work if we're starting from scratch. So just synthesize it for now
* from releasever. XXX: Dig into RHEL side of this. */
auto releasever = std::string(ret->treefile_rs->get_releasever());
if (releasever.length() > 0)
{
g_autofree char *platform = g_strdup_printf ("platform:f%s", releasever.c_str());
dnf_context_set_platform_module (ret->dnfctx, platform);
}

// The ref needs special handling as it gets variable-substituted.
auto ref = ret->treefile_rs->get_ref();
if (ref.length() > 0)
Expand Down Expand Up @@ -1188,18 +1199,6 @@ rpmostree_context_download_metadata (RpmOstreeContext *self,
g_signal_handler_disconnect (hifstate, progress_sigid);
}

/* For now, we don't natively support modules. But we still want to be able to install
* modular packages if the repos are enabled, but libdnf automatically filters them out.
* So for now, let's tell libdnf that we do want to be able to see them. See:
* https://github.com/projectatomic/rpm-ostree/issues/1435 */
dnf_sack_set_module_excludes (dnf_context_get_sack (self->dnfctx), NULL);
/* And also mark all repos as hotfix repos so that we can indiscriminately cherry-pick
* from modular repos and non-modular repos alike. */
g_autoptr(GPtrArray) repos =
rpmostree_get_enabled_rpmmd_repos (self->dnfctx, DNF_REPO_ENABLED_PACKAGES);
for (guint i = 0; i < repos->len; i++)
dnf_repo_set_module_hotfixes (static_cast<DnfRepo*>(repos->pdata[i]), TRUE);

return TRUE;
}

Expand Down Expand Up @@ -1996,6 +1995,10 @@ rpmostree_context_prepare (RpmOstreeContext *self,
g_variant_dict_lookup (self->spec->dict, "exclude-packages",
"^a&s", &exclude_packages);

g_autofree char **modules = NULL;
g_assert (g_variant_dict_lookup (self->spec->dict, "modules",
"^a&s", &modules));

g_autofree char **cached_pkgnames = NULL;
g_assert (g_variant_dict_lookup (self->spec->dict, "cached-packages",
"^a&s", &cached_pkgnames));
Expand Down Expand Up @@ -2031,6 +2034,7 @@ rpmostree_context_prepare (RpmOstreeContext *self,
if (self->rojig_pure)
{
g_assert_cmpint (g_strv_length (pkgnames), ==, 0);
g_assert_cmpint (g_strv_length (modules), ==, 0);
g_assert_cmpint (g_strv_length (cached_pkgnames), ==, 0);
g_assert_cmpint (g_strv_length (cached_replace_pkgs), ==, 0);
g_assert_cmpint (g_strv_length (removed_base_pkgnames), ==, 0);
Expand All @@ -2045,6 +2049,10 @@ rpmostree_context_prepare (RpmOstreeContext *self,
g_assert_cmpint (g_strv_length (removed_base_pkgnames), ==, 0);
}

/* We only support modules on the compose side for now, so this isn't wired */
if (self->is_system)
g_assert_cmpint (g_strv_length (modules), ==, 0);

/* track cached pkgs already added to the sack so far */
g_autoptr(GHashTable) local_pkgs_to_install =
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
Expand Down Expand Up @@ -2204,6 +2212,15 @@ rpmostree_context_prepare (RpmOstreeContext *self,
GLNX_HASH_TABLE_FOREACH_V (local_pkgs_to_install, DnfPackage*, pkg)
hy_goal_install (goal, pkg);

if (g_strv_length (modules) > 0)
{
/* Instead of error code like DNF_ERROR_PACKAGE_NOT_FOUND, libdnf directly
* prints errors on stdout... so just fail. We'll need to fix libdnf
* error-handling for the client-side. */
if (!dnf_context_module_install (dnfctx, (const char**)modules, error))
return FALSE;
}

/* And finally, handle repo packages to install */
g_autoptr(GPtrArray) missing_pkgs = NULL;
for (char **it = pkgnames; it && *it; it++)
Expand Down

0 comments on commit 78fbdc6

Please sign in to comment.