mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #30922 from dperny/fix-log-error-handling-return-error
Fix both logs commands to correctly handle errors
This commit is contained in:
commit
946787e85e
4 changed files with 54 additions and 5 deletions
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
@ -108,9 +109,10 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
|
||||||
select {
|
select {
|
||||||
case <-chStarted:
|
case <-chStarted:
|
||||||
// The client may be expecting all of the data we're sending to
|
// The client may be expecting all of the data we're sending to
|
||||||
// be multiplexed, so send it through OutStream, which will
|
// be multiplexed, so mux it through the Systemerr stream, which
|
||||||
// have been set up to handle that if needed.
|
// will cause the client to throw an error when demuxing
|
||||||
fmt.Fprintf(logsConfig.OutStream, "Error running logs job: %v\n", err)
|
stdwriter := stdcopy.NewStdWriter(logsConfig.OutStream, stdcopy.Systemerr)
|
||||||
|
fmt.Fprintf(stdwriter, "Error running logs job: %v\n", err)
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/backend"
|
"github.com/docker/docker/api/types/backend"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
types "github.com/docker/docker/api/types/swarm"
|
types "github.com/docker/docker/api/types/swarm"
|
||||||
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -252,7 +253,8 @@ func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter
|
||||||
// The client may be expecting all of the data we're sending to
|
// The client may be expecting all of the data we're sending to
|
||||||
// be multiplexed, so send it through OutStream, which will
|
// be multiplexed, so send it through OutStream, which will
|
||||||
// have been set up to handle that if needed.
|
// have been set up to handle that if needed.
|
||||||
fmt.Fprintf(logsConfig.OutStream, "Error grabbing service logs: %v\n", err)
|
stdwriter := stdcopy.NewStdWriter(w, stdcopy.Systemerr)
|
||||||
|
fmt.Fprintf(stdwriter, "Error grabbing service logs: %v\n", err)
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ const (
|
||||||
Stdout
|
Stdout
|
||||||
// Stderr represents standard error steam type.
|
// Stderr represents standard error steam type.
|
||||||
Stderr
|
Stderr
|
||||||
|
// Systemerr represents errors originating from the system that make it
|
||||||
|
// into the the multiplexed stream.
|
||||||
|
Systemerr
|
||||||
|
|
||||||
stdWriterPrefixLen = 8
|
stdWriterPrefixLen = 8
|
||||||
stdWriterFdIndex = 0
|
stdWriterFdIndex = 0
|
||||||
|
@ -115,8 +118,9 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream := StdType(buf[stdWriterFdIndex])
|
||||||
// Check the first byte to know where to write
|
// Check the first byte to know where to write
|
||||||
switch StdType(buf[stdWriterFdIndex]) {
|
switch stream {
|
||||||
case Stdin:
|
case Stdin:
|
||||||
fallthrough
|
fallthrough
|
||||||
case Stdout:
|
case Stdout:
|
||||||
|
@ -125,6 +129,11 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
||||||
case Stderr:
|
case Stderr:
|
||||||
// Write on stderr
|
// Write on stderr
|
||||||
out = dsterr
|
out = dsterr
|
||||||
|
case Systemerr:
|
||||||
|
// If we're on Systemerr, we won't write anywhere.
|
||||||
|
// NB: if this code changes later, make sure you don't try to write
|
||||||
|
// to outstream if Systemerr is the stream
|
||||||
|
out = nil
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex])
|
return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex])
|
||||||
}
|
}
|
||||||
|
@ -155,11 +164,18 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we might have an error from the source mixed up in our multiplexed
|
||||||
|
// stream. if we do, return it.
|
||||||
|
if stream == Systemerr {
|
||||||
|
return written, fmt.Errorf("error from daemon in stream: %s", string(buf[stdWriterPrefixLen:frameSize+stdWriterPrefixLen]))
|
||||||
|
}
|
||||||
|
|
||||||
// Write the retrieved frame (without header)
|
// Write the retrieved frame (without header)
|
||||||
nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
|
nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
|
||||||
if ew != nil {
|
if ew != nil {
|
||||||
return 0, ew
|
return 0, ew
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the frame has not been fully written: error
|
// If the frame has not been fully written: error
|
||||||
if nw != frameSize {
|
if nw != frameSize {
|
||||||
return 0, io.ErrShortWrite
|
return 0, io.ErrShortWrite
|
||||||
|
|
|
@ -246,6 +246,35 @@ func TestStdCopyDetectsNotFullyWrittenFrames(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStdCopyReturnsErrorFromSystem tests that StdCopy correctly returns an
|
||||||
|
// error, when that error is muxed into the Systemerr stream.
|
||||||
|
func TestStdCopyReturnsErrorFromSystem(t *testing.T) {
|
||||||
|
// write in the basic messages, just so there's some fluff in there
|
||||||
|
stdOutBytes := []byte(strings.Repeat("o", startingBufLen))
|
||||||
|
stdErrBytes := []byte(strings.Repeat("e", startingBufLen))
|
||||||
|
buffer, err := getSrcBuffer(stdOutBytes, stdErrBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// add in an error message on the Systemerr stream
|
||||||
|
systemErrBytes := []byte(strings.Repeat("S", startingBufLen))
|
||||||
|
systemWriter := NewStdWriter(buffer, Systemerr)
|
||||||
|
_, err = systemWriter.Write(systemErrBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now copy and demux. we should expect an error containing the string we
|
||||||
|
// wrote out
|
||||||
|
_, err = StdCopy(ioutil.Discard, ioutil.Discard, buffer)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error, got none")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), string(systemErrBytes)) {
|
||||||
|
t.Fatal("expected error to contain message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkWrite(b *testing.B) {
|
func BenchmarkWrite(b *testing.B) {
|
||||||
w := NewStdWriter(ioutil.Discard, Stdout)
|
w := NewStdWriter(ioutil.Discard, Stdout)
|
||||||
data := []byte("Test line for testing stdwriter performance\n")
|
data := []byte("Test line for testing stdwriter performance\n")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue