substitute: Honor all the specified server URLs.

* guix/scripts/substitute.scm (lookup-narinfos/diverse): New procedure.
  (lookup-narinfo): Use it.
  (process-query): Change #:cache-url to #:cache-urls.
  [valid?]: Remove 'narinfo?' check, which is no longer necessary.
  Use 'lookup-narinfos/diverse' instead of 'lookup-narinfos'.
  (process-substitution): Change #:cache-url to #:cache-urls.
  (%cache-url): Rename to...
  (%cache-urls): ... this.  Turn into a list.
  (guix-substitute): Remove 'getaddrinfo' test with early exit.  Adjust
  calls to 'process-query' and 'process-substitution'.
* tests/substitute.scm: Change '%cache-url' to '%cache-urls'.
This commit is contained in:
Ludovic Courtès 2015-10-28 10:11:43 +01:00
parent a89dde1ed8
commit 55b2fc1877
2 changed files with 46 additions and 41 deletions

View File

@ -72,6 +72,7 @@
assert-valid-narinfo assert-valid-narinfo
lookup-narinfos lookup-narinfos
lookup-narinfos/diverse
read-narinfo read-narinfo
write-narinfo write-narinfo
guix-substitute)) guix-substitute))
@ -610,11 +611,32 @@ information is available locally."
(let ((missing (fetch-narinfos cache missing))) (let ((missing (fetch-narinfos cache missing)))
(append cached (or missing '())))))) (append cached (or missing '()))))))
(define (lookup-narinfo cache path) (define (lookup-narinfos/diverse caches paths)
"Return the narinfo for PATH in CACHE, or #f when no substitute for PATH was "Look up narinfos for PATHS on all of CACHES, a list of URLS, in that order.
found." That is, when a cache lacks a narinfo, look it up in the next cache, and so
(match (lookup-narinfos cache (list path)) on. Return a list of narinfos for PATHS or a subset thereof."
((answer) answer))) (let loop ((caches caches)
(paths paths)
(result '()))
(match paths
(() ;we're done
result)
(_
(match caches
((cache rest ...)
(let* ((narinfos (lookup-narinfos cache paths))
(hits (map narinfo-path narinfos))
(missing (lset-difference string=? paths hits))) ;XXX: perf
(loop rest missing (append narinfos result))))
(() ;that's it
result))))))
(define (lookup-narinfo caches path)
"Return the narinfo for PATH in CACHES, or #f when no substitute for PATH
was found."
(match (lookup-narinfos/diverse caches (list path))
((answer) answer)
(_ #f)))
(define (remove-expired-cached-narinfos directory) (define (remove-expired-cached-narinfos directory)
"Remove expired narinfo entries from DIRECTORY. The sole purpose of this "Remove expired narinfo entries from DIRECTORY. The sole purpose of this
@ -756,34 +778,34 @@ expected by the daemon."
(or (narinfo-size narinfo) 0))) (or (narinfo-size narinfo) 0)))
(define* (process-query command (define* (process-query command
#:key cache-url acl) #:key cache-urls acl)
"Reply to COMMAND, a query as written by the daemon to this process's "Reply to COMMAND, a query as written by the daemon to this process's
standard input. Use ACL as the access-control list against which to check standard input. Use ACL as the access-control list against which to check
authorized substitutes." authorized substitutes."
(define (valid? obj) (define (valid? obj)
(and (narinfo? obj) (valid-narinfo? obj acl))) (valid-narinfo? obj acl))
(match (string-tokenize command) (match (string-tokenize command)
(("have" paths ..1) (("have" paths ..1)
;; Return the subset of PATHS available in CACHE-URL. ;; Return the subset of PATHS available in CACHE-URLS.
(let ((substitutable (lookup-narinfos cache-url paths))) (let ((substitutable (lookup-narinfos/diverse cache-urls paths)))
(for-each (lambda (narinfo) (for-each (lambda (narinfo)
(format #t "~a~%" (narinfo-path narinfo))) (format #t "~a~%" (narinfo-path narinfo)))
(filter valid? substitutable)) (filter valid? substitutable))
(newline))) (newline)))
(("info" paths ..1) (("info" paths ..1)
;; Reply info about PATHS if it's in CACHE-URL. ;; Reply info about PATHS if it's in CACHE-URLS.
(let ((substitutable (lookup-narinfos cache-url paths))) (let ((substitutable (lookup-narinfos/diverse cache-urls paths)))
(for-each display-narinfo-data (filter valid? substitutable)) (for-each display-narinfo-data (filter valid? substitutable))
(newline))) (newline)))
(wtf (wtf
(error "unknown `--query' command" wtf)))) (error "unknown `--query' command" wtf))))
(define* (process-substitution store-item destination (define* (process-substitution store-item destination
#:key cache-url acl) #:key cache-urls acl)
"Substitute STORE-ITEM (a store file name) from CACHE-URL, and write it to "Substitute STORE-ITEM (a store file name) from CACHE-URLS, and write it to
DESTINATION as a nar file. Verify the substitute against ACL." DESTINATION as a nar file. Verify the substitute against ACL."
(let* ((narinfo (lookup-narinfo cache-url store-item)) (let* ((narinfo (lookup-narinfo cache-urls store-item))
(uri (narinfo-uri narinfo))) (uri (narinfo-uri narinfo)))
;; Make sure it is signed and everything. ;; Make sure it is signed and everything.
(assert-valid-narinfo narinfo acl) (assert-valid-narinfo narinfo acl)
@ -880,21 +902,16 @@ found."
b b
first))) first)))
(define %cache-url (define %cache-urls
(match (and=> (or* (find-daemon-option "untrusted-substitute-urls") ;client (match (and=> (or* (find-daemon-option "untrusted-substitute-urls") ;client
(find-daemon-option "substitute-urls")) ;admin (find-daemon-option "substitute-urls")) ;admin
string-tokenize) string-tokenize)
((url) ((urls ...)
url) urls)
((head tail ..1)
;; Currently we don't handle multiple substitute URLs.
(warning (_ "these substitute URLs will not be used:~{ ~a~}~%")
tail)
head)
(#f (#f
;; This can only happen when this script is not invoked by the ;; This can only happen when this script is not invoked by the
;; daemon. ;; daemon.
"http://hydra.gnu.org"))) '("http://hydra.gnu.org"))))
(define (guix-substitute . args) (define (guix-substitute . args)
"Implement the build daemon's substituter protocol." "Implement the build daemon's substituter protocol."
@ -905,20 +922,8 @@ found."
;; Starting from commit 22144afa in Nix, we are allowed to bail out directly ;; Starting from commit 22144afa in Nix, we are allowed to bail out directly
;; when we know we cannot substitute, but we must emit a newline on stdout ;; when we know we cannot substitute, but we must emit a newline on stdout
;; when everything is alright. ;; when everything is alright.
(let ((uri (string->uri %cache-url))) (when (null? %cache-urls)
(case (uri-scheme uri) (exit 0))
((http)
;; Exit gracefully if there's no network access.
(let ((host (uri-host uri)))
(catch 'getaddrinfo-error
(lambda ()
(getaddrinfo host))
(lambda (key error)
(warning (_ "failed to look up host '~a' (~a), \
substituter disabled~%")
host (gai-strerror error))
(exit 0)))))
(else #t)))
;; Say hello (see above.) ;; Say hello (see above.)
(newline) (newline)
@ -933,13 +938,13 @@ substituter disabled~%")
(or (eof-object? command) (or (eof-object? command)
(begin (begin
(process-query command (process-query command
#:cache-url %cache-url #:cache-urls %cache-urls
#:acl acl) #:acl acl)
(loop (read-line))))))) (loop (read-line)))))))
(("--substitute" store-path destination) (("--substitute" store-path destination)
;; Download STORE-PATH and add store it as a Nar in file DESTINATION. ;; Download STORE-PATH and add store it as a Nar in file DESTINATION.
(process-substitution store-path destination (process-substitution store-path destination
#:cache-url %cache-url #:cache-urls %cache-urls
#:acl (current-acl))) #:acl (current-acl)))
(("--version") (("--version")
(show-version-and-exit "guix substitute")) (show-version-and-exit "guix substitute"))

View File

@ -167,8 +167,8 @@ a file for NARINFO."
(call-with-narinfo narinfo (lambda () body ...))) (call-with-narinfo narinfo (lambda () body ...)))
;; Transmit these options to 'guix substitute'. ;; Transmit these options to 'guix substitute'.
(set! (@@ (guix scripts substitute) %cache-url) (set! (@@ (guix scripts substitute) %cache-urls)
(getenv "GUIX_BINARY_SUBSTITUTE_URL")) (list (getenv "GUIX_BINARY_SUBSTITUTE_URL")))
(test-equal "query narinfo without signature" (test-equal "query narinfo without signature"
"" ; not substitutable "" ; not substitutable