From 083ef6617b73f8ec3e54d67bb2cbcd61a5734eba Mon Sep 17 00:00:00 2001 From: Stefan Gehrig Date: Thu, 12 Oct 2023 09:19:58 +0200 Subject: [PATCH] Ensure that non-JSON-parsing errors are returned to the caller Signed-off-by: Stefan Gehrig Co-authored-by: Cory Snider (cherry picked from commit 0d27579fc734b9d466b4b81b4bc904f4c1abe25b) Signed-off-by: Sebastiaan van Stijn --- client/container_wait.go | 8 +++++-- client/container_wait_test.go | 43 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/client/container_wait.go b/client/container_wait.go index 2375eb1e80..b7d80542c9 100644 --- a/client/container_wait.go +++ b/client/container_wait.go @@ -66,8 +66,12 @@ func (cli *Client) ContainerWait(ctx context.Context, containerID string, condit // // If there's a JSON parsing error, read the real error message // off the body and send it to the client. - _, _ = io.ReadAll(io.LimitReader(stream, containerWaitErrorMsgLimit)) - errC <- errors.New(responseText.String()) + if errors.As(err, new(*json.SyntaxError)) { + _, _ = io.ReadAll(io.LimitReader(stream, containerWaitErrorMsgLimit)) + errC <- errors.New(responseText.String()) + } else { + errC <- err + } return } diff --git a/client/container_wait_test.go b/client/container_wait_test.go index 0c6e974773..aa87569c05 100644 --- a/client/container_wait_test.go +++ b/client/container_wait_test.go @@ -9,11 +9,14 @@ import ( "log" "net/http" "strings" + "syscall" "testing" + "testing/iotest" "time" "github.com/docker/docker/api/types/container" "github.com/docker/docker/errdefs" + "github.com/pkg/errors" ) func TestContainerWaitError(t *testing.T) { @@ -117,6 +120,46 @@ func TestContainerWaitProxyInterruptLong(t *testing.T) { } } +func TestContainerWaitErrorHandling(t *testing.T) { + for _, test := range []struct { + name string + rdr io.Reader + exp error + }{ + {name: "invalid json", rdr: strings.NewReader(`{]`), exp: errors.New("{]")}, + {name: "context canceled", rdr: iotest.ErrReader(context.Canceled), exp: context.Canceled}, + {name: "context deadline exceeded", rdr: iotest.ErrReader(context.DeadlineExceeded), exp: context.DeadlineExceeded}, + {name: "connection reset", rdr: iotest.ErrReader(syscall.ECONNRESET), exp: syscall.ECONNRESET}, + } { + t.Run(test.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + client := &Client{ + version: "1.30", + client: newMockClient(func(req *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(test.rdr), + }, nil + }), + } + resultC, errC := client.ContainerWait(ctx, "container_id", "") + select { + case err := <-errC: + if err.Error() != test.exp.Error() { + t.Fatalf("ContainerWait() errC = %v; want %v", err, test.exp) + } + return + case result := <-resultC: + t.Fatalf("expected to not get a wait result, got %d", result.StatusCode) + return + } + // Unexpected - we should not reach this line + }) + } +} + func ExampleClient_ContainerWait_withTimeout() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel()