diff --git a/ci/test-container.sh b/ci/test-container.sh index b15edc37f3..d461a56271 100755 --- a/ci/test-container.sh +++ b/ci/test-container.sh @@ -107,6 +107,14 @@ rpm -q ignition dnf -y uninstall kexec-tools if rpm -q kexec-tools; then fatal "failed to remove kexec-tools"; fi +rpm -q kernel +# Fedora doesn't ship kernel variants, so we just install some other package +rpm-ostree install --remove-installed-kernel strace +if rpm -qa |grep -E '^kernel'; then + fatal "Found installed kernel after --remove-installed-kernel" +fi +rpm -e strace + # test replacement by Koji URL rpm-ostree override replace $koji_url |& tee out.txt n_downloaded=$(grep Downloading out.txt | wc -l) diff --git a/rpmostree-cxxrs.cxx b/rpmostree-cxxrs.cxx index 0efac02db7..84c2bf4783 100644 --- a/rpmostree-cxxrs.cxx +++ b/rpmostree-cxxrs.cxx @@ -1702,6 +1702,8 @@ struct Treefile final : public ::rust::Opaque bool remove_package_override_remove (::rust::Str package) noexcept; bool has_packages_override_remove_name (::rust::Str name) const noexcept; bool remove_all_overrides () noexcept; + bool has_remove_kernel () const noexcept; + bool set_remove_kernel (bool remove) noexcept; ::rust::Vec< ::rust::String> get_modules_enable () const noexcept; bool has_modules_enable () const noexcept; ::rust::Vec< ::rust::String> get_modules_install () const noexcept; @@ -2480,6 +2482,12 @@ extern "C" bool rpmostreecxx$cxxbridge1$Treefile$remove_all_overrides (::rpmostreecxx::Treefile &self) noexcept; + bool rpmostreecxx$cxxbridge1$Treefile$has_remove_kernel ( + const ::rpmostreecxx::Treefile &self) noexcept; + + bool rpmostreecxx$cxxbridge1$Treefile$set_remove_kernel (::rpmostreecxx::Treefile &self, + bool remove) noexcept; + void rpmostreecxx$cxxbridge1$Treefile$get_modules_enable ( const ::rpmostreecxx::Treefile &self, ::rust::Vec< ::rust::String> *return$) noexcept; @@ -4877,6 +4885,18 @@ Treefile::remove_all_overrides () noexcept return rpmostreecxx$cxxbridge1$Treefile$remove_all_overrides (*this); } +bool +Treefile::has_remove_kernel () const noexcept +{ + return rpmostreecxx$cxxbridge1$Treefile$has_remove_kernel (*this); +} + +bool +Treefile::set_remove_kernel (bool remove) noexcept +{ + return rpmostreecxx$cxxbridge1$Treefile$set_remove_kernel (*this, remove); +} + ::rust::Vec< ::rust::String> Treefile::get_modules_enable () const noexcept { diff --git a/rpmostree-cxxrs.h b/rpmostree-cxxrs.h index a07f0751cb..b4d644f2de 100644 --- a/rpmostree-cxxrs.h +++ b/rpmostree-cxxrs.h @@ -1484,6 +1484,8 @@ struct Treefile final : public ::rust::Opaque bool remove_package_override_remove (::rust::Str package) noexcept; bool has_packages_override_remove_name (::rust::Str name) const noexcept; bool remove_all_overrides () noexcept; + bool has_remove_kernel () const noexcept; + bool set_remove_kernel (bool remove) noexcept; ::rust::Vec< ::rust::String> get_modules_enable () const noexcept; bool has_modules_enable () const noexcept; ::rust::Vec< ::rust::String> get_modules_install () const noexcept; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 134e18d957..5a28168194 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -559,6 +559,8 @@ pub mod ffi { fn remove_package_override_remove(&mut self, package: &str) -> bool; fn has_packages_override_remove_name(&self, name: &str) -> bool; fn remove_all_overrides(&mut self) -> bool; + fn has_remove_kernel(&self) -> bool; + fn set_remove_kernel(&mut self, remove: bool) -> bool; fn get_modules_enable(&self) -> Vec; fn has_modules_enable(&self) -> bool; fn get_modules_install(&self) -> Vec; diff --git a/rust/src/treefile.rs b/rust/src/treefile.rs index b80ea5cada..bacad0fae6 100644 --- a/rust/src/treefile.rs +++ b/rust/src/treefile.rs @@ -486,6 +486,10 @@ fn treefile_merge(dest: &mut TreeComposeConfig, src: &mut TreeComposeConfig) { &mut dest.derive.override_replace, &mut src.derive.override_replace, ); + merge_basic_field( + &mut dest.derive.override_remove_kernel, + &mut src.derive.override_remove_kernel, + ); dest.handle_repo_packages_override_replacements(); merge_map_field( &mut dest.derive.override_replace_local, @@ -1024,6 +1028,15 @@ impl Treefile { .unwrap_or_default() } + /// Kernel removal is configured + pub(crate) fn has_remove_kernel(&self) -> bool { + self.parsed + .derive + .override_remove_kernel + .clone() + .unwrap_or_default() + } + // Check that the same overrides don't already exist. Of course, in the local replace // case, this doesn't catch same pkg name but different EVRA; we'll just barf at that // later on in the core. This is an early easy sanity check. @@ -1119,6 +1132,21 @@ impl Treefile { Some(old_map) != self.parsed.derive.override_replace } + /// Change the state of kernel removal for derived images + pub(crate) fn set_remove_kernel(&mut self, remove: bool) -> bool { + let current = self + .parsed + .derive + .override_remove_kernel + .take() + .unwrap_or_default(); + // Note we did .take() above, so this also canonicalizes false -> None + if remove { + self.parsed.derive.override_remove_kernel = Some(true); + } + current != remove + } + pub(crate) fn remove_package_override_replace(&mut self, package: &str) -> bool { self.parsed .derive @@ -1180,6 +1208,7 @@ impl Treefile { .map(|x| !x.is_empty()) .unwrap_or_default() || changed; + changed = self.set_remove_kernel(false) || changed; changed } @@ -1600,6 +1629,7 @@ impl Treefile { clone.override_replace.take(); clone.override_remove.take(); clone.override_replace_local.take(); + clone.override_remove_kernel.take(); if clone != Default::default() { let j = serde_json::to_string_pretty(&clone)?; bail!( @@ -1873,6 +1903,12 @@ impl Treefile { .as_ref() .and_then(|m| m.install.as_ref().map(|i| !i.is_empty())) .unwrap_or_default() + || self + .parsed + .derive + .override_remove_kernel + .clone() + .unwrap_or_default() } /// Derive RPM importer flags for a given package and treefile settings. @@ -2693,6 +2729,9 @@ pub(crate) struct DeriveConfigFields { pub(crate) packages_local_fileoverride: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) override_remove: Option>, + /// Remove the kernel + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) override_remove_kernel: Option, #[serde(rename = "ex-override-replace")] #[serde(skip_serializing_if = "Option::is_none")] pub(crate) override_replace: Option>, diff --git a/src/app/rpmostree-pkg-builtins.cxx b/src/app/rpmostree-pkg-builtins.cxx index e753b50cee..80729e94c0 100644 --- a/src/app/rpmostree-pkg-builtins.cxx +++ b/src/app/rpmostree-pkg-builtins.cxx @@ -38,6 +38,7 @@ static gboolean opt_apply_live; static gboolean opt_idempotent; static gchar **opt_install; static gboolean opt_assumeyes; +static gboolean opt_remove_kernel; static gchar **opt_uninstall; static gboolean opt_cache_only; static gboolean opt_download_only; @@ -81,6 +82,9 @@ static GOptionEntry install_option_entry[] "Just download latest ostree and RPM data, don't deploy", NULL }, { "apply-live", 'A', 0, G_OPTION_ARG_NONE, &opt_apply_live, "Apply changes to both pending deployment and running filesystem tree", NULL }, + { "remove-installed-kernel", 0, 0, G_OPTION_ARG_NONE, &opt_remove_kernel, + "Remove the installed kernel packages (usually helpful to install a different kernel)", + NULL }, { "force-replacefiles", 0, 0, G_OPTION_ARG_NONE, &opt_force_replacefiles, "Allow package to replace files from other packages", NULL }, { NULL } }; @@ -236,6 +240,8 @@ rpmostree_builtin_install (int argc, char **argv, RpmOstreeCommandInvocation *in CXX_TRY (treefile->add_packages (v, true), error); } } + if (opt_remove_kernel) + treefile->set_remove_kernel (true); return rpmostree_container_rebuild (*treefile, cancellable, error); } diff --git a/src/libpriv/rpmostree-core.cxx b/src/libpriv/rpmostree-core.cxx index 55aff33d00..47d98fdade 100644 --- a/src/libpriv/rpmostree-core.cxx +++ b/src/libpriv/rpmostree-core.cxx @@ -54,6 +54,15 @@ #define RPMOSTREE_MESSAGE_PKG_IMPORT \ SD_ID128_MAKE (df, 8b, b5, 4f, 04, fa, 47, 08, ac, 16, 11, 1b, bf, 4b, a3, 52) +// This is a copy of the defaults from libdnf because it's not public API +// see INSTALLONLYPKGS there. +static const char *KERNEL_PACKAGES[] = { "kernel", + "kernel-PAE", + "installonlypkg(kernel)", + "installonlypkg(kernel-module)", + "installonlypkg(vm)", + "multiversion(kernel)" }; + static OstreeRepo *get_pkgcache_repo (RpmOstreeContext *self); static int @@ -1734,6 +1743,7 @@ rpmostree_context_prepare (RpmOstreeContext *self, GCancellable *cancellable, GE auto exclude_packages = self->treefile_rs->get_exclude_packages (); auto modules_enable = self->treefile_rs->get_modules_enable (); auto modules_install = self->treefile_rs->get_modules_install (); + auto remove_kernel = self->treefile_rs->has_remove_kernel (); /* we only support pure installs for now (compose case) */ if (self->lockfile) @@ -1945,6 +1955,25 @@ rpmostree_context_prepare (RpmOstreeContext *self, GCancellable *cancellable, GE g_ptr_array_add (removed_pkgnames, (gpointer)pkgname); } + if (remove_kernel) + { + for (guint i = 0; i < G_N_ELEMENTS (KERNEL_PACKAGES); i++) + { + const char *pkg = KERNEL_PACKAGES[i]; + hy_autoquery HyQuery query = hy_query_create (sack); + hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME); + hy_query_filter (query, HY_PKG_PROVIDES, HY_EQ, pkg); + g_autoptr (GPtrArray) packages = hy_query_run (query); + for (guint i = 0; i < packages->len; i++) + { + auto pkg = static_cast (g_ptr_array_index (packages, i)); + g_printerr ("Removing kernel pkg: %s\n", dnf_package_get_name (pkg)); + g_ptr_array_add (removed_pkgnames, (gpointer)dnf_package_get_name (pkg)); + hy_goal_erase (goal, pkg); + } + } + } + /* Then, handle local packages to install */ GLNX_HASH_TABLE_FOREACH_V (local_pkgs_to_install, DnfPackage *, pkg) hy_goal_install (goal, pkg);