deploy: Swap current symlink only after updating the kernel
While this still isn't fully atomic (that depends on the bootloader), this better ensures that the deployed kernel is booted with the intended tree. For example, if we get ENOSPC when writing out the kernel, we won't have swapped the symlink.
This commit is contained in:
parent
52a3369709
commit
8c694622b1
|
|
@ -34,6 +34,15 @@ typedef struct {
|
|||
GFile *ostree_dir;
|
||||
char *osname;
|
||||
GFile *osname_dir;
|
||||
|
||||
char *current_deployment_ref;
|
||||
char *previous_deployment_ref;
|
||||
char *resolved_commit;
|
||||
char *resolved_previous_commit;
|
||||
|
||||
char *previous_deployment_revision;
|
||||
GFile *deploy_target_path;
|
||||
GFile *previous_deployment;
|
||||
} OtAdminDeploy;
|
||||
|
||||
static gboolean opt_no_kernel;
|
||||
|
|
@ -379,23 +388,17 @@ static gboolean
|
|||
deploy_tree (OtAdminDeploy *self,
|
||||
const char *deploy_target,
|
||||
const char *revision,
|
||||
GFile **out_deploy_dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
gs_free char *current_deployment_ref = NULL;
|
||||
gs_free char *previous_deployment_ref = NULL;
|
||||
ot_lfree char *deploy_target_fullname = NULL;
|
||||
ot_lfree char *deploy_target_fullname_tmp = NULL;
|
||||
ot_lobj GFile *deploy_target_path = NULL;
|
||||
ot_lobj GFile *deploy_target_path_tmp = NULL;
|
||||
ot_lfree char *deploy_target_etc_name = NULL;
|
||||
ot_lobj GFile *deploy_target_etc_path = NULL;
|
||||
ot_lobj GFile *deploy_target_default_etc_path = NULL;
|
||||
ot_lobj GFile *deploy_parent = NULL;
|
||||
ot_lobj GFile *previous_deployment = NULL;
|
||||
ot_lfree char *previous_deployment_revision = NULL;
|
||||
ot_lobj GFile *previous_deployment_etc = NULL;
|
||||
ot_lobj GFile *previous_deployment_etc_default = NULL;
|
||||
ot_lobj OstreeRepoFile *root = NULL;
|
||||
|
|
@ -403,17 +406,12 @@ deploy_tree (OtAdminDeploy *self,
|
|||
ot_lobj GFileInfo *existing_checkout_info = NULL;
|
||||
ot_lfree char *checkout_target_name = NULL;
|
||||
ot_lfree char *checkout_target_tmp_name = NULL;
|
||||
ot_lfree char *resolved_commit = NULL;
|
||||
gs_free char *resolved_previous_commit = NULL;
|
||||
GError *temp_error = NULL;
|
||||
gboolean skip_checkout;
|
||||
|
||||
if (!revision)
|
||||
revision = deploy_target;
|
||||
|
||||
current_deployment_ref = g_strdup_printf ("deployment/%s/current", self->osname);
|
||||
previous_deployment_ref = g_strdup_printf ("deployment/%s/previous", self->osname);
|
||||
|
||||
if (!g_file_query_exists (self->osname_dir, cancellable))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
|
|
@ -422,12 +420,12 @@ deploy_tree (OtAdminDeploy *self,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, FALSE, &resolved_commit, error))
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, FALSE, &self->resolved_commit, error))
|
||||
goto out;
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, TRUE, &resolved_previous_commit, error))
|
||||
if (!ostree_repo_resolve_rev (self->repo, revision, TRUE, &self->resolved_previous_commit, error))
|
||||
goto out;
|
||||
|
||||
root = (OstreeRepoFile*)ostree_repo_file_new_root (self->repo, resolved_commit);
|
||||
root = (OstreeRepoFile*)ostree_repo_file_new_root (self->repo, self->resolved_commit);
|
||||
if (!ostree_repo_file_ensure_resolved (root, error))
|
||||
goto out;
|
||||
|
||||
|
|
@ -437,31 +435,31 @@ deploy_tree (OtAdminDeploy *self,
|
|||
if (!file_info)
|
||||
goto out;
|
||||
|
||||
deploy_target_fullname = g_strconcat (deploy_target, "-", resolved_commit, NULL);
|
||||
deploy_target_path = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname);
|
||||
deploy_target_fullname = g_strconcat (deploy_target, "-", self->resolved_commit, NULL);
|
||||
self->deploy_target_path = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname);
|
||||
|
||||
deploy_target_fullname_tmp = g_strconcat (deploy_target_fullname, ".tmp", NULL);
|
||||
deploy_target_path_tmp = g_file_resolve_relative_path (self->osname_dir, deploy_target_fullname_tmp);
|
||||
|
||||
deploy_parent = g_file_get_parent (deploy_target_path);
|
||||
deploy_parent = g_file_get_parent (self->deploy_target_path);
|
||||
if (!gs_file_ensure_directory (deploy_parent, TRUE, cancellable, error))
|
||||
goto out;
|
||||
|
||||
deploy_target_etc_name = g_strconcat (deploy_target, "-", resolved_commit, "-etc", NULL);
|
||||
deploy_target_etc_name = g_strconcat (deploy_target, "-", self->resolved_commit, "-etc", NULL);
|
||||
deploy_target_etc_path = g_file_resolve_relative_path (self->osname_dir, deploy_target_etc_name);
|
||||
|
||||
/* Delete any previous temporary data */
|
||||
if (!gs_shutil_rm_rf (deploy_target_path_tmp, cancellable, error))
|
||||
goto out;
|
||||
|
||||
existing_checkout_info = g_file_query_info (deploy_target_path, OSTREE_GIO_FAST_QUERYINFO,
|
||||
existing_checkout_info = g_file_query_info (self->deploy_target_path, OSTREE_GIO_FAST_QUERYINFO,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable, &temp_error);
|
||||
if (existing_checkout_info)
|
||||
{
|
||||
if (opt_force)
|
||||
{
|
||||
if (!gs_shutil_rm_rf (deploy_target_path, cancellable, error))
|
||||
if (!gs_shutil_rm_rf (self->deploy_target_path, cancellable, error))
|
||||
goto out;
|
||||
if (!gs_shutil_rm_rf (deploy_target_etc_path, cancellable, error))
|
||||
goto out;
|
||||
|
|
@ -482,27 +480,27 @@ deploy_tree (OtAdminDeploy *self,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (!ot_admin_get_current_deployment (self->ostree_dir, self->osname, &previous_deployment,
|
||||
if (!ot_admin_get_current_deployment (self->ostree_dir, self->osname, &self->previous_deployment,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
if (previous_deployment)
|
||||
if (self->previous_deployment)
|
||||
{
|
||||
ot_lfree char *etc_name;
|
||||
ot_lobj GFile *parent;
|
||||
|
||||
etc_name = g_strconcat (gs_file_get_basename_cached (previous_deployment), "-etc", NULL);
|
||||
parent = g_file_get_parent (previous_deployment);
|
||||
etc_name = g_strconcat (gs_file_get_basename_cached (self->previous_deployment), "-etc", NULL);
|
||||
parent = g_file_get_parent (self->previous_deployment);
|
||||
|
||||
previous_deployment_etc = g_file_get_child (parent, etc_name);
|
||||
|
||||
if (!g_file_query_exists (previous_deployment_etc, cancellable)
|
||||
|| g_file_equal (previous_deployment, deploy_target_path))
|
||||
|| g_file_equal (self->previous_deployment, self->deploy_target_path))
|
||||
g_clear_object (&previous_deployment_etc);
|
||||
else
|
||||
previous_deployment_etc_default = g_file_get_child (previous_deployment, "etc");
|
||||
previous_deployment_etc_default = g_file_get_child (self->previous_deployment, "etc");
|
||||
|
||||
if (!ostree_repo_resolve_rev (self->repo, current_deployment_ref, TRUE,
|
||||
&previous_deployment_revision, error))
|
||||
if (!ostree_repo_resolve_rev (self->repo, self->current_deployment_ref, TRUE,
|
||||
&self->previous_deployment_revision, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -513,7 +511,7 @@ deploy_tree (OtAdminDeploy *self,
|
|||
ot_lobj GFile *triggers_run_path = NULL;
|
||||
|
||||
g_print ("ostadmin: Creating deployment %s\n",
|
||||
gs_file_get_path_cached (deploy_target_path));
|
||||
gs_file_get_path_cached (self->deploy_target_path));
|
||||
|
||||
memset (&checkout_data, 0, sizeof (checkout_data));
|
||||
checkout_data.loop = g_main_loop_new (NULL, TRUE);
|
||||
|
|
@ -559,33 +557,12 @@ deploy_tree (OtAdminDeploy *self,
|
|||
else
|
||||
g_print ("ostadmin: No previous deployment; therefore, no configuration changes to merge\n");
|
||||
|
||||
if (!gs_file_rename (deploy_target_path_tmp, deploy_target_path,
|
||||
if (!gs_file_rename (deploy_target_path_tmp, self->deploy_target_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Write out a ref so that any "ostree prune" on the raw repo
|
||||
* doesn't GC the currently deployed tree.
|
||||
*/
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, current_deployment_ref,
|
||||
resolved_commit, error))
|
||||
goto out;
|
||||
/* Only overwrite previous if it's different from what we're deploying now.
|
||||
*/
|
||||
if (resolved_previous_commit != NULL
|
||||
&& strcmp (resolved_previous_commit, resolved_commit) != 0)
|
||||
{
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, previous_deployment_ref,
|
||||
previous_deployment_revision, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!update_current (self, previous_deployment, deploy_target_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
ot_transfer_out_value (out_deploy_dir, &deploy_target_path);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -597,7 +574,6 @@ deploy_tree (OtAdminDeploy *self,
|
|||
*/
|
||||
static gboolean
|
||||
do_update_kernel (OtAdminDeploy *self,
|
||||
GFile *deploy_path,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
|
|
@ -611,7 +587,7 @@ do_update_kernel (OtAdminDeploy *self,
|
|||
"--boot-dir", gs_file_get_path_cached (self->admin_opts->boot_dir),
|
||||
"update-kernel",
|
||||
self->osname,
|
||||
gs_file_get_path_cached (deploy_path), NULL);
|
||||
gs_file_get_path_cached (self->deploy_target_path), NULL);
|
||||
g_ptr_array_add (args, NULL);
|
||||
|
||||
proc = gs_subprocess_new_simple_argv ((char**)args->pdata,
|
||||
|
|
@ -628,6 +604,38 @@ do_update_kernel (OtAdminDeploy *self,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
complete_deployment (OtAdminDeploy *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
|
||||
/* Write out a ref so that any "ostree prune" on the raw repo
|
||||
* doesn't GC the currently deployed tree.
|
||||
*/
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, self->current_deployment_ref,
|
||||
self->resolved_commit, error))
|
||||
goto out;
|
||||
/* Only overwrite previous if it's different from what we're deploying now.
|
||||
*/
|
||||
if (self->resolved_previous_commit != NULL
|
||||
&& strcmp (self->resolved_previous_commit, self->resolved_commit) != 0)
|
||||
{
|
||||
if (!ostree_repo_write_ref (self->repo, NULL, self->previous_deployment_ref,
|
||||
self->previous_deployment_revision, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!update_current (self, self->previous_deployment, self->deploy_target_path,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error)
|
||||
{
|
||||
|
|
@ -675,20 +683,31 @@ ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts,
|
|||
|
||||
self->osname = g_strdup (osname);
|
||||
self->osname_dir = ot_gfile_get_child_build_path (self->ostree_dir, "deploy", osname, NULL);
|
||||
if (!deploy_tree (self, deploy_target, revision, &deploy_path,
|
||||
cancellable, error))
|
||||
self->current_deployment_ref = g_strdup_printf ("deployment/%s/current", self->osname);
|
||||
self->previous_deployment_ref = g_strdup_printf ("deployment/%s/previous", self->osname);
|
||||
|
||||
if (!deploy_tree (self, deploy_target, revision, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!opt_no_kernel)
|
||||
{
|
||||
if (!do_update_kernel (self, deploy_path, cancellable, error))
|
||||
if (!do_update_kernel (self, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!complete_deployment (self, cancellable, error))
|
||||
goto out;
|
||||
|
||||
ret = TRUE;
|
||||
out:
|
||||
g_clear_object (&self->repo);
|
||||
g_free (self->osname);
|
||||
g_free (self->current_deployment_ref);
|
||||
g_free (self->previous_deployment_ref);
|
||||
g_free (self->resolved_commit);
|
||||
g_free (self->resolved_previous_commit);
|
||||
g_free (self->previous_deployment_revision);
|
||||
g_clear_object (&self->previous_deployment);
|
||||
g_clear_object (&self->ostree_dir);
|
||||
g_clear_object (&self->osname_dir);
|
||||
if (context)
|
||||
|
|
|
|||
Loading…
Reference in New Issue