lib/sysroot: Move staged into deployment list, rework handling
Followup to: https://github.com/ostreedev/ostree/pull/1503 After starting some more work on on this in rpm-ostree, it is actually simpler if the staged deployment just shows up in the list. It's effectively opt-in today; down the line we may make it the default, but I worry about breaking things that e.g. assume they can mutate the deployment before rebooting and have `/etc` already merged. There's not that many things in libostree that iterate over the deployment list. The biggest change here is around the `ostree_sysroot_write_deployments_with_options` API. I initially tried hard to support a use case like "push a rollback" while retaining the staged deployment, but everything gets very messy because that function truly is operating on the bootloader list. For now what I settled on is to just discard the staged deployment; down the line we can enhance things. Where we then have some new gymnastics is around implementing the finalization; we need to go to some effort to pull the staged deployment out of the list and mark it as unstaged, and then pass it down to `write_deployments()`. Closes: #1539 Approved by: jlebon
This commit is contained in:
parent
09dc2a8772
commit
16d3359bf8
|
|
@ -308,15 +308,6 @@ cleanup_old_deployments (OstreeSysroot *self,
|
||||||
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);
|
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* And also the staged deployment, if any */
|
|
||||||
if (self->staged_deployment)
|
|
||||||
{
|
|
||||||
char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, self->staged_deployment);
|
|
||||||
g_hash_table_replace (active_deployment_dirs, deployment_path, deployment_path);
|
|
||||||
char *bootcsum = g_strdup (ostree_deployment_get_bootcsum (self->staged_deployment));
|
|
||||||
g_hash_table_replace (active_boot_checksums, bootcsum, bootcsum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find all deployment directories, both active and inactive */
|
/* Find all deployment directories, both active and inactive */
|
||||||
g_autoptr(GPtrArray) all_deployment_dirs = NULL;
|
g_autoptr(GPtrArray) all_deployment_dirs = NULL;
|
||||||
if (!list_all_deployment_directories (self, &all_deployment_dirs,
|
if (!list_all_deployment_directories (self, &all_deployment_dirs,
|
||||||
|
|
|
||||||
|
|
@ -2144,6 +2144,38 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||||
{
|
{
|
||||||
g_assert (self->loaded);
|
g_assert (self->loaded);
|
||||||
|
|
||||||
|
/* It dramatically simplifies a lot of the logic below if we
|
||||||
|
* drop the staged deployment from both the source deployment list,
|
||||||
|
* as well as the target list. We don't want to write it to the bootloader
|
||||||
|
* now, which is mostly what this function is concerned with.
|
||||||
|
* In the future we though should probably adapt things to keep it.
|
||||||
|
*/
|
||||||
|
if (self->staged_deployment)
|
||||||
|
{
|
||||||
|
if (!glnx_unlinkat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, 0, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!_ostree_sysroot_rmrf_deployment (self, self->staged_deployment, cancellable, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_assert (self->staged_deployment == self->deployments->pdata[0]);
|
||||||
|
g_ptr_array_remove_index (self->deployments, 0);
|
||||||
|
}
|
||||||
|
/* First new deployment; we'll see if it's staged */
|
||||||
|
OstreeDeployment *first_new =
|
||||||
|
(new_deployments->len > 0 ? new_deployments->pdata[0] : NULL);
|
||||||
|
g_autoptr(GPtrArray) new_deployments_copy = NULL;
|
||||||
|
if (first_new && ostree_deployment_is_staged (first_new))
|
||||||
|
{
|
||||||
|
g_assert_cmpint (new_deployments->len, >, 0);
|
||||||
|
new_deployments_copy = g_ptr_array_sized_new (new_deployments->len - 1);
|
||||||
|
for (guint i = 1; i < new_deployments->len; i++)
|
||||||
|
g_ptr_array_add (new_deployments_copy, new_deployments->pdata[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
new_deployments_copy = g_ptr_array_ref (new_deployments);
|
||||||
|
new_deployments = new_deployments_copy;
|
||||||
|
|
||||||
/* Assign a bootserial to each new deployment.
|
/* Assign a bootserial to each new deployment.
|
||||||
*/
|
*/
|
||||||
assign_bootserials (new_deployments);
|
assign_bootserials (new_deployments);
|
||||||
|
|
@ -2162,6 +2194,8 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||||
for (guint i = 0; i < new_deployments->len; i++)
|
for (guint i = 0; i < new_deployments->len; i++)
|
||||||
{
|
{
|
||||||
OstreeDeployment *cur_deploy = self->deployments->pdata[i];
|
OstreeDeployment *cur_deploy = self->deployments->pdata[i];
|
||||||
|
if (ostree_deployment_is_staged (cur_deploy))
|
||||||
|
continue;
|
||||||
OstreeDeployment *new_deploy = new_deployments->pdata[i];
|
OstreeDeployment *new_deploy = new_deployments->pdata[i];
|
||||||
if (!deployment_bootconfigs_equal (cur_deploy, new_deploy))
|
if (!deployment_bootconfigs_equal (cur_deploy, new_deploy))
|
||||||
{
|
{
|
||||||
|
|
@ -2185,12 +2219,12 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self,
|
||||||
for (guint i = 0; i < new_deployments->len; i++)
|
for (guint i = 0; i < new_deployments->len; i++)
|
||||||
{
|
{
|
||||||
OstreeDeployment *deployment = new_deployments->pdata[i];
|
OstreeDeployment *deployment = new_deployments->pdata[i];
|
||||||
g_autoptr(GFile) deployment_root = NULL;
|
g_assert (!ostree_deployment_is_staged (deployment));
|
||||||
|
|
||||||
if (deployment == self->booted_deployment)
|
if (deployment == self->booted_deployment)
|
||||||
found_booted_deployment = TRUE;
|
found_booted_deployment = TRUE;
|
||||||
|
|
||||||
deployment_root = ostree_sysroot_get_deployment_directory (self, deployment);
|
g_autoptr(GFile) deployment_root = ostree_sysroot_get_deployment_directory (self, deployment);
|
||||||
if (!g_file_query_exists (deployment_root, NULL))
|
if (!g_file_query_exists (deployment_root, NULL))
|
||||||
return glnx_throw (error, "Unable to find expected deployment root: %s",
|
return glnx_throw (error, "Unable to find expected deployment root: %s",
|
||||||
gs_file_get_path_cached (deployment_root));
|
gs_file_get_path_cached (deployment_root));
|
||||||
|
|
@ -2672,7 +2706,10 @@ ostree_sysroot_stage_tree (OstreeSysroot *self,
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_ostree_sysroot_reload_staged (self, error))
|
/* Bump mtime so external processes know something changed, and then reload. */
|
||||||
|
if (!_ostree_sysroot_bump_mtime (self, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!ostree_sysroot_load (self, cancellable, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -2716,10 +2753,17 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self,
|
||||||
cancellable, error))
|
cancellable, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
/* Now, take ownership of the staged state, as normally the API below strips
|
||||||
|
* it out.
|
||||||
|
*/
|
||||||
|
g_autoptr(OstreeDeployment) staged = g_steal_pointer (&self->staged_deployment);
|
||||||
|
staged->staged = FALSE;
|
||||||
|
g_ptr_array_remove_index (self->deployments, 0);
|
||||||
|
|
||||||
/* TODO: Proxy across flags too? */
|
/* TODO: Proxy across flags too? */
|
||||||
OstreeSysrootSimpleWriteDeploymentFlags flags = 0;
|
OstreeSysrootSimpleWriteDeploymentFlags flags = 0;
|
||||||
if (!ostree_sysroot_simple_write_deployment (self, ostree_deployment_get_osname (self->staged_deployment),
|
if (!ostree_sysroot_simple_write_deployment (self, ostree_deployment_get_osname (staged),
|
||||||
self->staged_deployment, merge_deployment, flags,
|
staged, merge_deployment, flags,
|
||||||
cancellable, error))
|
cancellable, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
|
@ -2744,6 +2788,9 @@ ostree_sysroot_deployment_set_kargs (OstreeSysroot *self,
|
||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
|
/* For now; instead of this do a redeployment */
|
||||||
|
g_assert (!ostree_deployment_is_staged (deployment));
|
||||||
|
|
||||||
g_autoptr(OstreeDeployment) new_deployment = ostree_deployment_clone (deployment);
|
g_autoptr(OstreeDeployment) new_deployment = ostree_deployment_clone (deployment);
|
||||||
OstreeBootconfigParser *new_bootconfig = ostree_deployment_get_bootconfig (new_deployment);
|
OstreeBootconfigParser *new_bootconfig = ostree_deployment_get_bootconfig (new_deployment);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -740,6 +740,15 @@ compare_deployments_by_boot_loader_version_reversed (gconstpointer a_pp,
|
||||||
OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a);
|
OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a);
|
||||||
OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b);
|
OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b);
|
||||||
|
|
||||||
|
/* Staged deployments are always first */
|
||||||
|
if (ostree_deployment_is_staged (a))
|
||||||
|
{
|
||||||
|
g_assert (!ostree_deployment_is_staged (b));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (ostree_deployment_is_staged (b))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return compare_boot_loader_configs (a_bootconfig, b_bootconfig);
|
return compare_boot_loader_configs (a_bootconfig, b_bootconfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -824,11 +833,16 @@ _ostree_sysroot_reload_staged (OstreeSysroot *self,
|
||||||
g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs);
|
g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs);
|
||||||
if (target)
|
if (target)
|
||||||
{
|
{
|
||||||
self->staged_deployment =
|
g_autoptr(OstreeDeployment) staged =
|
||||||
_ostree_sysroot_deserialize_deployment_from_variant (target, error);
|
_ostree_sysroot_deserialize_deployment_from_variant (target, error);
|
||||||
if (!self->staged_deployment)
|
if (!staged)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
_ostree_deployment_set_bootconfig_from_kargs (self->staged_deployment, kargs);
|
|
||||||
|
_ostree_deployment_set_bootconfig_from_kargs (staged, kargs);
|
||||||
|
if (!load_origin (self, staged, NULL, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
self->staged_deployment = g_steal_pointer (&staged);
|
||||||
self->staged_deployment_data = g_steal_pointer (&staged_deployment_data);
|
self->staged_deployment_data = g_steal_pointer (&staged_deployment_data);
|
||||||
/* We set this flag for ostree_deployment_is_staged() because that API
|
/* We set this flag for ostree_deployment_is_staged() because that API
|
||||||
* doesn't have access to the sysroot, which currently has the
|
* doesn't have access to the sysroot, which currently has the
|
||||||
|
|
@ -938,8 +952,16 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
|
||||||
if (root_is_ostree_booted && !self->booted_deployment)
|
if (root_is_ostree_booted && !self->booted_deployment)
|
||||||
return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot but not in a booted deployment");
|
return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot but not in a booted deployment");
|
||||||
|
|
||||||
|
if (!_ostree_sysroot_reload_staged (self, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* Ensure the entires are sorted */
|
/* Ensure the entires are sorted */
|
||||||
g_ptr_array_sort (deployments, compare_deployments_by_boot_loader_version_reversed);
|
g_ptr_array_sort (deployments, compare_deployments_by_boot_loader_version_reversed);
|
||||||
|
|
||||||
|
/* Staged shows up first */
|
||||||
|
if (self->staged_deployment)
|
||||||
|
g_ptr_array_insert (deployments, 0, g_object_ref (self->staged_deployment));
|
||||||
|
|
||||||
/* And then set their index variables */
|
/* And then set their index variables */
|
||||||
for (guint i = 0; i < deployments->len; i++)
|
for (guint i = 0; i < deployments->len; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -947,9 +969,6 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
|
||||||
ostree_deployment_set_index (deployment, i);
|
ostree_deployment_set_index (deployment, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_ostree_sysroot_reload_staged (self, error))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* Determine whether we're "physical" or not, the first time we initialize */
|
/* Determine whether we're "physical" or not, the first time we initialize */
|
||||||
if (!self->loaded)
|
if (!self->loaded)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -201,16 +201,6 @@ ot_admin_builtin_status (int argc, char **argv, OstreeCommandInvocation *invocat
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OstreeDeployment *staged = ostree_sysroot_get_staged_deployment (sysroot);
|
|
||||||
if (staged)
|
|
||||||
{
|
|
||||||
if (!deployment_print_status (sysroot, repo, staged,
|
|
||||||
FALSE, FALSE, FALSE,
|
|
||||||
cancellable,
|
|
||||||
error))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (guint i = 0; i < deployments->len; i++)
|
for (guint i = 0; i < deployments->len; i++)
|
||||||
{
|
{
|
||||||
OstreeDeployment *deployment = deployments->pdata[i];
|
OstreeDeployment *deployment = deployments->pdata[i];
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# Test the deploy --stage functionality
|
# Test the deploy --stage functionality; first, we stage a deployment
|
||||||
|
# reboot, and validate that it worked.
|
||||||
|
|
||||||
- name: Write staged-deploy commit
|
- name: Write staged-deploy commit
|
||||||
shell: |
|
shell: |
|
||||||
ostree --repo=/ostree/repo commit --parent="${commit}" -b staged-deploy --tree=ref="${commit}" --no-bindings
|
ostree --repo=/ostree/repo commit --parent="${commit}" -b staged-deploy --tree=ref="${commit}" --no-bindings
|
||||||
ostree admin deploy --stage --karg-proc-cmdline --karg=ostreetest=yes staged-deploy
|
ostree admin deploy --stage --karg-proc-cmdline --karg=ostreetest=yes staged-deploy
|
||||||
|
test -f /run/ostree/staged-deployment
|
||||||
environment:
|
environment:
|
||||||
commit: "{{ rpmostree_status['deployments'][0]['checksum'] }}"
|
commit: "{{ rpmostree_status['deployments'][0]['checksum'] }}"
|
||||||
- include_tasks: ../tasks/reboot.yml
|
- include_tasks: ../tasks/reboot.yml
|
||||||
|
|
@ -13,12 +15,44 @@
|
||||||
journalctl -b "-1" -u ostree-finalize-staged.service | grep -q -e 'Transaction complete'
|
journalctl -b "-1" -u ostree-finalize-staged.service | grep -q -e 'Transaction complete'
|
||||||
# And that we have the new kernel argument
|
# And that we have the new kernel argument
|
||||||
grep -q -e 'ostreetest=yes' /proc/cmdline
|
grep -q -e 'ostreetest=yes' /proc/cmdline
|
||||||
|
# And there should not be a staged deployment
|
||||||
|
test '!' -f /run/ostree/staged-deployment
|
||||||
- name: Rollback
|
- name: Rollback
|
||||||
shell: rpm-ostree rollback
|
shell: rpm-ostree rollback
|
||||||
- include_tasks: ../tasks/reboot.yml
|
- include_tasks: ../tasks/reboot.yml
|
||||||
- shell: |
|
- shell: |
|
||||||
ostree refs --delete staged-deploy
|
|
||||||
rpm-ostree cleanup -rp
|
rpm-ostree cleanup -rp
|
||||||
# And now we shouldn't have the kernel commandline entry
|
# And now we shouldn't have the kernel commandline entry
|
||||||
- name: Check we do not have new kernel cmdline entry
|
- name: Check we do not have new kernel cmdline entry
|
||||||
shell: grep -qv -e 'ostreetest=yes' /proc/cmdline
|
shell: grep -qv -e 'ostreetest=yes' /proc/cmdline
|
||||||
|
|
||||||
|
# Ensure we can unstage
|
||||||
|
- name: Write staged-deploy commit, then unstage
|
||||||
|
shell: |
|
||||||
|
ostree admin deploy --stage --karg-proc-cmdline --karg=ostreetest=yes staged-deploy
|
||||||
|
ostree admin status > status.txt
|
||||||
|
grep -qFe '(staged)' status.txt
|
||||||
|
test -f /run/ostree/staged-deployment
|
||||||
|
ostree admin undeploy 0
|
||||||
|
ostree admin status > status.txt
|
||||||
|
grep -vqFe '(staged)' status.txt
|
||||||
|
test '!' -f /run/ostree/staged-deployment
|
||||||
|
environment:
|
||||||
|
commit: "{{ rpmostree_status['deployments'][0]['checksum'] }}"
|
||||||
|
|
||||||
|
# Staged should be overwritten by non-staged
|
||||||
|
- name: Write staged-deploy commit, then unstage
|
||||||
|
shell: |
|
||||||
|
ostree admin deploy --stage --karg-proc-cmdline --karg=ostreetest=yes staged-deploy
|
||||||
|
test -f /run/ostree/staged-deployment
|
||||||
|
ostree --repo=/ostree/repo commit --parent="${commit}" -b nonstaged-deploy --tree=ref="${commit}" --no-bindings
|
||||||
|
ostree admin deploy --karg-proc-cmdline --karg=ostreetest=yes nonstaged-deploy
|
||||||
|
ostree admin status > status.txt
|
||||||
|
grep -vqFe '(staged)' status.txt
|
||||||
|
test '!' -f /run/ostree/staged-deployment
|
||||||
|
ostree admin undeploy 0
|
||||||
|
environment:
|
||||||
|
commit: "{{ rpmostree_status['deployments'][0]['checksum'] }}"
|
||||||
|
|
||||||
|
- name: Cleanup refs
|
||||||
|
shell: ostree refs --delete staged-deploy nonstaged-deploy
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue