Skip to content

Commit

Permalink
DnfContext: make dnf_context_module_install like dnf module install
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jlebon committed May 7, 2021
1 parent 31e0561 commit 27b34c9
Showing 1 changed file with 116 additions and 50 deletions.
166 changes: 116 additions & 50 deletions libdnf/dnf-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3448,9 +3449,8 @@ dnf_context_module_install(DnfContext * context, const char ** module_specs, GEr
}
std::vector<std::tuple<libdnf::ModulePackageContainer::ModuleErrorType, std::string, std::string>> messages;

g_autoptr(GPtrArray) resolved = g_ptr_array_new_with_free_func (g_free);

std::vector<std::string> nevras_to_install;
// vec<(module spec, Nsvcap, module dict)>
std::vector<std::tuple<const char *, std::unique_ptr<libdnf::Nsvcap>, std::map<std::string, std::map<std::string, std::vector<libdnf::ModulePackage *>>>>> 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) {
Expand All @@ -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<libdnf::ModuleProfile> 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<std::string> 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)));
}
}

Expand All @@ -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<libdnf::ModulePackage*> 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<libdnf::ModuleProfile> 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<const char *> hotfixRepos;
for (unsigned int i = 0; i < priv->repos->len; i++) {
auto repo = static_cast<DnfRepo *>(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:"));
Expand All @@ -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);
Expand Down

0 comments on commit 27b34c9

Please sign in to comment.