From 78fbdc692c8ea1c3f49596b0830f87391cf79047 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 16 Apr 2021 18:20:32 -0400 Subject: [PATCH] WIP: Add support for composing with modules Still pretty rough. Need to flesh out UX a bit more with client-side in mind. --- docs/treefile.md | 3 +++ libdnf | 2 +- rust/src/treefile.rs | 12 +++++++++ src/app/rpmostree-composeutil.cxx | 2 ++ src/libpriv/rpmostree-core.cxx | 41 ++++++++++++++++++++++--------- 5 files changed, 47 insertions(+), 13 deletions(-) diff --git a/docs/treefile.md b/docs/treefile.md index 1ecbf78f18..5abb665766 100644 --- a/docs/treefile.md +++ b/docs/treefile.md @@ -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. diff --git a/libdnf b/libdnf index be66d0908d..700a3d431c 160000 --- a/libdnf +++ b/libdnf @@ -1 +1 @@ -Subproject commit be66d0908d939ddaf3022c5489012bd1dbdb0075 +Subproject commit 700a3d431c3810d3dc0ce4d4d5db72eb1f38107a diff --git a/rust/src/treefile.rs b/rust/src/treefile.rs index 7f41d48e0f..7518340dd6 100644 --- a/rust/src/treefile.rs +++ b/rust/src/treefile.rs @@ -135,6 +135,11 @@ fn treefile_parse_stream( } } + // 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) @@ -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, @@ -1024,6 +1030,8 @@ pub(crate) struct TreeComposeConfig { // Core content #[serde(skip_serializing_if = "Option::is_none")] pub(crate) packages: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) modules: Option>, // Deprecated option #[serde(skip_serializing_if = "Option::is_none")] pub(crate) bootstrap_packages: Option>, @@ -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) @@ -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] diff --git a/src/app/rpmostree-composeutil.cxx b/src/app/rpmostree-composeutil.cxx index c8090df362..30c0f95109 100644 --- a/src/app/rpmostree-composeutil.cxx +++ b/src/app/rpmostree-composeutil.cxx @@ -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)) diff --git a/src/libpriv/rpmostree-core.cxx b/src/libpriv/rpmostree-core.cxx index df24b54ec8..c45413a227 100644 --- a/src/libpriv/rpmostree-core.cxx +++ b/src/libpriv/rpmostree-core.cxx @@ -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); @@ -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) @@ -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(repos->pdata[i]), TRUE); - return TRUE; } @@ -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)); @@ -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); @@ -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); @@ -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++)