@@ -16,9 +16,11 @@ use ostree_ext::keyfileext::KeyFileExt;
16
16
use ostree_ext:: ostree;
17
17
use ostree_ext:: sysroot:: SysrootLock ;
18
18
use std:: ffi:: OsString ;
19
+ use std:: io:: Seek ;
19
20
use std:: os:: unix:: process:: CommandExt ;
20
21
use std:: process:: Command ;
21
22
23
+ use crate :: spec:: Host ;
22
24
use crate :: spec:: HostSpec ;
23
25
use crate :: spec:: ImageReference ;
24
26
@@ -64,7 +66,18 @@ pub(crate) struct SwitchOpts {
64
66
pub ( crate ) target : String ,
65
67
}
66
68
67
- /// Perform a status operation
69
+ /// Perform an edit operation
70
+ #[ derive( Debug , Parser ) ]
71
+ pub ( crate ) struct EditOpts {
72
+ /// Path to new system specification; use `-` for stdin
73
+ pub ( crate ) filename : String ,
74
+
75
+ /// Don't display progress
76
+ #[ clap( long) ]
77
+ pub ( crate ) quiet : bool ,
78
+ }
79
+
80
+ /// Perform an status operation
68
81
#[ derive( Debug , Parser ) ]
69
82
pub ( crate ) struct StatusOpts {
70
83
/// Output in JSON format.
@@ -111,6 +124,8 @@ pub(crate) enum Opt {
111
124
Upgrade ( UpgradeOpts ) ,
112
125
/// Target a new container image reference to boot.
113
126
Switch ( SwitchOpts ) ,
127
+ /// Change host specification
128
+ Edit ( EditOpts ) ,
114
129
/// Display status
115
130
Status ( StatusOpts ) ,
116
131
/// Add a transient writable overlayfs on `/usr` that will be discarded on reboot.
@@ -405,6 +420,44 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
405
420
Ok ( ( ) )
406
421
}
407
422
423
+ /// Implementation of the `bootc edit` CLI command.
424
+ #[ context( "Editing spec" ) ]
425
+ async fn edit ( opts : EditOpts ) -> Result < ( ) > {
426
+ prepare_for_write ( ) . await ?;
427
+ let sysroot = & get_locked_sysroot ( ) . await ?;
428
+ let repo = & sysroot. repo ( ) ;
429
+ let booted_deployment = & sysroot. require_booted_deployment ( ) ?;
430
+ let ( _deployments, host) = crate :: status:: get_status ( sysroot, Some ( booted_deployment) ) ?;
431
+
432
+ let new_host: Host = if opts. filename == "-" {
433
+ let tmpf = tempfile:: NamedTempFile :: new ( ) ?;
434
+ serde_yaml:: to_writer ( std:: io:: BufWriter :: new ( tmpf. as_file ( ) ) , & host) ?;
435
+ crate :: utils:: spawn_editor ( & tmpf) ?;
436
+ tmpf. as_file ( ) . seek ( std:: io:: SeekFrom :: Start ( 0 ) ) ?;
437
+ serde_yaml:: from_reader ( & mut tmpf. as_file ( ) ) ?
438
+ } else {
439
+ let mut r = std:: io:: BufReader :: new ( std:: fs:: File :: open ( opts. filename ) ?) ;
440
+ serde_yaml:: from_reader ( & mut r) ?
441
+ } ;
442
+
443
+ if new_host. spec == host. spec {
444
+ anyhow:: bail!( "No changes in current host spec" ) ;
445
+ }
446
+ let new_image = new_host
447
+ . spec
448
+ . image
449
+ . as_ref ( )
450
+ . ok_or_else ( || anyhow:: anyhow!( "Unable to transition to unset image" ) ) ?;
451
+ let fetched = pull ( repo, new_image, opts. quiet ) . await ?;
452
+
453
+ // TODO gc old layers here
454
+
455
+ let stateroot = booted_deployment. osname ( ) ;
456
+ stage ( sysroot, & stateroot, fetched, & new_host. spec ) . await ?;
457
+
458
+ Ok ( ( ) )
459
+ }
460
+
408
461
/// Implementation of `bootc usroverlay`
409
462
async fn usroverlay ( ) -> Result < ( ) > {
410
463
// This is just a pass-through today. At some point we may make this a libostree API
@@ -426,6 +479,7 @@ where
426
479
match opt {
427
480
Opt :: Upgrade ( opts) => upgrade ( opts) . await ,
428
481
Opt :: Switch ( opts) => switch ( opts) . await ,
482
+ Opt :: Edit ( opts) => edit ( opts) . await ,
429
483
Opt :: UsrOverlay => usroverlay ( ) . await ,
430
484
#[ cfg( feature = "install" ) ]
431
485
Opt :: Install ( opts) => crate :: install:: install ( opts) . await ,
0 commit comments