override remove: allow inactive removals
The property of removal overrides dropping out if the package was removed from the base layer felt a bit too magical and hacky. We really should remember that wish and re-apply it if the pkg comes back. This is similar to package layering: requests can become inactive (seems like a better word than "dormant") if the package is already part of the base layer, but they don't really go away. This patch reworks the logic so that removal overrides work the same way. In the status output, we now have both "RemovedBasePackages" and "InactiveBaseRemovals" (which is only printed in verbose mode), similarly to how we have "LayeredPackages" and "InactiveRequests". And similarly, we also print out in the upgrader during a transaction all the inactive base removals. Another cool thing is that we now allow any pattern to be specified at the CLI. E.g. `ex override remove /usr/bin/strace` will resolve to strace. Closes: #836 Approved by: cgwalters
This commit is contained in:
parent
61560e0686
commit
0b1c5eda17
@ -215,7 +215,8 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
|
||||
g_autofree const gchar **origin_packages = NULL;
|
||||
g_autofree const gchar **origin_requested_packages = NULL;
|
||||
g_autofree const gchar **origin_requested_local_packages = NULL;
|
||||
g_autofree const gchar **origin_removed_base_packages = NULL;
|
||||
g_autofree const gchar **origin_base_removals = NULL;
|
||||
g_autofree const gchar **origin_requested_base_removals = NULL;
|
||||
const gchar *origin_refspec;
|
||||
const gchar *id;
|
||||
const gchar *os_name;
|
||||
@ -255,8 +256,10 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
|
||||
lookup_array_and_canonicalize (dict, "requested-packages");
|
||||
origin_requested_local_packages =
|
||||
lookup_array_and_canonicalize (dict, "requested-local-packages");
|
||||
origin_removed_base_packages =
|
||||
lookup_array_and_canonicalize (dict, "removed-base-packages");
|
||||
origin_base_removals =
|
||||
lookup_array_and_canonicalize (dict, "base-removals");
|
||||
origin_requested_base_removals =
|
||||
lookup_array_and_canonicalize (dict, "requested-base-removals");
|
||||
}
|
||||
else
|
||||
origin_refspec = NULL;
|
||||
@ -418,9 +421,13 @@ status_generic (RPMOSTreeSysroot *sysroot_proxy,
|
||||
}
|
||||
|
||||
/* print base overrides before overlays */
|
||||
if (origin_removed_base_packages)
|
||||
print_packages ("RemovedBasePackages", max_key_len,
|
||||
origin_removed_base_packages, NULL);
|
||||
if (origin_base_removals)
|
||||
print_packages ("RemovedBasePackages", max_key_len, origin_base_removals, NULL);
|
||||
|
||||
/* only print inactive base removal requests in verbose mode */
|
||||
if (origin_requested_base_removals && opt_verbose)
|
||||
print_packages ("InactiveBaseRemovals", max_key_len,
|
||||
origin_requested_base_removals, origin_base_removals);
|
||||
|
||||
/* only print inactive layering requests in verbose mode */
|
||||
if (origin_requested_packages && opt_verbose)
|
||||
|
@ -67,6 +67,7 @@ struct RpmOstreeSysrootUpgrader {
|
||||
RpmOstreeRefSack *rsack;
|
||||
|
||||
GPtrArray *overlay_packages; /* Finalized list of pkgs to overlay */
|
||||
GPtrArray *override_remove_packages; /* Finalized list of base pkgs to remove */
|
||||
|
||||
char *base_revision; /* Non-layered replicated commit */
|
||||
char *final_revision; /* Computed by layering; if NULL, only using base_revision */
|
||||
@ -204,6 +205,7 @@ rpmostree_sysroot_upgrader_finalize (GObject *object)
|
||||
g_free (self->final_revision);
|
||||
|
||||
g_clear_pointer (&self->overlay_packages, (GDestroyNotify)g_ptr_array_unref);
|
||||
g_clear_pointer (&self->override_remove_packages, (GDestroyNotify)g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (rpmostree_sysroot_upgrader_parent_class)->finalize (object);
|
||||
}
|
||||
@ -501,20 +503,17 @@ generate_treespec (RpmOstreeSysrootUpgrader *self)
|
||||
{
|
||||
g_autoptr(GKeyFile) treespec = g_key_file_new ();
|
||||
|
||||
GPtrArray *packages = self->overlay_packages;
|
||||
if (packages->len > 0)
|
||||
if (self->overlay_packages->len > 0)
|
||||
{
|
||||
g_key_file_set_string_list (treespec, "tree", "packages",
|
||||
(const char* const*)packages->pdata,
|
||||
packages->len);
|
||||
(const char* const*)self->overlay_packages->pdata,
|
||||
self->overlay_packages->len);
|
||||
}
|
||||
|
||||
GHashTable *local_packages =
|
||||
rpmostree_origin_get_local_packages (self->origin);
|
||||
GHashTable *local_packages = rpmostree_origin_get_local_packages (self->origin);
|
||||
if (g_hash_table_size (local_packages) > 0)
|
||||
{
|
||||
g_autoptr(GPtrArray) sha256_nevra =
|
||||
g_ptr_array_new_with_free_func (g_free);
|
||||
g_autoptr(GPtrArray) sha256_nevra = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
GLNX_HASH_TABLE_FOREACH_KV (local_packages, const char*, k, const char*, v)
|
||||
g_ptr_array_add (sha256_nevra, g_strconcat (v, ":", k, NULL));
|
||||
@ -524,16 +523,11 @@ generate_treespec (RpmOstreeSysrootUpgrader *self)
|
||||
sha256_nevra->len);
|
||||
}
|
||||
|
||||
GHashTable *overrides_remove =
|
||||
rpmostree_origin_get_overrides_remove (self->origin);
|
||||
if (g_hash_table_size (overrides_remove) > 0)
|
||||
if (self->override_remove_packages->len > 0)
|
||||
{
|
||||
g_autofree char **pkgv =
|
||||
(char**) g_hash_table_get_keys_as_array (overrides_remove, NULL);
|
||||
g_key_file_set_string_list (treespec, "tree",
|
||||
"removed-base-packages",
|
||||
(const char* const*)pkgv,
|
||||
g_strv_length (pkgv));
|
||||
g_key_file_set_string_list (treespec, "tree", "removed-base-packages",
|
||||
(const char* const*)self->override_remove_packages->pdata,
|
||||
self->override_remove_packages->len);
|
||||
}
|
||||
|
||||
return rpmostree_treespec_new_from_keyfile (treespec, NULL);
|
||||
@ -544,18 +538,10 @@ finalize_overrides (RpmOstreeSysrootUpgrader *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* Removal overrides have the magical property that they drop out if the base layer no
|
||||
* longer has them. This keeps the origin consistent. New removal overrides are checked in
|
||||
* deploy_transaction_execute() to ensure they're valid; i.e. the pkgs exist. */
|
||||
|
||||
GHashTable *removals = rpmostree_origin_get_overrides_remove (self->origin);
|
||||
g_autoptr(GPtrArray) ret_final_removals = g_ptr_array_new_with_free_func (g_free);
|
||||
|
||||
if (g_hash_table_size (removals) == 0)
|
||||
return TRUE;
|
||||
|
||||
/* NB: strings are owned by hash table */
|
||||
g_autoptr(GPtrArray) removals_to_remove = g_ptr_array_new ();
|
||||
|
||||
g_autoptr(GPtrArray) inactive_removals = g_ptr_array_new ();
|
||||
GLNX_HASH_TABLE_FOREACH (removals, const char*, pkgname)
|
||||
{
|
||||
/* only match pkgname */
|
||||
@ -563,18 +549,23 @@ finalize_overrides (RpmOstreeSysrootUpgrader *self,
|
||||
hy_query_filter (query, HY_PKG_NAME, HY_EQ, pkgname);
|
||||
g_autoptr(GPtrArray) pkgs = hy_query_run (query);
|
||||
|
||||
if (pkgs->len == 0)
|
||||
g_ptr_array_add (removals_to_remove, (gpointer)pkgname);
|
||||
if (pkgs->len > 1)
|
||||
return glnx_throw (error, "Multiple packages match \"%s\"", pkgname);
|
||||
else if (pkgs->len == 1)
|
||||
g_ptr_array_add (ret_final_removals, g_strdup (pkgname));
|
||||
else
|
||||
g_ptr_array_add (inactive_removals, (gpointer)pkgname);
|
||||
}
|
||||
|
||||
if (removals_to_remove->len > 0)
|
||||
if (inactive_removals->len > 0)
|
||||
{
|
||||
g_ptr_array_add (removals_to_remove, NULL);
|
||||
if (!rpmostree_origin_remove_overrides (self->origin,
|
||||
(char**)removals_to_remove->pdata, error))
|
||||
return FALSE;
|
||||
g_print ("Inactive base removals:\n");
|
||||
for (guint i = 0; i < inactive_removals->len; i++)
|
||||
g_print (" %s\n", (const char*)inactive_removals->pdata[i]);
|
||||
}
|
||||
|
||||
g_assert (!self->override_remove_packages);
|
||||
self->override_remove_packages = g_steal_pointer (&ret_final_removals);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -694,6 +685,7 @@ finalize_packages_to_overlay (RpmOstreeSysrootUpgrader *self,
|
||||
g_print (" %s (already provided by %s)\n", req, nevra);
|
||||
}
|
||||
|
||||
g_assert (!self->overlay_packages);
|
||||
self->overlay_packages = g_steal_pointer (&ret_missing_pkgs);
|
||||
ret = TRUE;
|
||||
out:
|
||||
@ -868,10 +860,6 @@ do_local_assembly (RpmOstreeSysrootUpgrader *self,
|
||||
static gboolean
|
||||
requires_local_assembly (RpmOstreeSysrootUpgrader *self)
|
||||
{
|
||||
GHashTable *local_pkgs = rpmostree_origin_get_local_packages (self->origin);
|
||||
GHashTable *overrides_remove =
|
||||
rpmostree_origin_get_overrides_remove (self->origin);
|
||||
|
||||
/* Now, it's possible all requested packages are in the new tree, so we have
|
||||
* another optimization here for that case. This is a bit tricky: assuming we
|
||||
* came here from an 'rpm-ostree install', this might mean that we redeploy
|
||||
@ -881,9 +869,9 @@ requires_local_assembly (RpmOstreeSysrootUpgrader *self)
|
||||
* https://github.com/projectatomic/rpm-ostree/issues/753
|
||||
*/
|
||||
|
||||
return g_hash_table_size (local_pkgs) > 0 ||
|
||||
g_hash_table_size (overrides_remove) > 0 ||
|
||||
self->overlay_packages->len > 0 ||
|
||||
return self->overlay_packages->len > 0 ||
|
||||
self->override_remove_packages->len > 0 ||
|
||||
g_hash_table_size (rpmostree_origin_get_local_packages (self->origin)) > 0 ||
|
||||
rpmostree_origin_get_regenerate_initramfs (self->origin);
|
||||
}
|
||||
|
||||
|
@ -278,9 +278,11 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot,
|
||||
rpmostree_origin_get_packages (origin));
|
||||
variant_add_from_hash_table (&dict, "requested-local-packages",
|
||||
rpmostree_origin_get_local_packages (origin));
|
||||
variant_add_from_hash_table (&dict, "requested-base-removals",
|
||||
rpmostree_origin_get_overrides_remove (origin));
|
||||
|
||||
g_variant_dict_insert (&dict, "packages", "^as", layered_pkgs ?: (char**)empty_v);
|
||||
g_variant_dict_insert (&dict, "removed-base-packages", "^as",
|
||||
g_variant_dict_insert (&dict, "base-removals", "^as",
|
||||
removed_base_pkgs ?: (char**)empty_v);
|
||||
|
||||
if (sigs != NULL)
|
||||
|
@ -719,8 +719,7 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
for (char **it = self->override_remove_pkgs; it && *it; it++)
|
||||
{
|
||||
const char *pkg = *it;
|
||||
g_autoptr(GPtrArray) pkgs =
|
||||
rpmostree_get_matching_packages (rsack->sack, pkg);
|
||||
g_autoptr(GPtrArray) pkgs = rpmostree_get_matching_packages (rsack->sack, pkg);
|
||||
|
||||
if (pkgs->len == 0)
|
||||
return glnx_throw (error, "No package \"%s\" in base commit %.7s", pkg, base);
|
||||
|
@ -2936,39 +2936,13 @@ rpmostree_context_commit_tmprootfs (RpmOstreeContext *self,
|
||||
g_assert (pkgs);
|
||||
g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.packages", pkgs);
|
||||
|
||||
/* embed the full NEVRA names of the base pkgs removed to make it easier to display
|
||||
* to users (this does not include reverse deps that were also removed -- users can
|
||||
* do `db diff` for the full list, much like regular pkglayering) */
|
||||
{
|
||||
g_autofree char **removed_base_pkgnames = NULL;
|
||||
g_assert (g_variant_dict_lookup (self->spec->dict, "removed-base-packages",
|
||||
"^a&s", &removed_base_pkgnames));
|
||||
/* NB: strings owned by solv pool */
|
||||
g_autoptr(GPtrArray) removed_base_pkgnevras = g_ptr_array_new ();
|
||||
|
||||
if (!self->empty)
|
||||
{
|
||||
g_autoptr(GPtrArray) packages =
|
||||
dnf_goal_get_packages (dnf_context_get_goal (self->hifctx),
|
||||
DNF_PACKAGE_INFO_REMOVE,
|
||||
DNF_PACKAGE_INFO_OBSOLETE, -1);
|
||||
|
||||
for (guint i = 0; i < packages->len; i++)
|
||||
{
|
||||
DnfPackage *pkg = packages->pdata[i];
|
||||
const char *name = dnf_package_get_name (pkg);
|
||||
const char *nevra = dnf_package_get_nevra (pkg);
|
||||
if (g_strv_contains ((const char*const*)removed_base_pkgnames, name))
|
||||
g_ptr_array_add (removed_base_pkgnevras, (gpointer)nevra);
|
||||
}
|
||||
}
|
||||
|
||||
g_variant_builder_add (&metadata_builder, "{sv}",
|
||||
"rpmostree.removed-base-packages",
|
||||
g_variant_new_strv (
|
||||
(const char *const*)removed_base_pkgnevras->pdata,
|
||||
removed_base_pkgnevras->len));
|
||||
}
|
||||
/* embed packages removed */
|
||||
g_autoptr(GVariant) removed_pkgs =
|
||||
g_variant_dict_lookup_value (self->spec->dict, "removed-base-packages",
|
||||
G_VARIANT_TYPE ("as"));
|
||||
g_assert (removed_pkgs);
|
||||
g_variant_builder_add (&metadata_builder, "{sv}", "rpmostree.removed-base-packages",
|
||||
removed_pkgs);
|
||||
|
||||
/* be nice to our future selves */
|
||||
g_variant_builder_add (&metadata_builder, "{sv}",
|
||||
|
@ -30,7 +30,8 @@ vm_assert_status_jq \
|
||||
'.deployments[0]["packages"]' \
|
||||
'.deployments[0]["requested-packages"]' \
|
||||
'.deployments[0]["requested-local-packages"]' \
|
||||
'.deployments[0]["removed-base-packages"]'
|
||||
'.deployments[0]["base-removals"]' \
|
||||
'.deployments[0]["requested-base-removals"]'
|
||||
echo "ok empty pkg arrays in status json"
|
||||
|
||||
# Be sure an unprivileged user exists
|
||||
|
@ -34,9 +34,12 @@ vm_assert_layered_pkg foo absent
|
||||
vm_assert_layered_pkg nonrootcap absent
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["base-checksum"]|not' \
|
||||
'.deployments[0]["pending-base-checksum"]|not'
|
||||
'.deployments[0]["pending-base-checksum"]|not' \
|
||||
'.deployments[0]["base-removals"]|length == 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 0'
|
||||
|
||||
vm_cmd ostree refs $(vm_get_booted_csum) --create vmcheck_tmp/without_foo_and_nonrootcap
|
||||
vm_cmd ostree refs $(vm_get_booted_csum) \
|
||||
--create vmcheck_tmp/without_foo_and_nonrootcap
|
||||
|
||||
# create a new branch with foo and nonrootcap already in it
|
||||
vm_rpmostree install foo nonrootcap
|
||||
@ -55,40 +58,64 @@ echo "ok setup"
|
||||
|
||||
vm_rpmostree ex override remove foo nonrootcap
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 2' \
|
||||
'.deployments[0]["removed-base-packages"]|index("foo-1.0-1.x86_64") >= 0' \
|
||||
'.deployments[0]["removed-base-packages"]|index("nonrootcap-1.0-1.x86_64") >= 0'
|
||||
'.deployments[0]["base-removals"]|length == 2' \
|
||||
'.deployments[0]["base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["base-removals"]|index("nonrootcap") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 2' \
|
||||
'.deployments[0]["requested-base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|index("nonrootcap") >= 0'
|
||||
echo "ok override remove foo and nonrootcap"
|
||||
|
||||
vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck
|
||||
vm_rpmostree upgrade
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 2' \
|
||||
'.deployments[0]["removed-base-packages"]|index("foo-1.0-1.x86_64") >= 0' \
|
||||
'.deployments[0]["removed-base-packages"]|index("nonrootcap-1.0-1.x86_64") >= 0'
|
||||
'.deployments[0]["base-removals"]|length == 2' \
|
||||
'.deployments[0]["base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["base-removals"]|index("nonrootcap") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 2' \
|
||||
'.deployments[0]["requested-base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|index("nonrootcap") >= 0'
|
||||
echo "ok override remove carried through upgrade"
|
||||
|
||||
vm_rpmostree ex override reset foo
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 1' \
|
||||
'.deployments[0]["removed-base-packages"]|index("nonrootcap-1.0-1.x86_64") >= 0'
|
||||
'.deployments[0]["base-removals"]|length == 1' \
|
||||
'.deployments[0]["base-removals"]|index("nonrootcap") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 1' \
|
||||
'.deployments[0]["requested-base-removals"]|index("nonrootcap") >= 0'
|
||||
echo "ok override reset foo"
|
||||
|
||||
vm_rpmostree ex override reset --all
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 0'
|
||||
'.deployments[0]["base-removals"]|length == 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 0'
|
||||
echo "ok override reset --all"
|
||||
|
||||
# check that upgrading to a base without foo drops the override
|
||||
# check that upgrading to a base without foo works
|
||||
vm_rpmostree ex override remove foo
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 1' \
|
||||
'.deployments[0]["removed-base-packages"]|index("foo-1.0-1.x86_64") >= 0'
|
||||
'.deployments[0]["base-removals"]|length == 1' \
|
||||
'.deployments[0]["base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 1' \
|
||||
'.deployments[0]["requested-base-removals"]|index("foo") >= 0'
|
||||
vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck_tmp/without_foo_and_nonrootcap
|
||||
vm_rpmostree upgrade
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["removed-base-packages"]|length == 0'
|
||||
echo "ok override drops out"
|
||||
'.deployments[0]["base-removals"]|length == 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 1' \
|
||||
'.deployments[0]["requested-base-removals"]|index("foo") >= 0'
|
||||
echo "ok override remove requested but not applied"
|
||||
|
||||
# check that upgrading again to a base with foo turns the override back on
|
||||
vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck_tmp/with_foo_and_nonrootcap
|
||||
vm_rpmostree upgrade
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["base-removals"]|length == 1' \
|
||||
'.deployments[0]["base-removals"]|index("foo") >= 0' \
|
||||
'.deployments[0]["requested-base-removals"]|length == 1' \
|
||||
'.deployments[0]["requested-base-removals"]|index("foo") >= 0'
|
||||
echo "ok override remove re-applied"
|
||||
vm_rpmostree cleanup -p
|
||||
|
||||
# a few error checks
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user