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:
parent
05895780a3
commit
3f4d223361
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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, ¤t_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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Reference in New Issue