Skip to content

Commit

Permalink
sysroot: Add concept of deployment "pinning" 📌
Browse files Browse the repository at this point in the history
Example user story: Jane rebases her OS to a new major version N, and wants to
keep around N-1 even after a few upgrades for a while so she can easily roll
back. I plan to add `rpm-ostree rebase --pin` to opt-in to this for example.

Builds on the new `libostree-transient` group to store pinning state there.

Closes: #1460
  • Loading branch information
cgwalters committed Feb 26, 2018
1 parent 6e54bfe commit cdce079
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 1 deletion.
1 change: 1 addition & 0 deletions Makefile-man.am
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ostree-admin-config-diff.1 ostree-admin-deploy.1 \
ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-os-init.1 \
ostree-admin-status.1 ostree-admin-set-origin.1 ostree-admin-switch.1 \
ostree-admin-undeploy.1 ostree-admin-upgrade.1 ostree-admin-unlock.1 \
ostree-admin-pin.1 \
ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 \
ostree-commit.1 ostree-export.1 ostree-gpg-sign.1 ostree-config.1 \
ostree-diff.1 ostree-fsck.1 ostree-init.1 ostree-log.1 ostree-ls.1 \
Expand Down
1 change: 1 addition & 0 deletions Makefile-ostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ ostree_SOURCES += \
src/ostree/ot-admin-builtin-set-origin.c \
src/ostree/ot-admin-builtin-status.c \
src/ostree/ot-admin-builtin-switch.c \
src/ostree/ot-admin-builtin-pin.c \
src/ostree/ot-admin-builtin-upgrade.c \
src/ostree/ot-admin-builtin-unlock.c \
src/ostree/ot-admin-builtins.h \
Expand Down
2 changes: 2 additions & 0 deletions apidoc/ostree-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ ostree_deployment_get_bootconfig
ostree_deployment_get_origin
ostree_deployment_get_origin_relpath
ostree_deployment_get_unlocked
ostree_deployment_is_pinned
ostree_deployment_set_index
ostree_deployment_set_bootserial
ostree_deployment_set_bootconfig
Expand Down Expand Up @@ -509,6 +510,7 @@ ostree_sysroot_init_osname
ostree_sysroot_deployment_set_kargs
ostree_sysroot_deployment_set_mutable
ostree_sysroot_deployment_unlock
ostree_sysroot_deployment_set_pinned
ostree_sysroot_write_deployments
ostree_sysroot_write_deployments_with_options
ostree_sysroot_write_origin_file
Expand Down
31 changes: 31 additions & 0 deletions bash/ostree
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,37 @@ _ostree_admin_os_init() {
return 0
}

_ostree_admin_pin() {
local boolean_options="
$main_boolean_options
"

local options_with_args="
--sysroot
"

local options_with_args_glob=$( __ostree_to_extglob "$options_with_args" )

case "$prev" in
--sysroot)
__ostree_compreply_dirs_only
return 0
;;
$options_with_args_glob )
return 0
;;
esac

case "$cur" in
-*)
local all_options="$boolean_options $options_with_args"
__ostree_compreply_all_options
;;
esac

return 0
}

_ostree_admin_set_origin() {
local boolean_options="
$main_boolean_options
Expand Down
2 changes: 2 additions & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
/* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2018.3 {
ostree_deployment_origin_remove_transient_state;
ostree_sysroot_deployment_set_pinned;
ostree_deployment_is_pinned;
} LIBOSTREE_2018.2;

/* Stub section for the stable release *after* this development one; don't
Expand Down
17 changes: 17 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,20 @@ ostree_deployment_get_unlocked (OstreeDeployment *self)
{
return self->unlocked;
}

/**
* ostree_deployment_is_pinned:
* @self: Deployment
*
* See ostree_sysroot_deployment_set_pinned().
*
* Returns: `TRUE` if deployment will not be subject to GC
* Since: 2018.3
*/
gboolean
ostree_deployment_is_pinned (OstreeDeployment *self)
{
if (!self->origin)
return FALSE;
return g_key_file_get_boolean (self->origin, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", NULL);
}
3 changes: 3 additions & 0 deletions src/libostree/ostree-deployment.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ _OSTREE_PUBLIC
GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self);


_OSTREE_PUBLIC
gboolean ostree_deployment_is_pinned (OstreeDeployment *self);

_OSTREE_PUBLIC
void ostree_deployment_set_index (OstreeDeployment *self, int index);
_OSTREE_PUBLIC
Expand Down
44 changes: 44 additions & 0 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1572,12 +1572,14 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,

/* Retain deployment if:
* - we're explicitly asked to, or
* - it's pinned
* - the deployment is for another osname, or
* - we're keeping pending deployments and this is a pending deployment, or
* - this is the merge or boot deployment, or
* - we're keeping rollback deployments and this is a rollback deployment
*/
if (retain
|| ostree_deployment_is_pinned (deployment)
|| !osname_matches
|| (retain_pending && !passed_crossover)
|| (is_booted || is_merge)
Expand Down Expand Up @@ -1832,3 +1834,45 @@ ostree_sysroot_deployment_unlock (OstreeSysroot *self,

return TRUE;
}

/**
* ostree_sysroot_deployment_set_pinned:
* @self: Sysroot
* @deployment: A deployment
* @is_pinned: Whether or not deployment will be automatically GC'd
* @error: Error
*
* By default, deployments may be subject to garbage collection. Typical uses of
* libostree only retain at most 2 deployments. If @is_pinned is `TRUE`, a
* metadata bit will be set causing libostree to avoid automatic GC of the
* deployment. However, this is really an "advisory" note; it's still possible
* for e.g. older versions of libostree unaware of pinning to GC the deployment.
*
* This function does nothing and returns successfully if the deployment
* is already in the desired pinning state.
*
* Since: 2018.3
*/
gboolean
ostree_sysroot_deployment_set_pinned (OstreeSysroot *self,
OstreeDeployment *deployment,
gboolean is_pinned,
GError **error)
{
const gboolean current_pin = ostree_deployment_is_pinned (deployment);
if (is_pinned == current_pin)
return TRUE;

g_autoptr(OstreeDeployment) deployment_clone = ostree_deployment_clone (deployment);
GKeyFile *origin_clone = ostree_deployment_get_origin (deployment_clone);

if (is_pinned)
g_key_file_set_boolean (origin_clone, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", TRUE);
else
g_key_file_remove_key (origin_clone, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", NULL);

if (!ostree_sysroot_write_origin_file (self, deployment, origin_clone, NULL, error))
return FALSE;

return TRUE;
}
6 changes: 6 additions & 0 deletions src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_set_pinned (OstreeSysroot *self,
OstreeDeployment *deployment,
gboolean is_pinned,
GError **error);

_OSTREE_PUBLIC
gboolean ostree_sysroot_deployment_unlock (OstreeSysroot *self,
OstreeDeployment *deployment,
Expand Down
80 changes: 80 additions & 0 deletions src/ostree/ot-admin-builtin-pin.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2018 Colin Walters <[email protected]>
*
* SPDX-License-Identifier: LGPL-2.0+
*
* 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.
*/

#include "config.h"

#include <stdlib.h>

#include "ot-main.h"
#include "ot-admin-builtins.h"
#include "ot-admin-functions.h"
#include "ostree.h"
#include "otutil.h"

/* ATTENTION:
* Please remember to update the bash-completion script (bash/ostree) and
* man page (man/ostree-admin-pin.xml) when changing the option list.
*/

static gboolean opt_unpin;

static GOptionEntry options[] = {
{ "unpin", 'u', 0, G_OPTION_ARG_NONE, &opt_unpin, "Unset pin", NULL },
{ NULL }
};

gboolean
ot_admin_builtin_pin (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error)
{
g_autoptr(GOptionContext) context = g_option_context_new ("INDEX");
g_autoptr(OstreeSysroot) sysroot = NULL;
if (!ostree_admin_option_context_parse (context, options, &argc, &argv,
OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER,
invocation, &sysroot, cancellable, error))
return FALSE;

if (argc < 2)
{
ot_util_usage_error (context, "INDEX must be specified", error);
return FALSE;
}

const char *deploy_index_str = argv[1];
const int deploy_index = atoi (deploy_index_str);

g_autoptr(OstreeDeployment) target_deployment = ot_admin_get_indexed_deployment (sysroot, deploy_index, error);
if (!target_deployment)
return FALSE;


gboolean current_pin = ostree_deployment_is_pinned (target_deployment);
const gboolean desired_pin = !opt_unpin;
if (current_pin == desired_pin)
g_print ("Deployment is already %s\n", current_pin ? "pinned" : "unpinned");
else
{
if (!ostree_sysroot_deployment_set_pinned (sysroot, target_deployment, desired_pin, error))
return FALSE;
g_print ("Deployment is now %s\n", desired_pin ? "pinned" : "unpinned");
}

return TRUE;
}
2 changes: 2 additions & 0 deletions src/ostree/ot-admin-builtin-status.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ ot_admin_builtin_status (int argc, char **argv, OstreeCommandInvocation *invocat
ostree_deployment_unlocked_state_to_string (unlocked),
red_bold_suffix);
}
if (ostree_deployment_is_pinned (deployment))
g_print (" Pinned: yes\n");
if (!origin)
g_print (" origin: none\n");
else
Expand Down
1 change: 1 addition & 0 deletions src/ostree/ot-admin-builtins.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ BUILTINPROTO(init_fs);
BUILTINPROTO(undeploy);
BUILTINPROTO(deploy);
BUILTINPROTO(cleanup);
BUILTINPROTO(pin);
BUILTINPROTO(unlock);
BUILTINPROTO(status);
BUILTINPROTO(set_origin);
Expand Down
3 changes: 3 additions & 0 deletions src/ostree/ot-builtin-admin.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ static OstreeCommand admin_subcommands[] = {
{ "set-origin", OSTREE_BUILTIN_FLAG_NO_REPO,
ot_admin_builtin_set_origin,
"Set Origin and create a new origin file" },
{ "pin", OSTREE_BUILTIN_FLAG_NO_REPO,
ot_admin_builtin_pin,
"Change the \"pinning\" state of a deployment" },
{ "status", OSTREE_BUILTIN_FLAG_NO_REPO,
ot_admin_builtin_status,
"List deployments" },
Expand Down
49 changes: 48 additions & 1 deletion tests/test-admin-deploy-2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ set -euo pipefail
# Exports OSTREE_SYSROOT so --sysroot not needed.
setup_os_repository "archive" "syslinux"

echo "1..3"
echo "1..6"

${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
Expand Down Expand Up @@ -63,3 +63,50 @@ 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 manual cleanup"

assert_n_pinned() {
local n=$1
${CMD_PREFIX} ostree admin status > status.txt
local n_pinned="$(grep -F -c -e 'Pinned: yes' < status.txt)"
if test "${n_pinned}" '!=' "${n}"; then
cat status.txt
fatal "${n_pinned} != ${n}"
fi
}
assert_n_deployments() {
local n=$1
${CMD_PREFIX} ostree admin status > status.txt
local n_deployments="$(grep -F -c -e 'Version: ' < status.txt)"
if test "${n_deployments}" '!=' "${n}"; then
cat status.txt
fatal "${n_deployments} != ${n}"
fi
}
assert_n_pinned 0
${CMD_PREFIX} ostree admin pin 0
assert_n_pinned 1
${CMD_PREFIX} ostree admin pin -u 0
assert_n_pinned 0
echo "ok pin unpin"

${CMD_PREFIX} ostree admin pin 0
${CMD_PREFIX} ostree admin pin 1
assert_n_pinned 2
assert_n_deployments 2
os_repository_new_commit
${CMD_PREFIX} ostree admin upgrade --os=testos
assert_n_pinned 2
assert_n_deployments 3
echo "ok pin across upgrades"

${CMD_PREFIX} ostree admin pin -u 1
os_repository_new_commit
${CMD_PREFIX} ostree admin upgrade --os=testos
assert_n_pinned 1
assert_n_deployments 3
os_repository_new_commit
${CMD_PREFIX} ostree admin upgrade --os=testos
assert_n_pinned 1
assert_n_deployments 3

echo "ok pinning"

0 comments on commit cdce079

Please sign in to comment.