Skip to content

Commit a80b83a

Browse files
committed
cli: support the stateroot option also on switch
draft, still working out the details and testing, this doesn't work yet # Background <hash> added the `stateroot` option to the `install` subcommand # Issue The `stateroot` option is not available on the `switch` subcommand # Solution Add the `stateroot` option to the `switch` subcommand # Implementation * If the stateroot is different than the current, we should allow using the same image as the currently booted one * Stateroot has to be explicitly created (`init_osname` binding) if it doesn't exist. If it does, we still call `init_osname` and simply ignore the error (TODO: only ignore non-already-exists errors) Signed-off-by: Omer Tuchfeld <[email protected]>
1 parent 1fd6c69 commit a80b83a

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

lib/src/cli.rs

+32-6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ pub(crate) struct SwitchOpts {
9999

100100
/// Target image to use for the next boot.
101101
pub(crate) target: String,
102+
103+
#[clap(long)]
104+
pub(crate) stateroot: Option<String>,
102105
}
103106

104107
/// Options controlling rollback
@@ -628,7 +631,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
628631
println!("No update available.")
629632
} else {
630633
let osname = booted_deployment.osname();
631-
crate::deploy::stage(sysroot, &osname, &fetched, &spec).await?;
634+
crate::deploy::stage(sysroot, &osname, &osname, &fetched, &spec).await?;
632635
changed = true;
633636
if let Some(prev) = booted_image.as_ref() {
634637
if let Some(fetched_manifest) = fetched.get_manifest(repo)? {
@@ -687,18 +690,42 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
687690
let (booted_deployment, _deployments, host) =
688691
crate::status::get_status_require_booted(sysroot)?;
689692

693+
let (old_stateroot, stateroot) = {
694+
let booted_osname = booted_deployment.osname();
695+
let stateroot = opts
696+
.stateroot
697+
.as_deref()
698+
.unwrap_or_else(|| booted_osname.as_str());
699+
700+
(booted_osname.to_owned(), stateroot.to_owned())
701+
};
702+
690703
let new_spec = {
691704
let mut new_spec = host.spec.clone();
692705
new_spec.image = Some(target.clone());
693706
new_spec
694707
};
695708

696-
if new_spec == host.spec {
697-
println!("Image specification is unchanged.");
709+
if new_spec == host.spec && old_stateroot == stateroot {
710+
// TODO: Should we really be confusing users with terms like "stateroot"?
711+
println!(
712+
"The currently running deployment in stateroot {stateroot} is already using this image"
713+
);
698714
return Ok(());
699715
}
700716
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;
701717

718+
if old_stateroot != stateroot {
719+
let init_result = sysroot.init_osname(&stateroot, cancellable);
720+
match init_result {
721+
Ok(_) => {}
722+
Err(err) => {
723+
// TODO: Only ignore non already-exists errors
724+
println!("Ignoring error creating new stateroot: {err}");
725+
}
726+
}
727+
}
728+
702729
let fetched = crate::deploy::pull(repo, &target, None, opts.quiet).await?;
703730

704731
if !opts.retain {
@@ -712,8 +739,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
712739
}
713740
}
714741

715-
let stateroot = booted_deployment.osname();
716-
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?;
742+
crate::deploy::stage(sysroot, &old_stateroot, &stateroot, &fetched, &new_spec).await?;
717743

718744
if opts.apply {
719745
crate::reboot::reboot()?;
@@ -766,7 +792,7 @@ async fn edit(opts: EditOpts) -> Result<()> {
766792
// TODO gc old layers here
767793

768794
let stateroot = booted_deployment.osname();
769-
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?;
795+
crate::deploy::stage(sysroot, &stateroot, &stateroot, &fetched, &new_spec).await?;
770796

771797
Ok(())
772798
}

lib/src/deploy.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@ async fn deploy(
373373
image: &ImageState,
374374
origin: &glib::KeyFile,
375375
) -> Result<Deployment> {
376-
let stateroot = Some(stateroot);
377376
let mut opts = ostree::SysrootDeployTreeOpts::default();
378377
// Compute the kernel argument overrides. In practice today this API is always expecting
379378
// a merge deployment. The kargs code also always looks at the booted root (which
@@ -396,7 +395,7 @@ async fn deploy(
396395
let cancellable = gio::Cancellable::NONE;
397396
return sysroot
398397
.stage_tree_with_options(
399-
stateroot,
398+
Some(stateroot),
400399
image.ostree_commit.as_str(),
401400
Some(origin),
402401
merge_deployment,
@@ -422,11 +421,12 @@ fn origin_from_imageref(imgref: &ImageReference) -> Result<glib::KeyFile> {
422421
#[context("Staging")]
423422
pub(crate) async fn stage(
424423
sysroot: &Storage,
424+
merge_stateroot: &str,
425425
stateroot: &str,
426426
image: &ImageState,
427427
spec: &RequiredHostSpec<'_>,
428428
) -> Result<()> {
429-
let merge_deployment = sysroot.merge_deployment(Some(stateroot));
429+
let merge_deployment = sysroot.merge_deployment(Some(merge_stateroot));
430430
let origin = origin_from_imageref(spec.image)?;
431431
let deployment = crate::deploy::deploy(
432432
sysroot,

0 commit comments

Comments
 (0)