Skip to content

Commit

Permalink
upgrader: Prune pkgcache repo
Browse files Browse the repository at this point in the history
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: #428

Closes: #437
Approved by: jlebon
  • Loading branch information
cgwalters authored and rh-atomic-bot committed Aug 30, 2016
1 parent 334cec5 commit 2bc8d7c
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 17 deletions.
2 changes: 1 addition & 1 deletion TODO
Expand Up @@ -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
Expand Down
181 changes: 165 additions & 16 deletions src/daemon/rpmostree-sysroot-upgrader.c
Expand Up @@ -21,6 +21,7 @@
#include "config.h"

#include <libglnx.h>
#include <systemd/sd-journal.h>
#include "rpmostreed-utils.h"
#include "rpmostree-util.h"

Expand All @@ -32,6 +33,8 @@

#include "ostree-repo.h"

#define RPMOSTREE_TMP_BASE_REF "rpmostree/base/tmp"

/**
* SECTION:rpmostree-sysroot-upgrader
* @title: Simple upgrade class
Expand Down Expand Up @@ -1226,21 +1229,19 @@ overlay_packages (RpmOstreeSysrootUpgrader *self,
* 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))
Expand Down Expand Up @@ -1316,6 +1317,158 @@ generate_baselayer_refs (RpmOstreeSysrootUpgrader *self,
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", &current_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
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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))
goto out;

/* Delete our temporary ref */
if (!ostree_repo_set_ref_immediate (repo, NULL, tmp_base_ref,
NULL, cancellable, error))
if (!generate_baselayer_refs (self, repo, cancellable, error))
goto out;

/* and shake it loose */
if (!ostree_sysroot_cleanup (self->sysroot, cancellable, error))
if (!sysroot_upgrader_cleanup (self, repo, cancellable, error))
goto out;


ret = TRUE;
out:
return ret;
Expand Down

0 comments on commit 2bc8d7c

Please sign in to comment.