ssh: 'send-files' displays a progress bar.

* guix/store.scm (export-paths): Add #:start, #:progress, and #:finish
parameters and honor them.
* guix/ssh.scm (prepare-to-send, notify-transfer-progress)
(notify-transfer-completion): New procedures.
(send-files): Pass #:start, #:progress, and #:finish to 'export-paths'.
This commit is contained in:
Ludovic Courtès 2020-08-31 11:36:26 +02:00
parent 7ae0456166
commit b03267df6d
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
2 changed files with 83 additions and 18 deletions

View file

@ -20,7 +20,11 @@ (define-module (guix ssh)
#:use-module (guix store)
#:use-module (guix inferior)
#:use-module (guix i18n)
#:use-module ((guix diagnostics) #:select (&fix-hint formatted-message))
#:use-module ((guix diagnostics)
#:select (info &fix-hint formatted-message))
#:use-module ((guix progress)
#:select (progress-bar
erase-current-line current-terminal-columns))
#:use-module (gcrypt pk-crypto)
#:use-module (ssh session)
#:use-module (ssh auth)
@ -36,6 +40,7 @@ (define-module (guix ssh)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
#:use-module (ice-9 binary-ports)
#:use-module (ice-9 vlist)
#:export (open-ssh-session
authenticate-server*
@ -402,6 +407,55 @@ (define key (string->canonical-sexp ,(canonical-sexp->string key)))
session
become-command))
(define (prepare-to-send store host log-port items)
"Notify the user that we're about to send ITEMS to HOST. Return three
values allowing 'notify-send-progress' to track the state of this transfer."
(let* ((count (length items))
(sizes (fold (lambda (item result)
(vhash-cons item
(path-info-nar-size
(query-path-info store item))
result))
vlist-null
items))
(total (vlist-fold (lambda (pair result)
(match pair
((_ . size) (+ size result))))
0
sizes)))
(info (N_ "sending ~a store item (~h MiB) to '~a'...~%"
"sending ~a store items (~h MiB) to '~a'...~%" count)
count
(inexact->exact (round (/ total (expt 2. 20))))
host)
(values log-port sizes total 0)))
(define (notify-transfer-progress item port sizes total sent)
"Notify the user that we've already transferred SENT bytes out of TOTAL.
Use SIZES to determine the size of ITEM, which is about to be sent."
(define (display-bar %)
(erase-current-line port)
(format port "~3@a% ~a"
(inexact->exact (round (* 100. (/ sent total))))
(progress-bar % (- (max (current-terminal-columns) 5) 5)))
(force-output port))
(let ((% (* 100. (/ sent total))))
(match (vhash-assoc item sizes)
(#f
(display-bar %)
(values port sizes total sent))
((_ . size)
(display-bar %)
(values port sizes total (+ sent size))))))
(define (notify-transfer-completion port . args)
"Notify the user that the transfer has completed."
(apply notify-transfer-progress "" port args) ;display the 100% progress bar
(erase-current-line port)
(force-output port))
(define* (send-files local files remote
#:key
recursive?
@ -412,7 +466,7 @@ (define* (send-files local files remote
;; Compute the subset of FILES missing on SESSION and send them.
(let* ((files (if recursive? (requisites local files) files))
(session (channel-get-session (store-connection-socket remote)))
(missing (inferior-remote-eval
(missing (take files 20) #;(inferior-remote-eval
`(begin
(use-modules (guix)
(srfi srfi-1) (srfi srfi-26))
@ -421,11 +475,8 @@ (define* (send-files local files remote
(remove (cut valid-path? store <>)
',files)))
session))
(count (length missing))
(sizes (map (lambda (item)
(path-info-nar-size (query-path-info local item)))
missing))
(port (store-import-channel session)))
(port (store-import-channel session))
(host (session-get session 'host)))
;; Make sure everything alright on the remote side.
(match (read port)
(('importing)
@ -433,14 +484,12 @@ (define* (send-files local files remote
(sexp
(handle-import/export-channel-error sexp remote)))
(format log-port (N_ "sending ~a store item (~h MiB) to '~a'...~%"
"sending ~a store items (~h MiB) to '~a'...~%" count)
count
(inexact->exact (round (/ (reduce + 0 sizes) (expt 2. 20))))
(session-get session 'host))
;; Send MISSING in topological order.
(export-paths local missing port)
(let ((tty? (isatty? log-port)))
(export-paths local missing port
#:start (cut prepare-to-send local host log-port <>)
#:progress (if tty? notify-transfer-progress (const #f))
#:finish (if tty? notify-transfer-completion (const #f))))
;; Tell the remote process that we're done. (In theory the end-of-archive
;; mark of 'export-paths' would be enough, but in practice it's not.)

View file

@ -1728,10 +1728,20 @@ (define* (export-path server path port #:key (sign? #t))
(or done? (loop (process-stderr server port))))
(= 1 (read-int s))))
(define* (export-paths server paths port #:key (sign? #t) recursive?)
(define* (export-paths server paths port #:key (sign? #t) recursive?
(start (const #f))
(progress (const #f))
(finish (const #f)))
"Export the store paths listed in PATHS to PORT, in topological order,
signing them if SIGN? is true. When RECURSIVE? is true, export the closure of
PATHS---i.e., PATHS and all their dependencies."
PATHS---i.e., PATHS and all their dependencies.
START, PROGRESS, and FINISH are used to track progress of the data transfer.
START is a one-argument that is passed the list of store items that will be
transferred; it returns values that are then used as the initial state
threaded through PROGRESS calls. PROGRESS is passed the store item about to
be sent, along with the values previously return by START or by PROGRESS
itself. FINISH is called when the last store item has been called."
(define ordered
(let ((sorted (topologically-sorted server paths)))
;; When RECURSIVE? is #f, filter out the references of PATHS.
@ -1739,14 +1749,20 @@ (define ordered
sorted
(filter (cut member <> paths) sorted))))
(let loop ((paths ordered))
(let loop ((paths ordered)
(state (call-with-values (lambda () (start ordered))
list)))
(match paths
(()
(apply finish state)
(write-int 0 port))
((head tail ...)
(write-int 1 port)
(and (export-path server head port #:sign? sign?)
(loop tail))))))
(loop tail
(call-with-values
(lambda () (apply progress head state))
list)))))))
(define-operation (query-failed-paths)
"Return the list of store items for which a build failure is cached.