Commit graph

82 commits

Author SHA1 Message Date
Sebastiaan van Stijn
cff4f20c44
migrate to github.com/containerd/log v0.1.0
The github.com/containerd/containerd/log package was moved to a separate
module, which will also be used by upcoming (patch) releases of containerd.

This patch moves our own uses of the package to use the new module.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-10-11 17:52:23 +02:00
Sebastiaan van Stijn
050de1e4f6
daemon/logger/journald: format code with gofumpt
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-06-29 00:31:53 +02:00
Brian Goff
74da6a6363 Switch all logging to use containerd log pkg
This unifies our logging and allows us to propagate logging and trace
contexts together.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2023-06-24 00:23:44 +00:00
Sebastiaan van Stijn
ab35df454d
remove pre-go1.17 build-tags
Removed pre-go1.17 build-tags with go fix;

    go mod init
    go fix -mod=readonly ./...
    rm go.mod

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-05-19 20:38:51 +02:00
cui fliter
f66684fdeb fix some comments
Signed-off-by: cui fliter <imcusg@gmail.com>
2023-04-25 13:39:28 +08:00
Sebastiaan van Stijn
ad7f1a8dcc
daemon/logger: use strings.Cut()
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-12-21 11:09:01 +01:00
Cory Snider
c3a6de9ec8 logger/journald: unlock OS threads
Managed containerd processes are executed with SysProcAttr.Pdeathsig set
to syscall.SIGKILL so that the managed containerd is automatically
killed along with the daemon. At least, that is the intention. In
practice, the signal is sent to the process when the creating _OS
thread_ dies! If a goroutine exits while locked to an OS thread, the Go
runtime will terminate the thread. If that thread happens to be the
same thread which the subprocess was started from, the subprocess will
be signaled. Prevent the journald driver from sometimes unintentionally
killing child processes by ensuring that all runtime.LockOSThread()
calls are paired with runtime.UnlockOSThread().

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-09-28 11:46:59 -04:00
Sebastiaan van Stijn
2f1c382a6d
golangci-lint: update to v1.49.0
Remove the "deadcode", "structcheck", and "varcheck" linters, as they are
deprecated:

    WARN [runner] The linter 'deadcode' is deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter.  Replaced by unused.
    WARN [runner] The linter 'structcheck' is deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter.  Replaced by unused.
    WARN [runner] The linter 'varcheck' is deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter.  Replaced by unused.
    WARN [linters context] structcheck is disabled because of generics. You can track the evolution of the generics support by following the https://github.com/golangci/golangci-lint/issues/2649.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-09-23 23:31:27 +02:00
Cory Snider
ef5b279887 logger/journald: implement --follow correctly
Implement --follow entirely correctly for the journald log reader, such
that it exits immediately upon reading back the last log message written
to the journal before the logger was closed. The impossibility of doing
so has been slightly exaggerated.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:41:38 -04:00
Cory Snider
e278d3f185 logger/*: fix reading w/ non-monotonic timestamps
Fix journald and logfile-powered (jsonfile, local) log readers
incorrectly filtering out messages with timestamps < Since which were
preceded by a message with a timestamp >= Since.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:41:38 -04:00
Cory Snider
342b44bf20 logger/journald: rewrite reader w/o cursors
Careful management of the journal read pointer is sufficient to ensure
that no entry is read more than once.

Unit test the journald logger without requiring a running journald by
using the systemd-journal-remote command to write arbitrary entries to
journal files.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:41:38 -04:00
Cory Snider
3e8405aa82 logger/journald: refactor cgo out of business logic
Wrap the libsystemd journal reading functionality in a more idiomatic Go
API and refactor the journald logging driver's ReadLogs implementation
to use the wrapper. Rewrite the parts of the ReadLogs implementation in
Go which were previously implemented in C as part of the cgo preamble.
Separating the business logic from the cgo minutiae should hopefully
make the code more accessible to a wider audience of developers for
reviewing the code and contributing improvements.

The structure of the ReadLogs implementation is retained with few
modifications. Any ignored errors were also ignored before the refactor;
the explicit error return values afforded by the sdjournal wrapper makes
this more obvious.

The package github.com/coreos/go-systemd/v22/sdjournal also provides a
more idiomatic Go wrapper around libsystemd. It is unsuitable for our
needs as it does not expose wrappers for the sd_journal_process and
sd_journal_get_fd functions.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:41:09 -04:00
Cory Snider
7ff4b64319 logger/journald: simplify control flow
Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:37:59 -04:00
Cory Snider
f7fe2c2290 logger/journald: follow the thread-safety rules
Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:37:59 -04:00
Cory Snider
12744335c0 logger/journald: simplify build constraints
Ensure the package can be imported, no matter the build constratints, by
adding an unconstrained doc.go containing a package statement.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-07-25 16:37:59 -04:00
Sebastiaan van Stijn
0e9a66d35a
logger/journald: remove journald_compat (for systemd < 209)
This was added in 6cdc4ba6cd in 2016, likely
because at the time we were still building for CentOS 6 and Ubuntu 14.04.

All currently supported distros appear to be on _at least_ 219 now, so it looks
safe to remove this;

```bash
docker run -it --rm centos:7

yum install -y systemd-devel

pkg-config 'libsystemd >= 209' && echo "OK" || echo "KO"
OK

pkg-config --print-provides 'libsystemd'
libsystemd = 219

pkg-config --print-provides 'libsystemd-journal'
libsystemd-journal = 219
```

And on a `debian:buster` (old stable)

```bash
docker run -it --rm debian:buster

apt-get update && apt-get install -y libsystemd-dev pkg-config

pkg-config 'libsystemd >= 209' && echo "OK" || echo "KO"
OK

pkg-config --print-provides 'libsystemd'
libsystemd = 241

pkg-config --print-provides 'libsystemd-journal'
Package libsystemd-journal was not found in the pkg-config search path.
Perhaps you should add the directory containing `libsystemd-journal.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libsystemd-journal' found
```

OpenSUSE leap (I think that's built for s390x)

```bash
docker run -it --rm docker.io/opensuse/leap:15

zypper install -y systemd-devel

pkg-config 'libsystemd >= 209' && echo "OK" || echo "KO"
OK

pkg-config --print-provides 'libsystemd'
libsystemd = 246

pkg-config --print-provides 'libsystemd-journal'
Package libsystemd-journal was not found in the pkg-config search path.
Perhaps you should add the directory containing `libsystemd-journal.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libsystemd-journal' found
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-07-20 18:48:00 +02:00
Sebastiaan van Stijn
75577fe7a8
logger/journald: fix SA4011: ineffective break statement
This was introduced in 906b979b88, which changed
a `goto` to a `break`, but afaics, the intent was still to break out of the loop.
(linter didn't catch this before because it didn't have the right build-tag set)

    daemon/logger/journald/read.go:238:4: SA4011: ineffective break statement. Did you mean to break out of the outer loop? (staticcheck)
                break // won't be able to write anything anymore
                ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-07-20 15:14:41 +02:00
Cory Snider
906b979b88 daemon/logger: remove ProducerGone from LogWatcher
Whether or not the logger has been closed is a property of the logger,
and only of concern to its log reading implementation, not log watchers.
The loggers and their reader implementations can communicate as they see
fit. A single channel per logger which is closed when the logger is
closed is plenty sufficient to broadcast the state to log readers, with
no extra bookeeping or synchronization required.

Signed-off-by: Cory Snider <csnider@mirantis.com>
2022-05-19 15:22:22 -04:00
Sebastiaan van Stijn
df650a1aeb
panic() instead of logrus.Fatal() in init funcs
Some packages were using `logrus.Fatal()` in init functions (which logs the error,
and (by default) calls `os.Exit(1)` after logging).

Given that logrus formatting and outputs have not yet been configured during the
initialization stage, it does not provide much benefits over a plain `panic()`.

This patch replaces some instances of `logrus.Fatal()` with `panic()`, which has
the added benefits of not introducing logrus as a dependency in some of these
packages, and also produces a stacktrace, which could help locating the problem
in the unlikely event an `init()` fails.

Before this change, an error would look like:

    $ dockerd
    FATA[0000] something bad happened

After this change, the same error looks like:

    $ dockerd
    panic: something bad happened

    goroutine 1 [running]:
      github.com/docker/docker/daemon/logger/awslogs.init.0()
        /go/src/github.com/docker/docker/daemon/logger/awslogs/cloudwatchlogs.go:128 +0x89

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2022-04-21 12:15:20 +02:00
Sebastiaan van Stijn
686be57d0a
Update to Go 1.17.0, and gofmt with Go 1.17
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-08-24 23:33:27 +02:00
Sebastiaan van Stijn
d43bcc8974
daemon/logger/journald: fix linting errors
daemon/logger/journald/read.go:128:3 comment on exported function `CErr` should be of the form `CErr ...`

    daemon/logger/journald/read.go:131:36: unnecessary conversion (unconvert)
            return C.GoString(C.strerror(C.int(-ret)))
	                                  ^
    daemon/logger/journald/read.go:380:2: S1023: redundant `return` statement (gosimple)
        return
        ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-06-10 13:03:21 +02:00
Christian Becker
322c9e6866 add partial metadata to journald logs
add all partial metadata available to journald logs to allow easier reassembly of partial messages in downstream logging systems

fixes #41403

Signed-off-by: Christian Becker <christian.becker@sixt.com>
2020-09-01 12:54:05 +02:00
Akihiro Suda
9a82a9a8ea vendor containerd, BuildKit, protobuf, grpc, and golang.org/x
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2020-03-03 10:25:20 +09:00
Sebastiaan van Stijn
9c701fdb6b
logger/journald: U1000: field mu is unused (unused)
this looks to be a false positive, but this field is not
used if journald is not supported, which may be the cause

```
daemon/logger/journald/journald.go:21:2: U1000: field `mu` is unused (unused)
```

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2019-09-18 12:57:42 +02:00
Kir Kolyshkin
20a0e58a79 journald/read: fix/unify errors
1. Use "in-place" variables for if statements to limit their scope to
   the respectful `if` block.

2. Report the error returned from sd_journal_* by using CErr().

3. Use errors.New() instead of fmt.Errorf().

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:35 -07:00
Kir Kolyshkin
dd4bfe30a8 journald: fix for --tail 0
From the first glance, `docker logs --tail 0` does not make sense,
as it is supposed to produce no output, but `tail -n 0` from GNU
coreutils is working like that, plus there is even a test case
(`TestLogsTail` in integration-cli/docker_cli_logs_test.go).

Now, something like `docker logs --follow --tail 0` makes total
sense, so let's make it work.

(NOTE if --tail is not used, config.Tail is set to -1)

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:35 -07:00
Kir Kolyshkin
b73fb8fd5d journald/read: avoid piling up open files
If we take a long time to process log messages, and during that time
journal file rotation occurs, the journald client library will keep
those rotated files open until sd_journal_process() is called.

By periodically calling sd_journal_process() during the processing
loop we shrink the window of time a client instance has open file
descriptors for rotated (deleted) journal files.

This code is modelled after that of journalctl [1]; the above explanation
as well as the value of 1024 is taken from there.

[v2: fix CErr() argument]

[1] https://github.com/systemd/systemd/blob/dc16327c48d/src/journal/journalctl.c#L2676
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:35 -07:00
Kir Kolyshkin
f091febc94 journald/read: simplify/fix followJournal()
TL;DR: simplify the code, fix --follow hanging indefinitely

Do the following to simplify the followJournal() code:

1. Use Go-native select instead of C-native polling.

2. Use Watch{Producer,Consumer}Gone(), eliminating the need
to have journald.closed variable, and an extra goroutine.

3. Use sd_journal_wait(). In the words of its own man page:

> A synchronous alternative for using sd_journal_get_fd(),
> sd_journal_get_events(), sd_journal_get_timeout() and
> sd_journal_process() is sd_journal_wait().

Unfortunately, the logic is still not as simple as it
could be; the reason being, once the container has exited,
journald might still be writing some logs from its internal
buffers onto journal file(s), and there is no way to
figure out whether it's done so we are guaranteed to
read all of it back. This bug can be reproduced with
something like

> $ ID=$(docker run -d busybox seq 1 150000); docker logs --follow $ID
> ...
> 128123
> $

(The last expected output line should be `150000`).

To avoid exiting from followJournal() early, add the
following logic: once the container is gone, keep trying
to drain the journal until there's no new data for at
least `waitTimeout` time period.

Should fix https://github.com/docker/for-linux/issues/575

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:35 -07:00
Kir Kolyshkin
981c01665b Call sd_journal_get_fd() earlier, only if needed
1. The journald client library initializes inotify watch(es)
during the first call to sd_journal_get_fd(), and it make sense
to open it earlier in order to not lose any journal file rotation
events.

2. It only makes sense to call this if we're going to use it
later on -- so add a check for config.Follow.

3. Remove the redundant call to sd_journal_get_fd().

NOTE that any subsequent calls to sd_journal_get_fd() return
the same file descriptor, so there's no real need to save it
for later use in wait_for_data_cancelable().

Based on earlier patch by Nalin Dahyabhai <nalin@redhat.com>.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:34 -07:00
Kir Kolyshkin
79039720c8 journald/read: avoid being blocked on send
In case the LogConsumer is gone, the code that sends the message can
stuck forever. Wrap the code in select case, as all other loggers do.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:34 -07:00
Kir Kolyshkin
ff3cd167ea journald/read: simplify walking backwards
In case Tail=N parameter is requested, we need to show N lines.
It does not make sense to walk backwards one by one if we can
do it at once. Now, if Since=T is also provided, make sure we
haven't jumped too far (before T), and if we did, move forward.

The primary motivation for this was to make the code simpler.

This also fixes a tiny bug in the "since" implementation.

Before this commit:
> $ docker logs -t --tail=6000 --since="2019-03-10T03:54:25.00" $ID | head
> 2019-03-10T03:54:24.999821000Z 95981

After:
> $ docker logs -t --tail=6000 --since="2019-03-10T03:54:25.00" $ID | head
> 2019-03-10T03:54:25.000013000Z 95982

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:34 -07:00
Kir Kolyshkin
e8f6166791 journald/read: simplify code
Minor code simplification.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2019-08-02 10:02:34 -07:00
Nalin Dahyabhai
1ada3e85bf Small journal cleanup
Clean up a deferred function call in the journal reading logic.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2019-08-02 10:02:34 -07:00
Robin THONI
bc709991b3 Use a regex to match labels
Signed-off-by: Robin THONI <robin@rthoni.com>
2019-01-16 22:17:16 +00:00
Yong Tang
533e07afbe
Merge pull request #38032 from RohitK89/21497-log-image-name
Add IMAGE_NAME attribute to journald log events
2018-10-17 12:18:05 -07:00
Colin Panisset
5cd2bb315a Only add CONTAINER_PARTIAL_MESSAGE if not the last partial
Addresses #38045

Signed-off-by: Colin Panisset <colin.panisset@cevo.com.au>
2018-10-17 07:51:59 +11:00
Rohit Kapur
5f7e102df7 Add IMAGE_NAME as a key to journald log messages
Signed-off-by: Rohit Kapur <rkapur@flatiron.com>
2018-10-12 16:16:31 -04:00
Kir Kolyshkin
b2b169f13f daemon/logger/journald: simplify readers field
As in other similar drivers (jsonlog, local), use a set
(i.e. `map[whatever]struct{}`), making the code simpler.

While at it, make sure we remove the reader from the set
after calling `ProducerGone()` on it.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2018-09-10 16:17:05 -07:00
Kir Kolyshkin
916eabd459 daemon.ContainerLogs(): fix resource leak on follow
When daemon.ContainerLogs() is called with options.follow=true
(as in "docker logs --follow"), the "loggerutils.followLogs()"
function never returns (even then the logs consumer is gone).
As a result, all the resources associated with it (including
an opened file descriptor for the log file being read, two FDs
for a pipe, and two FDs for inotify watch) are never released.

If this is repeated (such as by running "docker logs --follow"
and pressing Ctrl-C a few times), this results in DoS caused by
either hitting the limit of inotify watches, or the limit of
opened files. The only cure is daemon restart.

Apparently, what happens is:

1. logs producer (a container) is gone, calling (*LogWatcher).Close()
for all its readers (daemon/logger/jsonfilelog/jsonfilelog.go:175).

2. WatchClose() is properly handled by a dedicated goroutine in
followLogs(), cancelling the context.

3. Upon receiving the ctx.Done(), the code in followLogs()
(daemon/logger/loggerutils/logfile.go#L626-L638) keeps to
send messages _synchronously_ (which is OK for now).

4. Logs consumer is gone (Ctrl-C is pressed on a terminal running
"docker logs --follow"). Method (*LogWatcher).Close() is properly
called (see daemon/logs.go:114). Since it was called before and
due to to once.Do(), nothing happens (which is kinda good, as
otherwise it will panic on closing a closed channel).

5. A goroutine (see item 3 above) keeps sending log messages
synchronously to the logWatcher.Msg channel. Since the
channel reader is gone, the channel send operation blocks forever,
and resource cleanup set up in defer statements at the beginning
of followLogs() never happens.

Alas, the fix is somewhat complicated:

1. Distinguish between close from logs producer and logs consumer.
To that effect,
 - yet another channel is added to LogWatcher();
 - {Watch,}Close() are renamed to {Watch,}ProducerGone();
 - {Watch,}ConsumerGone() are added;

*NOTE* that ProducerGone()/WatchProducerGone() pair is ONLY needed
in order to stop ConsumerLogs(follow=true) when a container is stopped;
otherwise we're not interested in it. In other words, we're only
using it in followLogs().

2. Code that was doing (logWatcher*).Close() is modified to either call
ProducerGone() or ConsumerGone(), depending on the context.

3. Code that was waiting for WatchClose() is modified to wait for
either ConsumerGone() or ProducerGone(), or both, depending on the
context.

4. followLogs() are modified accordingly:
 - context cancellation is happening on WatchProducerGone(),
and once it's received the FileWatcher is closed and waitRead()
returns errDone on EOF (i.e. log rotation handling logic is disabled);
 - due to this, code that was writing synchronously to logWatcher.Msg
can be and is removed as the code above it handles this case;
 - function returns once ConsumerGone is received, freeing all the
resources -- this is the bugfix itself.

While at it,

1. Let's also remove the ctx usage to simplify the code a bit.
It was introduced by commit a69a59ffc7 ("Decouple removing the
fileWatcher from reading") in order to fix a bug. The bug was actually
a deadlock in fsnotify, and the fix was just a workaround. Since then
the fsnofify bug has been fixed, and a new fsnotify was vendored in.
For more details, please see
https://github.com/moby/moby/pull/27782#issuecomment-416794490

2. Since `(*filePoller).Close()` is fixed to remove all the files
being watched, there is no need to explicitly call
fileWatcher.Remove(name) anymore, so get rid of the extra code.

Should fix https://github.com/moby/moby/issues/37391

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2018-09-06 11:47:42 -07:00
Anusha Ragunathan
0b4b0a7b5d Improve partial message support in logger
Docker daemon has a 16K buffer for log messages. If a message length
exceeds 16K, it should be split by the logger and merged at the
endpoint.

This change adds `PartialLogMetaData` struct for enhanced partial support
- LastPartial (bool) : indicates if this is the last of all partials.
- ID (string)        : unique 32 bit ID. ID is same across all partials.
- Ordinal (int starts at 1) : indicates the position of msg in the series of partials.
Also, the timestamps across partials in the same.

Signed-off-by: Anusha Ragunathan <anusha.ragunathan@docker.com>
2018-04-11 13:26:28 -07:00
Daniel Nephin
4f0d95fa6e Add canonical import comment
Signed-off-by: Daniel Nephin <dnephin@docker.com>
2018-02-05 16:51:57 -05:00
Vlastimil Zeman
ae557f9d85
Add journald tag as SYSLOG_IDENTIFIER
Signed-off-by: Vlastimil Zeman <vlastimil.zeman@diffblue.com>
2017-11-22 08:51:23 +00:00
Jamie Hannaford
e8d9a61f4c Add --until flag for docker logs; closes #32807
Signed-off-by: Jamie Hannaford <jamie.hannaford@rackspace.com>
2017-11-01 10:08:49 +01:00
Derek McGowan
1009e6a40b
Update logrus to v1.0.1
Fixes case sensitivity issue

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
2017-07-31 13:16:46 -07:00
Aaron Lehmann
b642b3f21f Avoid using a map for log attributes
Having a map per log entry seemed heavier than necessary. These
attributes end up being sorted and serialized, so storing them in a map
doesn't add anything (there's no random access element). In SwarmKit,
they originate as a slice, so there's an unnecessary conversion to a map
and back.

This also fixes the sort comparator, which used to inefficiently split
the string on each comparison.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
2017-07-18 19:01:20 -07:00
David Glasser
917050c572 Fix stderr logging for journald and syslog
logger.PutMessage, added in #28762 (v17.04.0-ce), clears msg.Source. So journald
and syslog were treating stderr messages as if they were stdout.

Signed-off-by: David Glasser <glasser@davidglasser.net>
2017-06-26 14:37:43 -07:00
Jim Minter
4fdb17c777 Prevent ContainerLogs from hanging if container doesn't run for long
Signed-off-by: Jim Minter <jminter@redhat.com>
2017-04-20 12:27:51 +01:00
Brian Goff
1325f667ee Merge pull request #27565 from rothrock/env-by-regex
[splunk] Use a regex to match environment variables
2017-03-31 10:46:48 -04:00
Alexander Morozov
b3e7f70137 Merge pull request #31263 from nalind/journal-doublefree
Synchronize the cursor returned by followJournal
2017-02-22 17:45:49 -08:00
Nalin Dahyabhai
d57c330617 Synchronize the cursor returned by followJournal
Make sure that the cursor value returned by followJournal() is the last
of the values returned by its goroutine's calls to drainJournal() by
waiting for it, rather than returning a value that may be superceded by
another if we're singalling the goroutine that it should exit by closing
a pipe.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2017-02-22 16:22:00 -05:00