44
55use std:: ffi:: { CString , OsStr , OsString } ;
66use std:: io:: Seek ;
7+ use std:: os:: fd:: RawFd ;
78use std:: os:: unix:: process:: CommandExt ;
89use std:: process:: Command ;
910
@@ -25,6 +26,8 @@ use serde::{Deserialize, Serialize};
2526
2627use crate :: deploy:: RequiredHostSpec ;
2728use crate :: lints;
29+ use crate :: progress_jsonl;
30+ use crate :: progress_jsonl:: ProgressWriter ;
2831use crate :: spec:: Host ;
2932use crate :: spec:: ImageReference ;
3033use crate :: utils:: sigpolicy_from_opts;
@@ -52,6 +55,10 @@ pub(crate) struct UpgradeOpts {
5255 /// a userspace-only restart.
5356 #[ clap( long, conflicts_with = "check" ) ]
5457 pub ( crate ) apply : bool ,
58+
59+ /// Pipe download progress to this fd in a jsonl format.
60+ #[ clap( long) ]
61+ pub ( crate ) json_fd : Option < RawFd > ,
5562}
5663
5764/// Perform an switch operation
@@ -101,6 +108,10 @@ pub(crate) struct SwitchOpts {
101108
102109 /// Target image to use for the next boot.
103110 pub ( crate ) target : String ,
111+
112+ /// Pipe download progress to this fd in a jsonl format.
113+ #[ clap( long) ]
114+ pub ( crate ) json_fd : Option < RawFd > ,
104115}
105116
106117/// Options controlling rollback
@@ -644,6 +655,12 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
644655 let ( booted_deployment, _deployments, host) =
645656 crate :: status:: get_status_require_booted ( sysroot) ?;
646657 let imgref = host. spec . image . as_ref ( ) ;
658+ let prog = opts
659+ . json_fd
660+ . map ( progress_jsonl:: ProgressWriter :: from_raw_fd)
661+ . transpose ( ) ?
662+ . unwrap_or_default ( ) ;
663+
647664 // If there's no specified image, let's be nice and check if the booted system is using rpm-ostree
648665 if imgref. is_none ( ) {
649666 let booted_incompatible = host
@@ -700,7 +717,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
700717 }
701718 }
702719 } else {
703- let fetched = crate :: deploy:: pull ( repo, imgref, None , opts. quiet ) . await ?;
720+ let fetched = crate :: deploy:: pull ( repo, imgref, None , opts. quiet , prog . clone ( ) ) . await ?;
704721 let staged_digest = staged_image. map ( |s| s. digest ( ) . expect ( "valid digest in status" ) ) ;
705722 let fetched_digest = & fetched. manifest_digest ;
706723 tracing:: debug!( "staged: {staged_digest:?}" ) ;
@@ -723,7 +740,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
723740 println ! ( "No update available." )
724741 } else {
725742 let osname = booted_deployment. osname ( ) ;
726- crate :: deploy:: stage ( sysroot, & osname, & fetched, & spec) . await ?;
743+ crate :: deploy:: stage ( sysroot, & osname, & fetched, & spec, prog . clone ( ) ) . await ?;
727744 changed = true ;
728745 if let Some ( prev) = booted_image. as_ref ( ) {
729746 if let Some ( fetched_manifest) = fetched. get_manifest ( repo) ? {
@@ -759,6 +776,11 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
759776 ) ;
760777 let target = ostree_container:: OstreeImageReference { sigverify, imgref } ;
761778 let target = ImageReference :: from ( target) ;
779+ let prog = opts
780+ . json_fd
781+ . map ( progress_jsonl:: ProgressWriter :: from_raw_fd)
782+ . transpose ( ) ?
783+ . unwrap_or_default ( ) ;
762784
763785 // If we're doing an in-place mutation, we shortcut most of the rest of the work here
764786 if opts. mutate_in_place {
@@ -794,7 +816,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
794816 }
795817 let new_spec = RequiredHostSpec :: from_spec ( & new_spec) ?;
796818
797- let fetched = crate :: deploy:: pull ( repo, & target, None , opts. quiet ) . await ?;
819+ let fetched = crate :: deploy:: pull ( repo, & target, None , opts. quiet , prog . clone ( ) ) . await ?;
798820
799821 if !opts. retain {
800822 // By default, we prune the previous ostree ref so it will go away after later upgrades
@@ -808,7 +830,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
808830 }
809831
810832 let stateroot = booted_deployment. osname ( ) ;
811- crate :: deploy:: stage ( sysroot, & stateroot, & fetched, & new_spec) . await ?;
833+ crate :: deploy:: stage ( sysroot, & stateroot, & fetched, & new_spec, prog . clone ( ) ) . await ?;
812834
813835 if opts. apply {
814836 crate :: reboot:: reboot ( ) ?;
@@ -850,18 +872,20 @@ async fn edit(opts: EditOpts) -> Result<()> {
850872 host. spec . verify_transition ( & new_host. spec ) ?;
851873 let new_spec = RequiredHostSpec :: from_spec ( & new_host. spec ) ?;
852874
875+ let prog = ProgressWriter :: default ( ) ;
876+
853877 // We only support two state transitions right now; switching the image,
854878 // or flipping the bootloader ordering.
855879 if host. spec . boot_order != new_host. spec . boot_order {
856880 return crate :: deploy:: rollback ( sysroot) . await ;
857881 }
858882
859- let fetched = crate :: deploy:: pull ( repo, new_spec. image , None , opts. quiet ) . await ?;
883+ let fetched = crate :: deploy:: pull ( repo, new_spec. image , None , opts. quiet , prog . clone ( ) ) . await ?;
860884
861885 // TODO gc old layers here
862886
863887 let stateroot = booted_deployment. osname ( ) ;
864- crate :: deploy:: stage ( sysroot, & stateroot, & fetched, & new_spec) . await ?;
888+ crate :: deploy:: stage ( sysroot, & stateroot, & fetched, & new_spec, prog . clone ( ) ) . await ?;
865889
866890 Ok ( ( ) )
867891}
0 commit comments