diff --git a/Makefile-tests.am b/Makefile-tests.am index b81866be..0b83ebea 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -29,6 +29,7 @@ testfiles = t0000-basic \ t0006-libarchive \ t0011-pull-archive-z \ t0015-admin-deploy \ + t0016-admin-deploy \ $(NULL) insttest_SCRIPTS = $(addprefix tests/,$(testfiles:=.sh)) diff --git a/src/ostree/ot-admin-cleanup.c b/src/ostree/ot-admin-cleanup.c index d647fa5e..1ca242a7 100644 --- a/src/ostree/ot-admin-cleanup.c +++ b/src/ostree/ot-admin-cleanup.c @@ -94,6 +94,7 @@ list_deployment_dirs_for_os (GFile *osdir, out: return ret; } + static gboolean list_all_deployment_directories (GFile *sysroot, GPtrArray **out_deployments, @@ -153,6 +154,100 @@ list_all_deployment_directories (GFile *sysroot, return ret; } +static gboolean +parse_bootdir_name (const char *name, + char **out_osname, + char **out_csum) +{ + const char *lastdash; + + if (out_osname) + *out_osname = NULL; + if (out_csum) + *out_csum = NULL; + + lastdash = strrchr (name, '-'); + + if (!lastdash) + return FALSE; + + if (!ostree_validate_checksum_string (lastdash + 1, NULL)) + return FALSE; + + if (out_osname) + *out_osname = g_strndup (name, lastdash - name); + if (out_csum) + *out_csum = g_strdup (lastdash + 1); + + return TRUE; +} + +static gboolean +list_all_boot_directories (GFile *sysroot, + GPtrArray **out_bootdirs, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + gs_unref_object GFileEnumerator *dir_enum = NULL; + gs_unref_object GFile *boot_ostree = NULL; + gs_unref_object GFile *osdir = NULL; + gs_unref_ptrarray GPtrArray *ret_bootdirs = NULL; + GError *temp_error = NULL; + + boot_ostree = g_file_resolve_relative_path (sysroot, "boot/ostree"); + + ret_bootdirs = g_ptr_array_new_with_free_func (g_object_unref); + + dir_enum = g_file_enumerate_children (boot_ostree, OSTREE_GIO_FAST_QUERYINFO, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, &temp_error); + if (!dir_enum) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&temp_error); + goto done; + } + else + { + g_propagate_error (error, temp_error); + goto out; + } + } + + while (TRUE) + { + GFileInfo *file_info = NULL; + GFile *child = NULL; + const char *name; + + if (!gs_file_enumerator_iterate (dir_enum, &file_info, &child, + NULL, error)) + goto out; + if (file_info == NULL) + break; + + if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) + continue; + + /* Only look at directories ending in -CHECKSUM; nothing else + * should be in here, but let's be conservative. + */ + name = g_file_info_get_name (file_info); + if (!parse_bootdir_name (name, NULL, NULL)) + continue; + + g_ptr_array_add (ret_bootdirs, g_object_ref (child)); + } + + done: + ret = TRUE; + ot_transfer_out_value (out_bootdirs, &ret_bootdirs); + out: + return ret; +} + static gboolean cleanup_other_bootversions (GFile *sysroot, int bootversion, @@ -212,20 +307,25 @@ cleanup_old_deployments (GFile *sysroot, guint i; gs_unref_object GFile *active_root = g_file_new_for_path ("/"); gs_unref_hashtable GHashTable *active_deployment_dirs = NULL; + gs_unref_hashtable GHashTable *active_boot_checksums = NULL; gs_unref_ptrarray GPtrArray *all_deployment_dirs = NULL; + gs_unref_ptrarray GPtrArray *all_boot_dirs = NULL; if (!ot_admin_util_get_devino (active_root, &root_device, &root_inode, cancellable, error)) goto out; active_deployment_dirs = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, NULL, g_object_unref); + active_boot_checksums = g_hash_table_new_full (g_str_hash, (GEqualFunc)g_str_equal, g_free, NULL); for (i = 0; i < deployments->len; i++) { OtDeployment *deployment = deployments->pdata[i]; GFile *deployment_path = ot_admin_get_deployment_directory (sysroot, deployment); + char *bootcsum = g_strdup (ot_deployment_get_bootcsum (deployment)); /* Transfer ownership */ g_hash_table_insert (active_deployment_dirs, deployment_path, deployment_path); + g_hash_table_insert (active_boot_checksums, bootcsum, bootcsum); } if (!list_all_deployment_directories (sysroot, &all_deployment_dirs, @@ -260,6 +360,28 @@ cleanup_old_deployments (GFile *sysroot, } } + if (!list_all_boot_directories (sysroot, &all_boot_dirs, + cancellable, error)) + goto out; + + for (i = 0; i < all_boot_dirs->len; i++) + { + GFile *bootdir = all_boot_dirs->pdata[i]; + gs_free char *osname = NULL; + gs_free char *bootcsum = NULL; + + if (!parse_bootdir_name (gs_file_get_basename_cached (bootdir), + &osname, &bootcsum)) + g_assert_not_reached (); + + if (g_hash_table_lookup (active_boot_checksums, bootcsum)) + continue; + + g_print ("ostadmin: Deleting bootdir %s\n", gs_file_get_path_cached (bootdir)); + if (!gs_shutil_rm_rf (bootdir, cancellable, error)) + goto out; + } + ret = TRUE; out: return ret; diff --git a/tests/t0016-admin-deploy.sh b/tests/t0016-admin-deploy.sh new file mode 100755 index 00000000..ab0bb9a1 --- /dev/null +++ b/tests/t0016-admin-deploy.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# +# Copyright (C) 2011,2013 Colin Walters +# +# 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. + +set -e + +. $(dirname $0)/libtest.sh + +echo "1..1" + +setup_os_repository "archive-z2" + +echo "ok setup" + +echo "1..2" + +ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime +rev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) +export rev +# This initial deployment gets kicked off with some kernel arguments +ostree admin --sysroot=sysroot deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime +assert_has_dir sysroot/boot/ostree/testos-${bootcsum} + +echo "ok deploy command" + +# Commit + upgrade twice, so that we'll rotate out the original deployment +orig_bootcsum=${bootcsum} +os_repository_new_commit +ostree --repo=sysroot/ostree/repo remote add testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime +ostree admin --sysroot=sysroot upgrade --os=testos +os_repository_new_commit +ostree --repo=sysroot/ostree/repo remote add testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime +ostree admin --sysroot=sysroot upgrade --os=testos + +rev=${newrev} +newrev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) +assert_not_streq ${rev} ${newrev} +assert_not_streq ${orig_bootcsum} ${bootcsum} +assert_not_has_dir sysroot/boot/ostree/testos-${orig_bootcsum} +assert_has_dir sysroot/boot/ostree/testos-${bootcsum} +assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS' + +echo "ok deploy and GC /boot"