Skip to content

Commit

Permalink
sysroot: Stabilize deployment finalization, add API
Browse files Browse the repository at this point in the history
It's about time we do this; deployment finalization locking
is a useful feature.  An absolutely key thing here is that
we've slowly been moving towards the deployments as the primary
"source of truth".

Specifically in bootc for example, we will GC container images
not referenced by a deployment.

This is then neecessary to support a "pull but don't apply automatically" model.

Closes: #3025
  • Loading branch information
cgwalters committed Nov 20, 2023
1 parent 315cbdd commit 04de1dd
Show file tree
Hide file tree
Showing 12 changed files with 64 additions and 10 deletions.
1 change: 1 addition & 0 deletions apidoc/ostree-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ ostree_deployment_get_origin_relpath
ostree_deployment_get_unlocked
ostree_deployment_is_pinned
ostree_deployment_is_staged
ostree_deployment_is_finalization_locked
ostree_deployment_set_index
ostree_deployment_set_bootserial
ostree_deployment_set_bootconfig
Expand Down
1 change: 1 addition & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
LIBOSTREE_2023.8 {
global:
ostree_sysroot_update_post_copy;
ostree_deployment_is_finalization_locked;
} LIBOSTREE_2023.4;

/* Stub section for the stable release *after* this development one; don't
Expand Down
1 change: 1 addition & 0 deletions src/libostree/ostree-deployment-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ struct _OstreeDeployment
GKeyFile *origin;
OstreeDeploymentUnlockedState unlocked;
gboolean staged;
gboolean finalization_locked;
char **overlay_initrds;
char *overlay_initrds_id;
};
Expand Down
13 changes: 13 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,16 @@ ostree_deployment_is_staged (OstreeDeployment *self)
{
return self->staged;
}

/**
* ostree_deployment_is_finalization_locked:
* @self: Deployment
*
* Returns: `TRUE` if deployment is queued to be "finalized" at shutdown time, but requires
* additional action. Since: 2023.7
*/
gboolean
ostree_deployment_is_finalization_locked (OstreeDeployment *self)
{
return self->finalization_locked;
}
2 changes: 2 additions & 0 deletions src/libostree/ostree-deployment.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_staged (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_finalization_locked (OstreeDeployment *self);
_OSTREE_PUBLIC
gboolean ostree_deployment_is_pinned (OstreeDeployment *self);

_OSTREE_PUBLIC
Expand Down
23 changes: 19 additions & 4 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3657,6 +3657,10 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self, const char *osname,
g_autoptr (GVariantBuilder) builder = g_variant_builder_new ((GVariantType *)"a{sv}");
g_variant_builder_add (builder, "{sv}", "target", serialize_deployment_to_variant (deployment));

if (opts->locked)
g_variant_builder_add (builder, "{sv}", _OSTREE_SYSROOT_STAGED_KEY_LOCKED,
g_variant_new_boolean (TRUE));

if (merge_deployment)
g_variant_builder_add (builder, "{sv}", "merge-deployment",
serialize_deployment_to_variant (merge_deployment));
Expand Down Expand Up @@ -3722,11 +3726,22 @@ _ostree_sysroot_finalize_staged_inner (OstreeSysroot *self, GCancellable *cancel
}

/* Check if finalization is locked. */
if (!glnx_fstatat_allow_noent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, NULL, 0, error))
return FALSE;
if (errno == 0)
gboolean locked = false;
(void)g_variant_lookup (self->staged_deployment_data, _OSTREE_SYSROOT_STAGED_KEY_LOCKED, "b",
&locked);
if (locked)
g_debug ("staged is locked via metadata");
else
{
if (!glnx_fstatat_allow_noent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, NULL, 0,
error))
return FALSE;
if (errno == 0)
locked = TRUE;
}
if (locked)
{
ot_journal_print (LOG_INFO, "Not finalizing; found " _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
ot_journal_print (LOG_INFO, "Not finalizing; deployment is locked for finalization");
return TRUE;
}

Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-sysroot-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ struct OstreeSysroot
OstreeSysrootDebugFlags debug_flags;
};

/* Key in staged deployment variant for finalization locking */
#define _OSTREE_SYSROOT_STAGED_KEY_LOCKED "locked"

#define OSTREE_SYSROOT_LOCKFILE "ostree/lock"
/* We keep some transient state in /run */
#define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment"
Expand Down
2 changes: 2 additions & 0 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,8 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self, GError **error)
* canonical "staged_deployment" reference.
*/
self->staged_deployment->staged = TRUE;
(void)g_variant_dict_lookup (staged_deployment_dict, _OSTREE_SYSROOT_STAGED_KEY_LOCKED,
"b", &self->staged_deployment->finalization_locked);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ gboolean ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self, int fd, char

typedef struct
{
gboolean unused_bools[8];
/* If set to true, then this deployment will be staged but "locked" and not automatically applied
* on reboot. */
gboolean locked;
gboolean unused_bools[7];
int unused_ints[8];
char **override_kernel_argv;
char **overlay_initrds;
Expand Down
13 changes: 10 additions & 3 deletions src/ostree/ot-admin-builtin-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ static GOptionEntry options[] = {
"Do not apply configuration (/etc and kernel arguments) from booted deployment", NULL },
{ "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployments", NULL },
{ "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Complete deployment at OS shutdown", NULL },
{ "lock-finalization", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_lock_finalization,
{ "lock-finalization", 0, 0, G_OPTION_ARG_NONE, &opt_lock_finalization,
"Prevent automatic deployment finalization on shutdown", NULL },
{ "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending,
"Do not delete pending deployments", NULL },
Expand Down Expand Up @@ -123,6 +123,10 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
return FALSE;
}

// Locking implies staging
if (opt_lock_finalization)
opt_stage = TRUE;

const char *refspec = argv[1];

OstreeRepo *repo = ostree_sysroot_repo (sysroot);
Expand Down Expand Up @@ -236,6 +240,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
g_auto (GStrv) kargs_strv = kargs ? ostree_kernel_args_to_strv (kargs) : NULL;

OstreeSysrootDeployTreeOpts opts = {
.locked = opt_lock_finalization,
.override_kernel_argv = kargs_strv,
.overlay_initrds = overlay_initrd_chksums ? (char **)overlay_initrd_chksums->pdata : NULL,
};
Expand All @@ -247,9 +252,11 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
return glnx_throw (error, "--stage cannot currently be combined with --retain arguments");
if (opt_not_as_default)
return glnx_throw (error, "--stage cannot currently be combined with --not-as-default");
/* touch file *before* we stage to avoid races */
/* For compatibility with older versions of ostree, also write this legacy file.
* This can likely be safely deleted in the middle of 2024 say. */
if (opt_lock_finalization)
{
g_debug ("Writing legacy finalization lockfile");
if (!glnx_shutil_mkdir_p_at (AT_FDCWD,
dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED)),
0755, cancellable, error))
Expand All @@ -262,7 +269,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED);
}
/* use old API if we can to exercise it in CI */
if (!overlay_initrd_chksums)
if (!(overlay_initrd_chksums || opt_lock_finalization))
{
if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, merge_deployment,
kargs_strv, &new_deployment, cancellable, error))
Expand Down
4 changes: 3 additions & 1 deletion src/ostree/ot-admin-builtin-status.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ deployment_print_status (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeploym
GKeyFile *origin = ostree_deployment_get_origin (deployment);

const char *deployment_status = "";
if (ostree_deployment_is_staged (deployment))
if (ostree_deployment_is_finalization_locked (deployment))
deployment_status = " (finalization locked)";
else if (ostree_deployment_is_staged (deployment))
deployment_status = " (staged)";
else if (is_pending)
deployment_status = " (pending)";
Expand Down
6 changes: 5 additions & 1 deletion tests/kolainst/destructive/staged-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ EOF
test '!' -f /run/ostree/staged-deployment

test '!' -f /run/ostree/staged-deployment
ostree admin deploy --stage staged-deploy --lock-finalization
ostree admin status > status.txt
assert_not_file_has_content status.txt 'finalization locked'
ostree admin deploy staged-deploy --lock-finalization
ostree admin status > status.txt
assert_file_has_content status.txt 'finalization locked'
test -f /run/ostree/staged-deployment
test -f /run/ostree/staged-deployment-locked
# check that we can cleanup the staged deployment
Expand Down

0 comments on commit 04de1dd

Please sign in to comment.