From 27b34c96e2dbef5ead8d4d42807ab9d6b0a9a719 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Fri, 7 May 2021 14:57:44 -0400 Subject: [PATCH] DnfContext: make dnf_context_module_install like `dnf module install` This significantly revamps `dnf_context_module_install`. It fixes some issues noticed during review, but also aligns the logic closer to what `dnf module install` does. --- libdnf/dnf-context.cpp | 166 ++++++++++++++++++++++++++++------------- 1 file changed, 116 insertions(+), 50 deletions(-) diff --git a/libdnf/dnf-context.cpp b/libdnf/dnf-context.cpp index 1d61bae817..58f62bc231 100644 --- a/libdnf/dnf-context.cpp +++ b/libdnf/dnf-context.cpp @@ -72,6 +72,7 @@ #include "dnf-utils.h" #include "dnf-sack.h" #include "hy-query.h" +#include "hy-query-private.hpp" #include "hy-subject.h" #include "hy-selector.h" #include "dnf-repo.hpp" @@ -3448,9 +3449,8 @@ dnf_context_module_install(DnfContext * context, const char ** module_specs, GEr } std::vector> messages; - g_autoptr(GPtrArray) resolved = g_ptr_array_new_with_free_func (g_free); - - std::vector nevras_to_install; + // vec<(module spec, Nsvcap, module dict)> + std::vector, std::map>>>> all_resolved_module_dicts; for (const char ** specs = module_specs; *specs != NULL; ++specs) { auto resolved_spec = resolve_module_spec(*specs, *container); if (!resolved_spec.first) { @@ -3468,49 +3468,7 @@ dnf_context_module_install(DnfContext * context, const char ** module_specs, GEr std::make_tuple(libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC, tfm::format(_("Unable to resolve argument '%s'"), *specs), *specs)); } else { - for (auto module_dict_iter : module_dict) { - auto & stream_dict = module_dict_iter.second; - // this was checked in modify_module_dict_and_enable_stream() - assert(stream_dict.size() == 1); - for (const auto &iter : stream_dict) { - for (const auto &modpkg : iter.second) { - std::vector profiles; - if (resolved_spec.first->getProfile() != "") { - profiles = modpkg->getProfiles(resolved_spec.first->getProfile()); - if (profiles.empty()) { - throw std::runtime_error("No profile found matching " + resolved_spec.first->getProfile()); - } - } else { - profiles.push_back(modpkg->getDefaultProfile()); - } - auto nsvca = modpkg->getFullIdentifier(); - std::set pkgnames; - for (const auto &profile : profiles) { - container->install(modpkg, profile.getName()); - for (const auto &pkgname : profile.getContent()) { - pkgnames.insert(pkgname); - } - g_ptr_array_add (resolved, g_strdup_printf ("%s/%s", nsvca.c_str(), profile.getName().c_str())); - } - for (const auto &nevra : modpkg->getArtifacts()) { - int epoch; - char *name, *version, *release, *arch; - if (hy_split_nevra(nevra.c_str(), &name, &epoch, &version, &release, &arch)) { - // this really should never happen; unless the modular repodata is corrupted - g_autofree char *errmsg = g_strdup_printf (_("Failed to parse module artifact NEVRA '%s'"), nevra.c_str()); - g_set_error_literal(error, DNF_ERROR, DNF_ERROR_FAILED, errmsg); - return NULL; - } - // ignore source packages - if (g_str_equal (arch, "src") || g_str_equal (arch, "nosrc")) - continue; - if (pkgnames.count(name) != 0) { - nevras_to_install.push_back(std::string(nevra)); - } - } - } - } - } + all_resolved_module_dicts.emplace_back(make_tuple(*specs, std::move(resolved_spec.first), std::move(module_dict))); } } @@ -3529,6 +3487,118 @@ dnf_context_module_install(DnfContext * context, const char ** module_specs, GEr messages.end(),std::make_move_iterator(solver_error.begin()), std::make_move_iterator(solver_error.end())); } + // this code is based on dnf's ModuleBase.install() + + g_autoptr(GPtrArray) resolved = g_ptr_array_new_with_free_func (g_free); + for (auto it = std::make_move_iterator(all_resolved_module_dicts.begin()); + it != std::make_move_iterator(all_resolved_module_dicts.end()); it++) { + auto module_spec = std::get<0>(*it); + auto nsvcap_obj = std::get<1>(*it); + auto module_dict = std::get<2>(*it); + for (auto module_dict_iter : module_dict) { + // this was checked in modify_module_dict_and_enable_stream() + assert(module_dict_iter.second.size() == 1); + + for (auto & stream_dict_iter : module_dict_iter.second) { + try { + container->enableDependencyTree(stream_dict_iter.second); + + std::vector active_modules; + for (auto &modpkg : stream_dict_iter.second) { + if (container->isModuleActive(modpkg)) { + active_modules.push_back(modpkg); + } + } + + if (active_modules.empty()) { + throw std::runtime_error(tfm::format(_("No active module packages found for module spec '%s'"), module_spec)); + } + + auto latest = container->getLatestModule(active_modules, true); + if (latest->getRepoID() == LIBDNF_MODULE_FAIL_SAFE_REPO_NAME) { + throw std::runtime_error(tfm::format(_("Cannot install module '%s' from fail-safe repository"), latest->getFullIdentifier().c_str())); + } + + std::vector profiles; + if (nsvcap_obj->getProfile() != "") { + profiles = latest->getProfiles(nsvcap_obj->getProfile()); + if (profiles.empty()) { + throw std::runtime_error(tfm::format(_("No profile found matching '%s'"), nsvcap_obj->getProfile().c_str())); + } + } else { + profiles.push_back(latest->getDefaultProfile()); + } + + g_autoptr(GPtrArray) pkgnames = g_ptr_array_new_with_free_func (g_free); + auto nsvca = latest->getFullIdentifier(); + for (const auto &profile : profiles) { + container->install(latest, profile.getName()); + for (const auto &pkgname : profile.getContent()) { + g_ptr_array_add(pkgnames, g_strdup(pkgname.c_str())); + } + g_ptr_array_add(resolved, g_strdup_printf ("%s/%s", nsvca.c_str(), profile.getName().c_str())); + } + g_ptr_array_add(pkgnames, NULL); + + g_autoptr(GPtrArray) artifacts = g_ptr_array_new_with_free_func (g_free); + for (auto modpkg : active_modules) { + for (const auto &nevra : modpkg->getArtifacts()) { + g_ptr_array_add(artifacts, g_strdup (nevra.c_str())); + } + } + g_ptr_array_add (artifacts, NULL); + + hy_autoquery HyQuery base_nosrc_query = hy_query_create(priv->sack); + const char *src_arches[] = {"src", "nosrc", NULL}; + hy_query_filter_in(base_nosrc_query, HY_PKG_ARCH, HY_NEQ, src_arches); + + std::vector hotfixRepos; + for (unsigned int i = 0; i < priv->repos->len; i++) { + auto repo = static_cast(g_ptr_array_index(priv->repos, i)); + if (dnf_repo_get_module_hotfixes(repo)) { + hotfixRepos.push_back(dnf_repo_get_id(repo)); + } + } + hotfixRepos.push_back(nullptr); + + hy_autoquery HyQuery hotfix_query = hy_query_clone(base_nosrc_query); + hy_query_filter_in(hotfix_query, HY_PKG_NAME, HY_EQ, (const char**)pkgnames->pdata); + hy_query_filter_in(hotfix_query, HY_PKG_REPONAME, HY_EQ, (const char**)hotfixRepos.data()); + + hy_autoquery HyQuery install_base_query = hy_query_clone (base_nosrc_query); + hy_query_filter_in(install_base_query, HY_PKG_NEVRA_STRICT, HY_EQ, (const char**)artifacts->pdata); + hy_query_union(install_base_query, hotfix_query); + + for (char **it = (char**)pkgnames->pdata; it && *it; it++) { + const char *pkgname = *it; + hy_autoquery HyQuery query = hy_query_clone(install_base_query); + hy_query_filter(query, HY_PKG_NAME, HY_EQ, pkgname); + if (hy_query_is_empty(query)) { + // package can also be non-modular or part of another stream + hy_query_free(query); + query = hy_query_clone(base_nosrc_query); + hy_query_filter(query, HY_PKG_NAME, HY_EQ, pkgname); + if (hy_query_is_empty(query)) { + throw std::runtime_error(tfm::format(_("No match for package '%s' for module spec %s"), pkgname, module_spec)); + } + } + g_auto(HySelector) selector = hy_query_to_selector(query); + if (!hy_goal_install_selector(priv->goal, selector, error)) + return FALSE; + } + } catch (const libdnf::ModulePackageContainer::EnableMultipleStreamsException & exception) { + messages.emplace_back(std::make_tuple( + libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_MODIFY_MULTIPLE_TIMES_MODULE_STATE, + tfm::format(_("Problem during enablement of dependency tree for module '%1$s' stream '%2$s': %3$s"), + module_dict_iter.first, stream_dict_iter.first, exception.what()), module_spec)); + messages.emplace_back(std::make_tuple( + libdnf::ModulePackageContainer::ModuleErrorType::CANNOT_RESOLVE_MODULE_SPEC, + tfm::format(_("Unable to resolve argument '%s'"), module_spec), module_spec)); + } + } + } + } + auto errors = report_problems(messages); if (!errors.empty()) { std::string final_errmsg (_("Problems appeared for module install request:")); @@ -3539,10 +3609,6 @@ dnf_context_module_install(DnfContext * context, const char ** module_specs, GEr return NULL; } - for (const auto &nevra : nevras_to_install) { - if (!dnf_context_install (context, nevra.c_str(), error)) - return NULL; - } g_ptr_array_add (resolved, NULL); return (char**)g_ptr_array_free ((GPtrArray*)g_steal_pointer (&resolved), FALSE);