[PATCH] scripts: substitute: Improve fetch-narinfos progress reporting.

  • Done
  • quality assurance status badge
Details
2 participants
  • Ludovic Courtès
  • Christopher Baines
Owner
unassigned
Submitted by
Christopher Baines
Severity
normal
C
C
Christopher Baines wrote on 9 Dec 2020 19:57
(address . guix-patches@gnu.org)
20201209185759.30937-1-mail@cbaines.net
At least in guix weather, these changes make the progress bar actually appear.

* guix/scripts/substitute.scm (fetch-narinfos): Use (guix progress) for
progress reporting.
---
guix/scripts/substitute.scm | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)

Toggle diff (52 lines)
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index 25075eedff..5128310439 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -614,16 +614,8 @@ print a warning and return #f."
(define (fetch-narinfos url paths)
"Retrieve all the narinfos for PATHS from the cache at URL and return them."
- (define update-progress!
- (let ((done 0)
- (total (length paths)))
- (lambda ()
- (display "\r\x1b[K" (current-error-port)) ;erase current line
- (force-output (current-error-port))
- (format (current-error-port)
- (G_ "updating substitutes from '~a'... ~5,1f%")
- url (* 100. (/ done total)))
- (set! done (+ 1 done)))))
+ (define fetch-narinfos-progress-reporter
+ (progress-reporter/bar (length paths)))
(define hash-part->path
(let ((mapping (fold (lambda (path result)
@@ -641,7 +633,7 @@ print a warning and return #f."
(len (response-content-length response))
(cache (response-cache-control response))
(ttl (and cache (assoc-ref cache 'max-age))))
- (update-progress!)
+ (progress-reporter-report! fetch-narinfos-progress-reporter)
;; Make sure to read no more than LEN bytes since subsequent bytes may
;; belong to the next response.
@@ -673,7 +665,7 @@ print a warning and return #f."
(#f
'())
(port
- (update-progress!)
+ (start-progress-reporter! fetch-narinfos-progress-reporter)
;; Note: Do not check HTTPS server certificates to avoid depending
;; on the X.509 PKI. We can do it because we authenticate
;; narinfos, which provides a much stronger guarantee.
@@ -683,7 +675,7 @@ print a warning and return #f."
#:verify-certificate? #f
#:port port)))
(close-port port)
- (newline (current-error-port))
+ (stop-progress-reporter! fetch-narinfos-progress-reporter)
result)))))
((file #f)
(let* ((base (string-append (uri-path uri) "/"))
--
2.29.2
L
L
Ludovic Courtès wrote on 11 Dec 2020 19:01
(name . Christopher Baines)(address . mail@cbaines.net)(address . 45146@debbugs.gnu.org)
87mtyk43y7.fsf@gnu.org
Hi,

Christopher Baines <mail@cbaines.net> skribis:

Toggle quote (5 lines)
> At least in guix weather, these changes make the progress bar actually appear.
>
> * guix/scripts/substitute.scm (fetch-narinfos): Use (guix progress) for
> progress reporting.

Cool. I noticed that something was wrong with ‘guix weather’, but I
suspected it had to do with the order in which the erase-line sequence
and \r are sent.

Toggle quote (10 lines)
> - (lambda ()
> - (display "\r\x1b[K" (current-error-port)) ;erase current line
> - (force-output (current-error-port))
> - (format (current-error-port)
> - (G_ "updating substitutes from '~a'... ~5,1f%")
> - url (* 100. (/ done total)))
> - (set! done (+ 1 done)))))
> + (define fetch-narinfos-progress-reporter
> + (progress-reporter/bar (length paths)))

The problem here is that we’d see a progress bar without knowing what it
represents.

Besides, currently output from ‘guix substitute’ is printed as is by
client commands, regardless of whether stdout is a tty. The problem
already exists but it would become a bit more visible as logs get filled
with progress bars.

Ludo’.
C
C
Christopher Baines wrote on 24 Dec 2020 18:26
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 45146@debbugs.gnu.org)
87v9cr9kth.fsf@cbaines.net
Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (29 lines)
> Christopher Baines <mail@cbaines.net> skribis:
>
>> At least in guix weather, these changes make the progress bar actually appear.
>>
>> * guix/scripts/substitute.scm (fetch-narinfos): Use (guix progress) for
>> progress reporting.
>
> Cool. I noticed that something was wrong with ‘guix weather’, but I
> suspected it had to do with the order in which the erase-line sequence
> and \r are sent.
>
>> - (lambda ()
>> - (display "\r\x1b[K" (current-error-port)) ;erase current line
>> - (force-output (current-error-port))
>> - (format (current-error-port)
>> - (G_ "updating substitutes from '~a'... ~5,1f%")
>> - url (* 100. (/ done total)))
>> - (set! done (+ 1 done)))))
>> + (define fetch-narinfos-progress-reporter
>> + (progress-reporter/bar (length paths)))
>
> The problem here is that we’d see a progress bar without knowing what it
> represents.
>
> Besides, currently output from ‘guix substitute’ is printed as is by
> client commands, regardless of whether stdout is a tty. The problem
> already exists but it would become a bit more visible as logs get filled
> with progress bars.

Maybe it's best to circle back to fixing guix weather after trying to
restructure some of the guix substitute code.

I've made an initial attempt at moving things around in [1]. If the
underlying code can live in a module, and then the substitute, weather
and challenge scripts use that code with whatever UI stuff they want,
maybe that will allow for better addressing this weather specific issue.

-----BEGIN PGP SIGNATURE-----

iQKlBAEBCgCPFiEEPonu50WOcg2XVOCyXiijOwuE9XcFAl/kz1tfFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDNF
ODlFRUU3NDU4RTcyMEQ5NzU0RTBCMjVFMjhBMzNCMEI4NEY1NzcRHG1haWxAY2Jh
aW5lcy5uZXQACgkQXiijOwuE9XfAzw/+MYsnE4+kZ0dDKT+ftnyHxzo4lJ827PXJ
ABTntv3qoF7ZhopXURCK5D3Vd5+GhzydEM9cvP7iKkk52i42omBen5rBcsZYxpML
PpUVjc6NtMtWcMBEWWnQ+hsP7h46J1vK0iG4wxAfBwl0eL0aZiGyoppVx5jUiyTz
M3UyfXl0vScYXcdGUdFA/itUikUXjzdL9TVdbVbNi3sa/AXH21rs1SxOP+r7xG7x
g3DUL5ARKhHQuyMSzTlKNTnj1Tu/5W43+kx0qE64d9uJtTG14tMKP5REP/vMCzSV
rwBaNnfdsbOpmHaWj9UigIlNLQb0l5jM78N534ofuS3zhINnvD7y8cHeOhBldVPK
2NOsBjf3me2LB+pkUdXH9037vwMkN8f9826YbaYFhhcbYrTsU9RZ3uABFLrVGn/9
RJySJ7feTg0iWuRtaYFSIO1Aa9vhQYdTJ75YQZ5I191cF8Pm5AqxLkjb40rgUCj+
he9/yIV++vdNWCzZwuw+7Gto6P/4mCtWHcxlVmM5qsXWt//bk/73bC9vOuCe79sD
xN1hqWc118zJwn3SLgLBqU1+FCbQwOlL8RW23jU1yfBOmPj24IKp1thOaRnnsXNF
4H0X+2Z3kVw0ZIbpVjsVFrSamfgl6eRYGKinHx2QJQxmVMCLoepQR3tF6NPUrCOc
BwpTbFwiRcA=
=mDMM
-----END PGP SIGNATURE-----

C
C
Christopher Baines wrote on 24 Feb 2021 21:34
[PATCH 1/2] guix: substitutes: Make progress reporting configurable.
(address . 45146@debbugs.gnu.org)
20210224203412.15135-1-mail@cbaines.net
Rather than always outputting to (current-error-port) in
lookup-narinfos (which is called from within lookup-narinfos/diverse), take a
procedure which should return a progress reporter, and defer any output to
that.

As this is now general purpose code, make the default behaviour to output
nothing. Maintain the current behaviour of the substitute script by moving the
progress reporter implementation there, and passing it in when calling
lookup-narinfos/diverse.

These changes should be generally useful, but I'm particularly looking at
getting guix weather to do progress reporting differently, with this new
flexibility.

* guix/substitutes.scm (fetch-narinfos): Take a procedure to make a
progress-reporter, and use that rather than the hardcoded behaviour.
(lookup-narinfos): Add #:make-progress-reporter keyword argument, and pass
this through to fetch-narinfos.
(lookup-narinfos/diverse): Add a #:make-progress-reporter keyword argument,
and pass this through to lookup-narinfos.
* guix/scripts/substitute.scm (process-query): Pass a progress-reporter to
lookup-narinfos/diverse.
---
guix/scripts/substitute.scm | 23 +++++++++++++++++++--
guix/substitutes.scm | 40 ++++++++++++++++++++-----------------
2 files changed, 43 insertions(+), 20 deletions(-)

Toggle diff (146 lines)
diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm
index ed19e67531..47a723edb2 100755
--- a/guix/scripts/substitute.scm
+++ b/guix/scripts/substitute.scm
@@ -298,12 +298,30 @@ authorized substitutes."
(lambda (obj)
(valid-narinfo? obj acl))))
+ (define* (make-progress-reporter total #:key url)
+ (define done 0)
+
+ (define (report-progress)
+ (erase-current-line (current-error-port)) ;erase current line
+ (force-output (current-error-port))
+ (format (current-error-port)
+ (G_ "updating substitutes from '~a'... ~5,1f%")
+ url (* 100. (/ done total)))
+ (set! done (+ 1 done)))
+
+ (progress-reporter
+ (start report-progress)
+ (report report-progress)
+ (stop (lambda ()
+ (newline (current-error-port))))))
+
(match (string-tokenize command)
(("have" paths ..1)
;; Return the subset of PATHS available in CACHE-URLS.
(let ((substitutable (lookup-narinfos/diverse
cache-urls paths valid?
- #:open-connection open-connection-for-uri/cached)))
+ #:open-connection open-connection-for-uri/cached
+ #:make-progress-reporter make-progress-reporter)))
(for-each (lambda (narinfo)
(format #t "~a~%" (narinfo-path narinfo)))
substitutable)
@@ -312,7 +330,8 @@ authorized substitutes."
;; Reply info about PATHS if it's in CACHE-URLS.
(let ((substitutable (lookup-narinfos/diverse
cache-urls paths valid?
- #:open-connection open-connection-for-uri/cached)))
+ #:open-connection open-connection-for-uri/cached
+ #:make-progress-reporter make-progress-reporter)))
(for-each display-narinfo-data substitutable)
(newline)))
(wtf
diff --git a/guix/substitutes.scm b/guix/substitutes.scm
index dc94ccc8e4..ef78013659 100644
--- a/guix/substitutes.scm
+++ b/guix/substitutes.scm
@@ -173,18 +173,14 @@ if file doesn't exist, and the narinfo otherwise."
(apply throw args)))))
(define* (fetch-narinfos url paths
- #:key (open-connection guix:open-connection-for-uri))
+ #:key
+ (open-connection guix:open-connection-for-uri)
+ (make-progress-reporter
+ (const progress-reporter/silent)))
"Retrieve all the narinfos for PATHS from the cache at URL and return them."
- (define update-progress!
- (let ((done 0)
- (total (length paths)))
- (lambda ()
- (display "\r\x1b[K" (current-error-port)) ;erase current line
- (force-output (current-error-port))
- (format (current-error-port)
- (G_ "updating substitutes from '~a'... ~5,1f%")
- url (* 100. (/ done total)))
- (set! done (+ 1 done)))))
+ (define progress-reporter
+ (make-progress-reporter (length paths)
+ #:url url))
(define hash-part->path
(let ((mapping (fold (lambda (path result)
@@ -206,7 +202,7 @@ if file doesn't exist, and the narinfo otherwise."
(len (response-content-length response))
(cache (response-cache-control response))
(ttl (and cache (assoc-ref cache 'max-age))))
- (update-progress!)
+ (progress-reporter-report! progress-reporter)
;; Make sure to read no more than LEN bytes since subsequent bytes may
;; belong to the next response.
@@ -238,7 +234,7 @@ if file doesn't exist, and the narinfo otherwise."
;; narinfos, which provides a much stronger guarantee.
(let* ((requests (map (cut narinfo-request url <>) paths))
(result (begin
- (update-progress!)
+ (start-progress-reporter! progress-reporter)
(call-with-connection-error-handling
uri
(lambda ()
@@ -247,7 +243,7 @@ if file doesn't exist, and the narinfo otherwise."
requests
#:open-connection open-connection
#:verify-certificate? #f))))))
- (newline (current-error-port))
+ (stop-progress-reporter! progress-reporter)
result))
((file #f)
(let* ((base (string-append (uri-path uri) "/"))
@@ -297,7 +293,9 @@ for PATH."
(values #f #f))))
(define* (lookup-narinfos cache paths
- #:key (open-connection guix:open-connection-for-uri))
+ #:key (open-connection guix:open-connection-for-uri)
+ (make-progress-reporter
+ (const progress-reporter/silent)))
"Return the narinfos for PATHS, invoking the server at CACHE when no
information is available locally."
(let-values (((cached missing)
@@ -315,12 +313,16 @@ information is available locally."
(if (null? missing)
cached
(let ((missing (fetch-narinfos cache missing
- #:open-connection open-connection)))
+ #:open-connection open-connection
+ #:make-progress-reporter
+ make-progress-reporter)))
(append cached (or missing '()))))))
(define* (lookup-narinfos/diverse caches paths authorized?
#:key (open-connection
- guix:open-connection-for-uri))
+ guix:open-connection-for-uri)
+ (make-progress-reporter
+ (const progress-reporter/silent)))
"Look up narinfos for PATHS on all of CACHES, a list of URLS, in that order.
That is, when a cache lacks an AUTHORIZED? narinfo, look it up in the next
cache, and so on.
@@ -353,7 +355,9 @@ AUTHORIZED? narinfo."
(match caches
((cache rest ...)
(let* ((narinfos (lookup-narinfos cache paths
- #:open-connection open-connection))
+ #:open-connection open-connection
+ #:make-progress-reporter
+ make-progress-reporter))
(definite (map narinfo-path (filter authorized? narinfos)))
(missing (lset-difference string=? paths definite))) ;XXX: perf
(loop rest missing
--
2.30.0
C
C
Christopher Baines wrote on 24 Feb 2021 21:34
[PATCH 2/2] weather: Call lookup-narinfos with a custom progress reporter.
(address . 45146@debbugs.gnu.org)
20210224203412.15135-2-mail@cbaines.net
This means there's a useful progress bar when running guix weather.

* guix/scripts/weather.scm (report-server-coverage): Pass
#:make-progress-reporter to lookup-narinfos.
---
guix/scripts/weather.scm | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

Toggle diff (19 lines)
diff --git a/guix/scripts/weather.scm b/guix/scripts/weather.scm
index 9e94bff5a3..26ec543211 100644
--- a/guix/scripts/weather.scm
+++ b/guix/scripts/weather.scm
@@ -181,7 +181,11 @@ Return the coverage ratio, an exact number between 0 and 1."
(format #t (G_ "looking for ~h store items on ~a...~%")
(length items) server)
- (let/time ((time narinfos (lookup-narinfos server items)))
+ (let/time ((time narinfos (lookup-narinfos
+ server items
+ #:make-progress-reporter
+ (lambda* (total #:key url #:allow-other-keys)
+ (progress-reporter/bar total)))))
(format #t "~a~%" server)
(let ((obtained (length narinfos))
(requested (length items))
--
2.30.0
C
C
Christopher Baines wrote on 24 Feb 2021 21:44
Re: [bug#45146] [PATCH] scripts: substitute: Improve fetch-narinfos progress reporting.
(name . Ludovic Courtès)(address . ludo@gnu.org)(address . 45146@debbugs.gnu.org)
87y2fdjiuy.fsf@cbaines.net
Christopher Baines <mail@cbaines.net> writes:

Toggle quote (41 lines)
> Ludovic Courtès <ludo@gnu.org> writes:
>
>> Christopher Baines <mail@cbaines.net> skribis:
>>
>>> At least in guix weather, these changes make the progress bar actually appear.
>>>
>>> * guix/scripts/substitute.scm (fetch-narinfos): Use (guix progress) for
>>> progress reporting.
>>
>> Cool. I noticed that something was wrong with ‘guix weather’, but I
>> suspected it had to do with the order in which the erase-line sequence
>> and \r are sent.
>>
>>> - (lambda ()
>>> - (display "\r\x1b[K" (current-error-port)) ;erase current line
>>> - (force-output (current-error-port))
>>> - (format (current-error-port)
>>> - (G_ "updating substitutes from '~a'... ~5,1f%")
>>> - url (* 100. (/ done total)))
>>> - (set! done (+ 1 done)))))
>>> + (define fetch-narinfos-progress-reporter
>>> + (progress-reporter/bar (length paths)))
>>
>> The problem here is that we’d see a progress bar without knowing what it
>> represents.
>>
>> Besides, currently output from ‘guix substitute’ is printed as is by
>> client commands, regardless of whether stdout is a tty. The problem
>> already exists but it would become a bit more visible as logs get filled
>> with progress bars.
>
> Maybe it's best to circle back to fixing guix weather after trying to
> restructure some of the guix substitute code.
>
> I've made an initial attempt at moving things around in [1]. If the
> underlying code can live in a module, and then the substitute, weather
> and challenge scripts use that code with whatever UI stuff they want,
> maybe that will allow for better addressing this weather specific issue.
>
> 1: https://issues.guix.info/45409

I've sent a couple of updated patches for this. They're not particularly
dependent on the above work to create the (guix substitutes) module, but
I based the commits on that.

These commits make more careful changes, the substitute script behaviour
should remain the same.
-----BEGIN PGP SIGNATURE-----

iQKlBAEBCgCPFiEEPonu50WOcg2XVOCyXiijOwuE9XcFAmA2usVfFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDNF
ODlFRUU3NDU4RTcyMEQ5NzU0RTBCMjVFMjhBMzNCMEI4NEY1NzcRHG1haWxAY2Jh
aW5lcy5uZXQACgkQXiijOwuE9XfR8w/+Ja+e71rNDfGpa19X6X3ERGYBDKYqKa4U
xBd6e94Yd1zK4pgPRl2dJ0Ril31RUPREkSEx4QkAtn81cHUWQ6dVEf5L3yu8VBto
3V8K/7JuHLsilmInKOsdHRQUlX2FWnR/hj8hu9ZNpfBRPCHUypHgnGn0ZirOoDyE
iURu2waXyBdGuILnECxyIpbz1QpwUi8/HVQZ0YPm/AVUo9D84uWQ7g6A88kS2gBG
CjE1K5TIWZUWO9afunMrgMv/4sEzHpuvjm4JFnD6VGVJ3FFw4IEt+qsjuhevBl9g
fy31eQWz9dRigWM9/lzVrZJMf2kDSdQ+DGEA9JgDMVcjlbDBkdCYAciXXCajageP
OTSNY/3KlrGr0ZkMnXhBYli9HOkArknyV3RG5MEW7GWLH37FjJrW1v5hECuRoIHD
prVPribYQI3bjpdv3L75/UL465J0PiuFHaWQZcWFccDynwsCNE7KnEl5ajrvp9kd
zobGGXX5yLQcQziMfUElFY2zpVDbrxpd98sLhoIJb/1UiL6kpM/S4tLCSd8vgiij
qI1uIesI5Hahwp2Ls2Tjpq906rgGAuRChjqWJ8Qvidb4s+Fy/V5LscgB1F84fJb1
lpQFmS+65t0iWJgPH4ljrzm9hN6BMv5bBkmPKw6baEJ5uqnG370P7828wTBmWQIc
gak1OEesTQE=
=QXvR
-----END PGP SIGNATURE-----

C
C
Christopher Baines wrote on 9 Mar 2021 21:29
(address . 45146-done@debbugs.gnu.org)(name . Ludovic Courtès)(address . ludo@gnu.org)
87r1ko9ikr.fsf@cbaines.net
Christopher Baines <mail@cbaines.net> writes:

Toggle quote (50 lines)
> Christopher Baines <mail@cbaines.net> writes:
>
>> Ludovic Courtès <ludo@gnu.org> writes:
>>
>>> Christopher Baines <mail@cbaines.net> skribis:
>>>
>>>> At least in guix weather, these changes make the progress bar actually appear.
>>>>
>>>> * guix/scripts/substitute.scm (fetch-narinfos): Use (guix progress) for
>>>> progress reporting.
>>>
>>> Cool. I noticed that something was wrong with ‘guix weather’, but I
>>> suspected it had to do with the order in which the erase-line sequence
>>> and \r are sent.
>>>
>>>> - (lambda ()
>>>> - (display "\r\x1b[K" (current-error-port)) ;erase current line
>>>> - (force-output (current-error-port))
>>>> - (format (current-error-port)
>>>> - (G_ "updating substitutes from '~a'... ~5,1f%")
>>>> - url (* 100. (/ done total)))
>>>> - (set! done (+ 1 done)))))
>>>> + (define fetch-narinfos-progress-reporter
>>>> + (progress-reporter/bar (length paths)))
>>>
>>> The problem here is that we’d see a progress bar without knowing what it
>>> represents.
>>>
>>> Besides, currently output from ‘guix substitute’ is printed as is by
>>> client commands, regardless of whether stdout is a tty. The problem
>>> already exists but it would become a bit more visible as logs get filled
>>> with progress bars.
>>
>> Maybe it's best to circle back to fixing guix weather after trying to
>> restructure some of the guix substitute code.
>>
>> I've made an initial attempt at moving things around in [1]. If the
>> underlying code can live in a module, and then the substitute, weather
>> and challenge scripts use that code with whatever UI stuff they want,
>> maybe that will allow for better addressing this weather specific issue.
>>
>> 1: https://issues.guix.info/45409
>
> I've sent a couple of updated patches for this. They're not particularly
> dependent on the above work to create the (guix substitutes) module, but
> I based the commits on that.
>
> These commits make more careful changes, the substitute script behaviour
> should remain the same.

I've gone ahead and pushed these patches now as
f5ffb3bd9cd59cfff5b4625d6d65e8d116d0a25b.
-----BEGIN PGP SIGNATURE-----

iQKlBAEBCgCPFiEEPonu50WOcg2XVOCyXiijOwuE9XcFAmBH2rRfFIAAAAAALgAo
aXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5maWZ0aGhvcnNlbWFuLm5ldDNF
ODlFRUU3NDU4RTcyMEQ5NzU0RTBCMjVFMjhBMzNCMEI4NEY1NzcRHG1haWxAY2Jh
aW5lcy5uZXQACgkQXiijOwuE9XdQMhAAgiNV2SY/UBsSmZ2UYhRslrWwWZwUQMVQ
mPEX9n9FlYXH1fQI1ozVJucBLsbLzTuv8n1wYDi7dYgTr9U3U7tBV2NwZjeYwU+j
x2Y3jp06U1L+H/n+s9k74Eevu3Pcr3EGBfUZwQIjFTScmDvVkdaWO9aPMcFxAcL4
eDt9zh6cWUYBW+cRMKWQP+FK0FuBRH6tMG/BOdx9Ns627RPatokwjhNROzf17/tA
y5AwYC49WI/5MNat14b23wfkrv77xd3kZ/A06ZripXyVxlwqQ75ZXu78TRQ1BwsV
ojtYmLKDiJFzOoirBLZTtAUUZUXVZLsCvLdFQYFw9820fhL5eO7ec3pKdkW0yhmw
ghgSvFkPA6Z7oBdtokPcrZAKbLIKw8DUuVXiAGLUJ8QBVSWwj7eiu5W+ZUIzrOk7
4YrmOec7Qn32vfxdsTBiG+n27UcRIZmT3nYEAFUs3n14fcR/oB9XFokM7RnD7a8k
87wq2PDwvwKUYFgFABn2WGx+lMPcwkuuT4rw4Tz3yIfd8FD48//mHjgepC2bOm51
7+mm1s+rVXnGoAlwOuSLc/S/Pp1CYXkSjIi2C5/xYc9tqZhmEjXxanx3QoynTOrM
5mumL21El1E/eNnHQ5Pt7JqGac/EUxoB+dliNYGrjp+5ZpqV7PpkSVqoG+ZuD0TL
3xtoZ6HvTsk=
=rT0w
-----END PGP SIGNATURE-----

Closed
?