upgrader: Prune pkgcache repo
Until now, we weren't pruning the pkgcache repo at all. I ran out of space in the root partition in my CAHC vagrant test box, so it's time to fix this. The basic algorithm is to walk over the full rpmdb contents of each root, generate a set of "currently referenced" cached refs, then delete any refs in the pkgcache repo which aren't included. Then, do a prune of the pkgcache repo. While we're here, factor out a `sysroot_upgrader_cleanup()` function which does all of the cleanup. The idea is at some point we need to introduce an `rpm-ostree cleanup` command or so which calls this, to handle the case where the system is interrupted post-deploy but pre-clean. Closes: https://github.com/projectatomic/rpm-ostree/issues/428 Closes: #437 Approved by: jlebon
This commit is contained in:
parent
334cec56a0
commit
2bc8d7cccb
2
TODO
2
TODO
@ -29,7 +29,7 @@ Autobuilder
|
||||
Package layering
|
||||
----------------
|
||||
|
||||
* Provide a mechanism for updating packages (& pruning older versions)
|
||||
* Provide a mechanism for updating packages
|
||||
* Support pkgs which bring their own pps
|
||||
https://github.com/projectatomic/rpm-ostree/pull/107#issuecomment-205082381
|
||||
* Support local RPMs installation (though `ostree admin unlock` makes this
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <libglnx.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
#include "rpmostreed-utils.h"
|
||||
#include "rpmostree-util.h"
|
||||
|
||||
@ -32,6 +33,8 @@
|
||||
|
||||
#include "ostree-repo.h"
|
||||
|
||||
#define RPMOSTREE_TMP_BASE_REF "rpmostree/base/tmp"
|
||||
|
||||
/**
|
||||
* SECTION:rpmostree-sysroot-upgrader
|
||||
* @title: Simple upgrade class
|
||||
@ -1226,21 +1229,19 @@ out:
|
||||
* mostly to work around ostree's auto-ref cleanup. Otherwise we might get into
|
||||
* a situation where after the origin ref is updated, we lose our parent, which
|
||||
* means that users can no longer add/delete packages on that deployment. (They
|
||||
* can always just re-pull it, but let's try to be nice). */
|
||||
* can always just re-pull it, but let's try to be nice).
|
||||
**/
|
||||
static gboolean
|
||||
generate_baselayer_refs (RpmOstreeSysrootUpgrader *self,
|
||||
OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
glnx_unref_object OstreeRepo *repo = NULL;
|
||||
g_autoptr(GHashTable) refs = NULL;
|
||||
g_autoptr(GHashTable) bases =
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
|
||||
if (!ostree_sysroot_get_repo (self->sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_repo_list_refs_ext (repo, "rpmostree/base", &refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_NONE,
|
||||
cancellable, error))
|
||||
@ -1316,6 +1317,158 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For all packages in the sack, generate a cached refspec and add it
|
||||
* to @referenced_pkgs. This is necessary to implement garbage
|
||||
* collection of layered package refs.
|
||||
*/
|
||||
static gboolean
|
||||
add_package_refs_to_set (RpmOstreeRefSack *rsack,
|
||||
GHashTable *referenced_pkgs,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GPtrArray) pkglist = NULL;
|
||||
HyQuery query = hy_query_create (rsack->sack);
|
||||
hy_query_filter (query, HY_PKG_REPONAME, HY_EQ, HY_SYSTEM_REPO_NAME);
|
||||
pkglist = hy_query_run (query);
|
||||
|
||||
/* TODO: convert this to an iterator to avoid lots of malloc */
|
||||
|
||||
if (pkglist->len == 0)
|
||||
sd_journal_print (LOG_WARNING, "Failed to find any packages in root");
|
||||
else
|
||||
{
|
||||
for (guint i = 0; i < pkglist->len; i++)
|
||||
{
|
||||
DnfPackage *pkg = pkglist->pdata[i];
|
||||
g_autofree char *pkgref = rpmostree_get_cache_branch_pkg (pkg);
|
||||
g_hash_table_add (referenced_pkgs, g_steal_pointer (&pkgref));
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Loop over all deployments, gathering all referenced NEVRAs for
|
||||
* layered packages. Then delete any cached pkg refs that aren't in
|
||||
* that set.
|
||||
*/
|
||||
static gboolean
|
||||
clean_pkgcache_orphans (RpmOstreeSysrootUpgrader *self,
|
||||
OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
glnx_unref_object OstreeRepo *pkgcache_repo = NULL;
|
||||
g_autoptr(GPtrArray) deployments =
|
||||
ostree_sysroot_get_deployments (self->sysroot);
|
||||
g_autoptr(GHashTable) current_refs = NULL;
|
||||
g_autoptr(GHashTable) referenced_pkgs = /* cache refs of packages we want to keep */
|
||||
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
||||
GHashTableIter hiter;
|
||||
gpointer hkey, hvalue;
|
||||
gint n_objects_total;
|
||||
gint n_objects_pruned;
|
||||
guint64 freed_space;
|
||||
guint n_freed = 0;
|
||||
|
||||
if (!get_pkgcache_repo (repo, &pkgcache_repo, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
for (guint i = 0; i < deployments->len; i++)
|
||||
{
|
||||
OstreeDeployment *deployment = deployments->pdata[i];
|
||||
GKeyFile *origin = ostree_deployment_get_origin (deployment);
|
||||
g_auto(GStrv) packages = NULL;
|
||||
|
||||
if (!_rpmostree_util_parse_origin (origin, NULL, &packages, error))
|
||||
return FALSE;
|
||||
|
||||
if (packages && g_strv_length (packages) > 0)
|
||||
{
|
||||
g_autoptr(RpmOstreeRefSack) rsack = NULL;
|
||||
g_autofree char *deployment_dirpath = NULL;
|
||||
|
||||
deployment_dirpath = ostree_sysroot_get_deployment_dirpath (self->sysroot, deployment);
|
||||
|
||||
/* We could do this via the commit object, but it's faster
|
||||
* to reuse the existing rpmdb checkout.
|
||||
*/
|
||||
rsack = rpmostree_get_refsack_for_root (ostree_sysroot_get_fd (self->sysroot),
|
||||
deployment_dirpath,
|
||||
cancellable, error);
|
||||
if (rsack == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!add_package_refs_to_set (rsack, referenced_pkgs,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ostree_repo_list_refs_ext (pkgcache_repo, "rpmostree/pkg", ¤t_refs,
|
||||
OSTREE_REPO_LIST_REFS_EXT_NONE,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_hash_table_iter_init (&hiter, current_refs);
|
||||
while (g_hash_table_iter_next (&hiter, &hkey, &hvalue))
|
||||
{
|
||||
const char *ref = hkey;
|
||||
if (g_hash_table_contains (referenced_pkgs, ref))
|
||||
continue;
|
||||
|
||||
if (!ostree_repo_set_ref_immediate (pkgcache_repo, NULL, ref, NULL,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
n_freed++;
|
||||
}
|
||||
|
||||
if (!ostree_repo_prune (pkgcache_repo, OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY, 0,
|
||||
&n_objects_total, &n_objects_pruned, &freed_space,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (n_freed > 0 || freed_space > 0)
|
||||
{
|
||||
char *freed_space_str = g_format_size_full (freed_space, 0);
|
||||
g_print ("Freed %u pkgcache branches: %s\n", n_freed, freed_space_str);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
sysroot_upgrader_cleanup (RpmOstreeSysrootUpgrader *self,
|
||||
OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
glnx_unref_object OstreeRepo *pkgcache_repo = NULL;
|
||||
|
||||
if (!get_pkgcache_repo (repo, &pkgcache_repo, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* regenerate the baselayer refs in case we just kicked out an ancient layered
|
||||
* deployment whose base layer is not needed anymore */
|
||||
if (!generate_baselayer_refs (self, repo, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Delete our temporary ref */
|
||||
if (!ostree_repo_set_ref_immediate (repo, NULL, RPMOSTREE_TMP_BASE_REF,
|
||||
NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* and shake it loose */
|
||||
if (!ostree_sysroot_cleanup (self->sysroot, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!clean_pkgcache_orphans (self, repo, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpmostree_sysroot_upgrader_deploy:
|
||||
* @self: Self
|
||||
@ -1331,8 +1484,11 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
const char *tmp_base_ref = "rpmostree/base/tmp";
|
||||
glnx_unref_object OstreeDeployment *new_deployment = NULL;
|
||||
glnx_unref_object OstreeRepo *repo = NULL;
|
||||
|
||||
if (!ostree_sysroot_get_repo (self->sysroot, &repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* make sure we have a known target to deploy */
|
||||
g_assert (self->new_revision);
|
||||
@ -1362,7 +1518,7 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self,
|
||||
/* Generate a temporary ref for the new deployment in case we are
|
||||
* interrupted; the base layer refs generation isn't transactional.
|
||||
*/
|
||||
if (!ostree_repo_set_ref_immediate (repo, NULL, tmp_base_ref,
|
||||
if (!ostree_repo_set_ref_immediate (repo, NULL, RPMOSTREE_TMP_BASE_REF,
|
||||
ostree_deployment_get_csum (new_deployment),
|
||||
cancellable, error))
|
||||
goto out;
|
||||
@ -1376,19 +1532,12 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self,
|
||||
|
||||
/* regenerate the baselayer refs in case we just kicked out an ancient layered
|
||||
* deployment whose base layer is not needed anymore */
|
||||
if (!generate_baselayer_refs (self, cancellable, error))
|
||||
if (!generate_baselayer_refs (self, repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* Delete our temporary ref */
|
||||
if (!ostree_repo_set_ref_immediate (repo, NULL, tmp_base_ref,
|
||||
NULL, cancellable, error))
|
||||
if (!sysroot_upgrader_cleanup (self, repo, cancellable, error))
|
||||
goto out;
|
||||
|
||||
/* and shake it loose */
|
||||
if (!ostree_sysroot_cleanup (self->sysroot, cancellable, error))
|
||||
goto out;
|
||||
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
|
Loading…
Reference in New Issue
Block a user