Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep last version of deployment #1960

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libostree/ostree-deployment-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ struct _OstreeDeployment
gboolean staged;
char **overlay_initrds;
char *overlay_initrds_id;
gchar *version;
gboolean version_is_cached;
};

void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum);
const char *_ostree_deployment_get_version (OstreeDeployment *self, OstreeRepo *repo);

void _ostree_deployment_set_overlay_initrds (OstreeDeployment *self,
char **overlay_initrds);
Expand Down
28 changes: 28 additions & 0 deletions src/libostree/ostree-deployment.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ ostree_deployment_get_bootserial (OstreeDeployment *self)
return self->bootserial;
}

const char *
_ostree_deployment_get_version (OstreeDeployment *self,
OstreeRepo *repo)
{
g_return_val_if_fail (repo != NULL, NULL);

if (!self->version_is_cached)
{
/* Try extracting a version for this deployment. */
const gchar *csum = ostree_deployment_get_csum (self);

g_autoptr(GVariant) variant = NULL;
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, csum,
&variant, NULL))
return NULL;

g_autoptr(GVariant) metadata = g_variant_get_child_value (variant, 0);
g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_VERSION, "s", &self->version);

self->version_is_cached = TRUE;
}

return self->version;
}

/**
* ostree_deployment_get_bootconfig:
* @self: Deployment
Expand Down Expand Up @@ -259,6 +284,8 @@ ostree_deployment_clone (OstreeDeployment *self)
self->deployserial,
self->bootcsum, self->bootserial);

ret->version = g_strdup (self->version);

new_bootconfig = ostree_bootconfig_parser_clone (self->bootconfig);
ostree_deployment_set_bootconfig (ret, new_bootconfig);

Expand Down Expand Up @@ -331,6 +358,7 @@ ostree_deployment_finalize (GObject *object)
g_free (self->osname);
g_free (self->csum);
g_free (self->bootcsum);
g_free (self->version);
g_clear_object (&self->bootconfig);
g_clear_pointer (&self->origin, g_key_file_unref);
g_strfreev (self->overlay_initrds);
Expand Down
35 changes: 13 additions & 22 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1923,24 +1923,9 @@ install_deployment_kernel (OstreeSysroot *sysroot,
if (val == NULL)
return glnx_throw (error, "No PRETTY_NAME or ID in /etc/os-release");

g_autofree char *deployment_version = NULL;
const gchar *deployment_version = NULL;
if (repo)
{
/* Try extracting a version for this deployment. */
const char *csum = ostree_deployment_get_csum (deployment);
g_autoptr(GVariant) variant = NULL;
g_autoptr(GVariant) metadata = NULL;

/* XXX Copying ot_admin_checksum_version() + bits from
* ot-admin-builtin-status.c. Maybe this should be
* public API in libostree? */
if (ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, csum,
&variant, NULL))
{
metadata = g_variant_get_child_value (variant, 0);
g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_VERSION, "s", &deployment_version);
}
}
deployment_version = _ostree_deployment_get_version (deployment, repo);

/* XXX The SYSLINUX bootloader backend actually parses the title string
* (specifically, it looks for the substring "(ostree"), so further
Expand Down Expand Up @@ -3150,6 +3135,11 @@ ostree_sysroot_stage_tree_with_options (OstreeSysroot *self,
g_variant_builder_add (builder, "{sv}", "overlay-initrds",
g_variant_new_strv ((const char *const*)opts->overlay_initrds, -1));

/* Proxy across any flags */
if (opts && opts->finalization_flags)
g_variant_builder_add (builder, "{sv}", "write-deployment-flags",
g_variant_new_uint32 ((guint32)opts->finalization_flags));

const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED));
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, parent, 0755, cancellable, error))
return FALSE;
Expand Down Expand Up @@ -3269,14 +3259,15 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self,
staged->staged = FALSE;
g_ptr_array_remove_index (self->deployments, 0);

/* TODO: Proxy across flags too?
r4f4 marked this conversation as resolved.
Show resolved Hide resolved
*
* But note that we always use NO_CLEAN to avoid adding more latency at
OstreeSysrootSimpleWriteDeploymentFlags flags = 0;
g_variant_lookup (self->staged_deployment_data, "write-deployment-flags", "u", &flags);
/*
* Note that we always use NO_CLEAN to avoid adding more latency at
* shutdown, and also because e.g. rpm-ostree wants to own the cleanup
* process.
*/
OstreeSysrootSimpleWriteDeploymentFlags flags =
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN;
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN;

if (!ostree_sysroot_simple_write_deployment (self, ostree_deployment_get_osname (staged),
staged, merge_deployment, flags,
cancellable, error))
Expand Down
49 changes: 49 additions & 0 deletions src/libostree/ostree-sysroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,9 @@ ostree_sysroot_init_osname (OstreeSysroot *self,
* specified, then no cleanup will be performed after adding the
* deployment. Make sure to call ostree_sysroot_cleanup() sometime
* later, instead.
*
* If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION is
* specified, then the previous version will not be garbage collected.
r4f4 marked this conversation as resolved.
Show resolved Hide resolved
*/
gboolean
ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
Expand All @@ -1861,6 +1864,8 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING) > 0;
const gboolean retain_rollback =
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK) > 0;
const gboolean retain_previous =
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION) > 0;
gboolean retain =
(flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0;

Expand All @@ -1883,6 +1888,34 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
if (!booted_deployment && !merge_deployment && (retain_pending || retain_rollback))
retain = TRUE;

/* tracks current versioned deployment */
OstreeRepo *repo = ostree_sysroot_repo (sysroot);
const gchar *new_version = _ostree_deployment_get_version (new_deployment, repo);

gboolean retained_previous_version = FALSE;

/* we never prune booted and merge deployments, so if they exist and are of a
* different version from `new_version`, they already fulfill the criteria of
* retaining the previous version */
if (booted_deployment)
{
const gchar *booted_version =
_ostree_deployment_get_version (booted_deployment, repo);
retained_previous_version = (g_strcmp0 (booted_version, new_version) != 0);
}

/* checking also that booted and merge are not the same although that's not a
* big deal since we cache the version now (though this will still work in
* the NULL case)
*/
if (!retained_previous_version && merge_deployment &&
!ostree_deployment_equal (merge_deployment, booted_deployment))
{
const gchar *merge_version =
_ostree_deployment_get_version (merge_deployment, repo);
retained_previous_version = (g_strcmp0 (merge_version, new_version) != 0);
}

/* tracks when we come across the booted deployment */
gboolean before_booted = TRUE;
gboolean before_merge = TRUE;
Expand All @@ -1903,6 +1936,13 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
* deployments, fall back on merge deployment */
const gboolean passed_crossover = booted_deployment ? !before_booted : !before_merge;

gboolean is_previous_version = FALSE;
if (passed_crossover && osname_matches && !retained_previous_version)
{
const gchar *version = _ostree_deployment_get_version (deployment, repo);
is_previous_version = (g_strcmp0 (version, new_version) != 0);
}

/* Retain deployment if:
* - we're explicitly asked to, or
* - it's pinned
Expand All @@ -1918,6 +1958,15 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot,
|| (is_booted || is_merge)
|| (retain_rollback && passed_crossover))
g_ptr_array_add (new_deployments, g_object_ref (deployment));
/*
* - we're keeping the previous version deployment
*/
else if (retain_previous && !retained_previous_version && is_previous_version)
{
g_ptr_array_add (new_deployments, g_object_ref (deployment));
/* Just keep one previous version */
retained_previous_version = TRUE;
}

/* add right after booted/merge deployment */
if (!added_new && passed_crossover)
Expand Down
7 changes: 6 additions & 1 deletion src/libostree/ostree-sysroot.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,13 @@ gboolean ostree_sysroot_stage_overlay_initrd (OstreeSysroot *self,
GCancellable *cancellable,
GError **error);

/**
* finalization_flags: only used in the staging path
*/
typedef struct {
gboolean unused_bools[8];
int unused_ints[8];
int finalization_flags;
int unused_ints[7];
char **override_kernel_argv;
char **overlay_initrds;
gpointer unused_ptrs[6];
Expand Down Expand Up @@ -291,6 +295,7 @@ typedef enum {
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN = (1 << 2),
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING = (1 << 3),
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK = (1 << 4),
OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION = (1 << 5),
} OstreeSysrootSimpleWriteDeploymentFlags;

_OSTREE_PUBLIC
Expand Down
7 changes: 6 additions & 1 deletion src/ostree/ot-admin-builtin-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static gboolean opt_retain;
static gboolean opt_stage;
static gboolean opt_retain_pending;
static gboolean opt_retain_rollback;
static gboolean opt_retain_previous;
static gboolean opt_not_as_default;
static gboolean opt_no_prune;
static gboolean opt_no_merge;
Expand All @@ -55,6 +56,7 @@ static GOptionEntry options[] = {
{ "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Complete deployment at OS shutdown", NULL },
{ "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending, "Do not delete pending deployments", NULL },
{ "retain-rollback", 0, 0, G_OPTION_ARG_NONE, &opt_retain_rollback, "Do not delete rollback deployments", NULL },
{ "retain-previous-version", 0, 0, G_OPTION_ARG_NONE, &opt_retain_previous, "Do not delete previous deployment if version differs from new deployment", NULL },
{ "not-as-default", 0, 0, G_OPTION_ARG_NONE, &opt_not_as_default, "Append rather than prepend new deployment", NULL },
{ "karg-proc-cmdline", 0, 0, G_OPTION_ARG_NONE, &opt_kernel_proc_cmdline, "Import current /proc/cmdline", NULL },
{ "karg", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_kernel_argv, "Set kernel argument, like root=/dev/sda1; this overrides any earlier argument with the same name", "NAME=VALUE" },
Expand Down Expand Up @@ -195,6 +197,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
OstreeSysrootDeployTreeOpts opts = {
.override_kernel_argv = kargs_strv,
.overlay_initrds = overlay_initrd_chksums ? (char**)overlay_initrd_chksums->pdata : NULL,
.finalization_flags = opt_retain_previous ? OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION : 0,
};

g_autoptr(OstreeDeployment) new_deployment = NULL;
Expand All @@ -205,7 +208,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
if (opt_not_as_default)
return glnx_throw (error, "--stage cannot currently be combined with --not-as-default");
/* use old API if we can to exercise it in CI */
if (!overlay_initrd_chksums)
if (!overlay_initrd_chksums && !opt_retain_previous)
{
if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin,
merge_deployment, kargs_strv, &new_deployment,
Expand Down Expand Up @@ -250,6 +253,8 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING;
if (opt_retain_rollback)
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK;
if (opt_retain_previous)
flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PREVIOUS_VERSION;
}

if (opt_not_as_default)
Expand Down
42 changes: 40 additions & 2 deletions tests/kolainst/destructive/staged-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ case "${AUTOPKGTEST_REBOOT_MARK:-}" in
cd /ostree/repo/tmp
# https://github.com/ostreedev/ostree/issues/1569
ostree checkout -H ${commit} t
ostree commit --no-bindings --parent="${commit}" -b staged-deploy -I --consume t
ostree commit --no-bindings --parent="${commit}" -b staged-deploy -I --consume t --add-metadata-string=version=foobar
newcommit=$(ostree rev-parse staged-deploy)
orig_mtime=$(stat -c '%.Y' /sysroot/ostree/deploy)
systemctl show -p SubState ostree-finalize-staged.path | grep -q waiting
Expand Down Expand Up @@ -115,8 +115,46 @@ case "${AUTOPKGTEST_REBOOT_MARK:-}" in
ostree admin undeploy 1
echo "ok staged retained"

# Deploy a new version
commit=${host_commit}
ostree checkout -H ${commit} t
ostree commit --no-bindings --parent="${commit}" -b same-version -I --consume t --add-metadata-string=version=foobaz
ostree admin deploy same-version --retain-previous-version

# Cleanup refs
ostree refs --delete staged-deploy nonstaged-deploy same-version
echo "ok cleanup refs"

/tmp/autopkgtest-reboot "3"
;;
"3")
# Check currently deployed versions
rpm-ostree status

# Make a new commit with the same version as the previous reboot
commit=$(rpmostree_query_json '.deployments[0].checksum')
cd /ostree/repo/tmp
ostree checkout -H ${commit} t
ostree commit --no-bindings --parent="${commit}" -b same-version-again -I --consume t --add-metadata-string=version=foobaz
ostree admin deploy same-version-again --retain-previous-version

# Check that previous version was kept
ostree admin status > status.txt
test $(grep -Fce 'Version: ' status.txt) = 3
echo "ok previous version retained"

# Check also for the staging path
rpm-ostree cleanup -p
ostree admin deploy --stage same-version-again --retain-previous-version
ostree admin finalize-staged

# Check that previous version was kept
ostree admin status > status.txt
test $(grep -Fce 'Version: ' status.txt) = 3
echo "ok previous version retained during stage"

# Cleanup refs
ostree refs --delete staged-deploy nonstaged-deploy
ostree refs --delete same-version-again
echo "ok cleanup refs"
;;
*) fatal "Unexpected AUTOPKGTEST_REBOOT_MARK=${AUTOPKGTEST_REBOOT_MARK}" ;;
Expand Down