Skip to content

Commit

Permalink
Added error handling for context.Canceled in log reading code (#2268)
Browse files Browse the repository at this point in the history
* Added error handling for context.Canceled in log reading code

* Added error handling for context.Canceled in log reading code

* Added test case for context canceled error handling

* Added necessary import statement for log consumer test

* `make lint` changes

* Handled the termination of the containers

* `make lint` changes

* Cancelled log consumer test: Increased timeout

* Changes based on review comments

* Updated check for log messages in the Cancelled Context test

---------

Co-authored-by: Manuel de la Peña <mdelapenya@gmail.com>
  • Loading branch information
prateekdwivedi and mdelapenya committed Mar 12, 2024
1 parent f81826b commit 6c41653
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ func (c *DockerContainer) startLogProduction(ctx context.Context, opts ...LogPro
since = fmt.Sprintf("%d.%09d", now.Unix(), int64(now.Nanosecond()))
goto BEGIN
}
if errors.Is(err, context.DeadlineExceeded) {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
// Probably safe to continue here
continue
}
Expand Down Expand Up @@ -756,7 +756,7 @@ func (c *DockerContainer) startLogProduction(ctx context.Context, opts ...LogPro
if err != nil {
// TODO: add-logger: use logger to log out this error
_, _ = fmt.Fprintf(os.Stderr, "error occurred reading log with known length %s", err.Error())
if errors.Is(err, context.DeadlineExceeded) {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
// Probably safe to continue here
continue
}
Expand Down
115 changes: 115 additions & 0 deletions logconsumer_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package testcontainers

import (
"bytes"
"context"
"errors"
"fmt"
Expand Down Expand Up @@ -508,3 +509,117 @@ func Test_StartLogProductionStillStartsWithTooHighTimeout(t *testing.T) {

terminateContainerOnEnd(t, ctx, c)
}

func Test_MultiContainerLogConsumer_CancelledContext(t *testing.T) {
// Redirect stderr to a buffer
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

// Context with cancellation functionality for simulating user interruption
ctx, cancel := context.WithCancel(context.Background())

first := TestLogConsumer{
Msgs: []string{},
Done: make(chan bool),
Accepted: devNullAcceptorChan(),
}

containerReq1 := ContainerRequest{
FromDockerfile: FromDockerfile{
Context: "./testdata/",
Dockerfile: "echoserver.Dockerfile",
},
ExposedPorts: []string{"8080/tcp"},
WaitingFor: wait.ForLog("ready"),
LogConsumerCfg: &LogConsumerConfig{
Consumers: []LogConsumer{&first},
},
}

genericReq1 := GenericContainerRequest{
ContainerRequest: containerReq1,
Started: true,
}

c, err := GenericContainer(ctx, genericReq1)
require.NoError(t, err)

ep1, err := c.Endpoint(ctx, "http")
require.NoError(t, err)

_, err = http.Get(ep1 + "/stdout?echo=hello1")
require.NoError(t, err)

_, err = http.Get(ep1 + "/stdout?echo=there1")
require.NoError(t, err)

second := TestLogConsumer{
Msgs: []string{},
Done: make(chan bool),
Accepted: devNullAcceptorChan(),
}

containerReq2 := ContainerRequest{
FromDockerfile: FromDockerfile{
Context: "./testdata/",
Dockerfile: "echoserver.Dockerfile",
},
ExposedPorts: []string{"8080/tcp"},
WaitingFor: wait.ForLog("ready"),
LogConsumerCfg: &LogConsumerConfig{
Consumers: []LogConsumer{&second},
},
}

genericReq2 := GenericContainerRequest{
ContainerRequest: containerReq2,
Started: true,
}

c2, err := GenericContainer(ctx, genericReq2)
require.NoError(t, err)

ep2, err := c2.Endpoint(ctx, "http")
require.NoError(t, err)

_, err = http.Get(ep2 + "/stdout?echo=hello2")
require.NoError(t, err)

_, err = http.Get(ep2 + "/stdout?echo=there2")
require.NoError(t, err)

// Handling the termination of the containers
defer func() {
shutdownCtx, shutdownCancel := context.WithTimeout(
context.Background(), 60*time.Second,
)
defer shutdownCancel()
_ = c.Terminate(shutdownCtx)
_ = c2.Terminate(shutdownCtx)
}()

// Deliberately calling context cancel
cancel()

// We check log size due to context cancellation causing
// varying message counts, leading to test failure.
assert.GreaterOrEqual(t, len(first.Msgs), 2)
assert.GreaterOrEqual(t, len(second.Msgs), 2)

// Restore stderr
w.Close()
os.Stderr = oldStderr

// Read the stderr output from the buffer
var buf bytes.Buffer
_, _ = buf.ReadFrom(r)

// Check the stderr message
actual := buf.String()

// The context cancel shouldn't cause the system to throw a
// logStoppedForOutOfSyncMessage, as it hangs the system with
// the multiple containers.
assert.False(t, strings.Contains(actual, logStoppedForOutOfSyncMessage))
}

0 comments on commit 6c41653

Please sign in to comment.