admin prune: New builtin for cleaning up deployments and repo

After a while of pull-deploy cycles, you start to accumulate a lot of
them.  While the deployment read-only part is hardlinked, the -etc
space adds up.

Additionally, the repository itself just gets large.

The new command "ostree admin prune" deletes everything except the
"current" and "previous" deployments.
This commit is contained in:
Colin Walters 2012-11-16 17:41:46 -05:00
parent 05895780a3
commit 3f4d223361
6 changed files with 207 additions and 0 deletions

View File

@ -49,6 +49,7 @@ ostree_SOURCES += \
src/ostree/ot-admin-builtin-init.c \
src/ostree/ot-admin-builtin-diff.c \
src/ostree/ot-admin-builtin-deploy.c \
src/ostree/ot-admin-builtin-prune.c \
src/ostree/ot-admin-builtin-pull-deploy.c \
src/ostree/ot-admin-builtin-update-kernel.c \
src/ostree/ot-admin-builtins.h \

View File

@ -0,0 +1,179 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2012 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Author: Colin Walters <walters@verbum.org>
*/
#include "config.h"
#include "ot-admin-builtins.h"
#include "ot-admin-functions.h"
#include "ostree.h"
#include <glib/gi18n.h>
static gboolean opt_no_repo_prune;
static GOptionEntry options[] = {
{ "no-repo-prune", 0, 0, G_OPTION_ARG_NONE, &opt_no_repo_prune, "Only prune deployment checkouts; don't prune repository", NULL },
{ NULL }
};
static gboolean
list_deployments (GFile *from_dir,
GPtrArray *inout_deployments,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
GError *temp_error = NULL;
ot_lobj GFileEnumerator *dir_enum = NULL;
ot_lobj GFileInfo *file_info = NULL;
dir_enum = g_file_enumerate_children (from_dir, OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL, error);
if (!dir_enum)
goto out;
while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, error)) != NULL)
{
const char *name;
ot_lobj GFile *child = NULL;
ot_lobj GFile *possible_etc = NULL;
ot_lobj GFile *possible_usr = NULL;
name = g_file_info_get_name (file_info);
if (g_str_has_suffix (name, "-etc"))
goto next;
if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
goto next;
child = g_file_get_child (from_dir, name);
possible_etc = ot_gfile_get_child_strconcat (from_dir, name, "-etc", NULL);
/* Bit of a hack... */
possible_usr = g_file_get_child (child, "usr");
if (g_file_query_exists (possible_etc, cancellable))
g_ptr_array_add (inout_deployments, g_file_get_child (from_dir, name));
else if (g_file_query_exists (possible_usr, cancellable))
goto next;
else
{
if (!list_deployments (child, inout_deployments,
cancellable, error))
goto out;
}
next:
g_clear_object (&file_info);
}
if (temp_error != NULL)
{
g_propagate_error (error, temp_error);
goto out;
}
ret = TRUE;
out:
return ret;
}
gboolean
ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error)
{
GOptionContext *context;
gboolean ret = FALSE;
guint i;
ot_lobj GFile *repo_path = NULL;
ot_lobj GFile *current_deployment = NULL;
ot_lobj GFile *previous_deployment = NULL;
ot_lptrarray GPtrArray *deployments = NULL;
__attribute__((unused)) GCancellable *cancellable = NULL;
context = g_option_context_new ("- Delete untagged deployments and repository objects");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
if (!ot_admin_ensure_initialized (ostree_dir, cancellable, error))
goto out;
deployments = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
if (!list_deployments (ostree_dir, deployments, cancellable, error))
goto out;
if (!ot_admin_get_current_deployment (ostree_dir, &current_deployment,
cancellable, error));
if (!ot_admin_get_previous_deployment (ostree_dir, &previous_deployment,
cancellable, error));
for (i = 0; i < deployments->len; i++)
{
GFile *deployment = deployments->pdata[i];
ot_lobj GFile *deployment_etc = NULL;
ot_lobj GFile *parent = NULL;
if ((current_deployment && g_file_equal (deployment, current_deployment))
|| (previous_deployment && g_file_equal (deployment, previous_deployment)))
continue;
parent = g_file_get_parent (deployment);
deployment_etc = ot_gfile_get_child_strconcat (parent, ot_gfile_get_basename_cached (deployment),
"-etc", NULL);
g_print ("Deleting deployment %s\n", ot_gfile_get_path_cached (deployment));
if (!ot_gio_shutil_rm_rf (deployment, cancellable, error))
goto out;
/* Note - not atomic; we may be leaving the -etc directory around
* if this fails in the middle =/
*/
if (!ot_gio_shutil_rm_rf (deployment_etc, cancellable, error))
goto out;
}
repo_path = g_file_get_child (ostree_dir, "repo");
if (!opt_no_repo_prune)
{
ot_lptrarray GPtrArray *prune_argv = NULL;
ot_lfree char *repo_arg = NULL;
repo_arg = g_strconcat ("--repo=", ot_gfile_get_path_cached (repo_path), NULL);
prune_argv = g_ptr_array_new ();
ot_ptrarray_add_many (prune_argv, "ostree", repo_arg, "prune", "--refs-only", "--depth=0", NULL);
g_ptr_array_add (prune_argv, NULL);
if (!ot_spawn_sync_checked (ot_gfile_get_path_cached (ostree_dir),
(char**)prune_argv->pdata, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, NULL, error))
goto out;
}
ret = TRUE;
out:
if (context)
g_option_context_free (context);
return ret;
}

View File

@ -29,6 +29,7 @@ G_BEGIN_DECLS
gboolean ot_admin_builtin_init (int argc, char **argv, GFile *ostree_dir, GError **error);
gboolean ot_admin_builtin_deploy (int argc, char **argv, GFile *ostree_dir, GError **error);
gboolean ot_admin_builtin_prune (int argc, char **argv, GFile *ostree_dir, GError **error);
gboolean ot_admin_builtin_pull_deploy (int argc, char **argv, GFile *ostree_dir, GError **error);
gboolean ot_admin_builtin_diff (int argc, char **argv, GFile *ostree_dir, GError **error);
gboolean ot_admin_builtin_update_kernel (int argc, char **argv, GFile *ostree_dir, GError **error);

View File

@ -194,3 +194,24 @@ ot_admin_get_current_deployment (GFile *ostree_dir,
return query_symlink_target_allow_noent (current_path, out_deployment,
cancellable, error);
}
/**
* ot_admin_get_previous_deployment:
*
* Returns in @out_deployment the full file path of the current
* deployment that the /ostree/previous symbolic link points to, or
* %NULL if none.
*/
gboolean
ot_admin_get_previous_deployment (GFile *ostree_dir,
GFile **out_deployment,
GCancellable *cancellable,
GError **error)
{
ot_lobj GFile *previous_path = NULL;
previous_path = g_file_get_child (ostree_dir, "previous");
return query_symlink_target_allow_noent (previous_path, out_deployment,
cancellable, error);
}

View File

@ -35,6 +35,10 @@ gboolean ot_admin_get_current_deployment (GFile *ostree_dir,
GFile **out_deployment,
GCancellable *cancellable,
GError **error);
gboolean ot_admin_get_previous_deployment (GFile *ostree_dir,
GFile **out_deployment,
GCancellable *cancellable,
GError **error);
G_END_DECLS

View File

@ -46,6 +46,7 @@ static OstreeAdminCommand admin_subcommands[] = {
{ "init", ot_admin_builtin_init },
{ "deploy", ot_admin_builtin_deploy },
{ "pull-deploy", ot_admin_builtin_pull_deploy },
{ "prune", ot_admin_builtin_prune },
{ "update-kernel", ot_admin_builtin_update_kernel },
{ "config-diff", ot_admin_builtin_diff },
{ NULL, NULL }