diff --git a/README.md b/README.md index 30b4347d80..5ee79ed3e8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,11 @@ func TestIntegrationNginxLatestReturn(t *testing.T) { } // Clean up the container after the test is complete - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %w", err) + } + }() resp, err := http.Get(nginxC.URI) if resp.StatusCode != http.StatusOK { diff --git a/compose_api.go b/compose_api.go index 17e38fec90..c75453091c 100644 --- a/compose_api.go +++ b/compose_api.go @@ -25,8 +25,10 @@ func (f stackUpOptionFunc) applyToStackUp(o *stackUpOptions) { f(o) } +//nolint:unused type stackDownOptionFunc func(do *api.DownOptions) +//nolint:unused func (f stackDownOptionFunc) applyToStackDown(do *api.DownOptions) { f(do) } @@ -42,6 +44,7 @@ func RunServices(serviceNames ...string) StackUpOption { // IgnoreOrphans - Ignore legacy containers for services that are not defined in the project type IgnoreOrphans bool +//nolint:unused func (io IgnoreOrphans) applyToStackUp(co *api.CreateOptions, _ *api.StartOptions) { co.IgnoreOrphans = bool(io) } diff --git a/container_test.go b/container_test.go index 4520ccca9f..a63c54c17c 100644 --- a/container_test.go +++ b/container_test.go @@ -324,7 +324,7 @@ func Test_BuildImageWithContexts(t *testing.T) { } else if err != nil { t.Fatal(err) } else { - c.Terminate(ctx) + terminateContainerOnEnd(t, ctx, c) } }) } @@ -346,7 +346,7 @@ func Test_GetLogsFromFailedContainer(t *testing.T) { if err != nil && !errors.Is(err, context.DeadlineExceeded) { t.Fatal(err) } else if err == nil { - c.Terminate(ctx) + terminateContainerOnEnd(t, ctx, c) t.Fatal("was expecting error starting container") } @@ -396,9 +396,7 @@ func createTestContainer(t *testing.T, ctx context.Context) int { t.Fatalf("could not get mapped port: %v", err) } - t.Cleanup(func() { - container.Terminate(context.Background()) - }) + terminateContainerOnEnd(t, ctx, container) return port.Int() } diff --git a/docker.go b/docker.go index 30af40bfa0..04c70a6487 100644 --- a/docker.go +++ b/docker.go @@ -295,13 +295,9 @@ func (c *DockerContainer) Logs(ctx context.Context) (io.ReadCloser, error) { r := bufio.NewReader(rc) go func() { - var ( - isPrefix = true - lineStarted = true - line []byte - ) + var lineStarted = true for err == nil { - line, isPrefix, err = r.ReadLine() + line, isPrefix, err := r.ReadLine() if lineStarted && len(line) >= streamHeaderSize { line = line[streamHeaderSize:] // trim stream header diff --git a/docker_test.go b/docker_test.go index 38a6c8f134..b0db986818 100644 --- a/docker_test.go +++ b/docker_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "log" // Import mysql into the scope of this package (required) "io" @@ -194,7 +195,7 @@ func TestContainerWithHostNetworkOptions_UseExposePortsFromImageConfigs(t *testi t.Fatal(err) } - defer nginxC.Terminate(ctx) + terminateContainerOnEnd(t, ctx, nginxC) endpoint, err := nginxC.Endpoint(ctx, "http") if err != nil { @@ -225,7 +226,7 @@ func TestContainerWithNetworkModeAndNetworkTogether(t *testing.T) { // Error when NetworkMode = host and Network = []string{"bridge"} t.Logf("Can't use Network and NetworkMode together, %s", err) } - defer nginx.Terminate(ctx) + terminateContainerOnEnd(t, ctx, nginx) } func TestContainerWithHostNetworkOptionsAndWaitStrategy(t *testing.T) { @@ -636,9 +637,10 @@ func TestContainerTerminationRemovesDockerImage(t *testing.T) { if err != nil { t.Fatal(err) } + _, _, err = client.ImageInspectWithRaw(ctx, imageID) if err == nil { - t.Fatal("custom built image should have been removed") + t.Fatal("custom built image should have been removed", err) } }) } @@ -686,6 +688,9 @@ func TestTwoContainersExposingTheSamePort(t *testing.T) { } endpointB, err := nginxB.PortEndpoint(ctx, nginxDefaultPort, "http") + if err != nil { + t.Fatal(err) + } resp, err = http.Get(endpointB) if err != nil { @@ -1016,6 +1021,9 @@ func TestContainerCreationWaitsForLog(t *testing.T) { "root", "password", host, port, "database") db, err := sql.Open("mysql", connectionString) + if err != nil { + t.Fatal(err) + } defer db.Close() if err = db.Ping(); err != nil { @@ -1719,7 +1727,11 @@ func ExampleDockerProvider_CreateContainer() { ContainerRequest: req, Started: true, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() } func ExampleContainer_Host() { @@ -1733,7 +1745,11 @@ func ExampleContainer_Host() { ContainerRequest: req, Started: true, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() ip, _ := nginxC.Host(ctx) println(ip) } @@ -1748,7 +1764,11 @@ func ExampleContainer_Start() { nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ ContainerRequest: req, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() _ = nginxC.Start(ctx) } @@ -1762,7 +1782,11 @@ func ExampleContainer_Stop() { nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ ContainerRequest: req, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() timeout := 10 * time.Second _ = nginxC.Stop(ctx, &timeout) } @@ -1778,7 +1802,11 @@ func ExampleContainer_MappedPort() { ContainerRequest: req, Started: true, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() ip, _ := nginxC.Host(ctx) port, _ := nginxC.MappedPort(ctx, "80") _, _ = http.Get(fmt.Sprintf("http://%s:%s", ip, port.Port())) @@ -1926,11 +1954,7 @@ func TestContainerCustomPlatformImage(t *testing.T) { Started: false, }) - t.Cleanup(func() { - if c != nil { - c.Terminate(ctx) - } - }) + terminateContainerOnEnd(t, ctx, c) assert.Error(t, err) }) @@ -2450,7 +2474,7 @@ func TestContainerRunningCheckingStatusCode(t *testing.T) { t.Fatal(err) } - defer influx.Terminate(ctx) + terminateContainerOnEnd(t, ctx, influx) } func TestContainerWithUserID(t *testing.T) { diff --git a/docs/examples/cockroachdb.md b/docs/examples/cockroachdb.md index 7700bbd75a..4eb66a2f18 100644 --- a/docs/examples/cockroachdb.md +++ b/docs/examples/cockroachdb.md @@ -94,7 +94,11 @@ func TestIntegrationDBInsertSelect(t *testing.T) { if err != nil { t.Fatal(err) } - defer cdbContainer.Terminate(ctx) + t.Cleanup(func() { + if err := cdbContainer.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) db, err := sql.Open("pgx", cdbContainer.URI+"/projectmanagement") if err != nil { diff --git a/docs/examples/nginx.md b/docs/examples/nginx.md index 59c2cd8985..401edcfdda 100644 --- a/docs/examples/nginx.md +++ b/docs/examples/nginx.md @@ -60,7 +60,12 @@ func TestIntegrationNginxLatestReturn(t *testing.T) { } // Clean up the container after the test is complete - defer nginxC.Terminate(ctx) + t.Cleanup(func() { + t.Log("terminating container") + if err := ctr.Terminate(ctx)); err != nil { + t.Errorf("failed to terminate container: :%w", err) + } + }) resp, err := http.Get(nginxC.URI) if resp.StatusCode != http.StatusOK { diff --git a/docs/examples/pulsar.md b/docs/examples/pulsar.md index 6aec7a3bde..a1b372b5d1 100644 --- a/docs/examples/pulsar.md +++ b/docs/examples/pulsar.md @@ -71,7 +71,13 @@ func TestPulsar(t *testing.T) { if err != nil { t.Fatal(err) } - t.Cleanup(func() { c.Container.Terminate(ctx) }) + // Cleanup the container after the test is complete + t.Cleanup(func() { + t.Log("terminating container") + if err := ctr.Terminate(ctx)); err != nil { + t.Errorf("failed to terminate container: :%w", err) + } + }) pc, err := pulsar.NewClient(pulsar.ClientOptions{ URL: c.URI, @@ -131,4 +137,4 @@ type logConsumer struct{} func (lc *logConsumer) Accept(l testcontainers.Log) { fmt.Print(string(l.Content)) } -``` \ No newline at end of file +``` diff --git a/docs/examples/redis.md b/docs/examples/redis.md index 39eaa729ed..2ae4940250 100644 --- a/docs/examples/redis.md +++ b/docs/examples/redis.md @@ -64,7 +64,13 @@ func TestIntegrationSetGet(t *testing.T) { if err != nil { t.Fatal(err) } - defer redisContainer.Terminate(ctx) + // Clean up the container after the test is complete + t.Cleanup(func() { + t.Log("terminating container") + if err := redisContainer.Terminate(ctx)); err != nil { + t.Errorf("failed to terminate container: :%w", err) + } + }) // You will likely want to wrap your Redis package of choice in an // interface to aid in unit testing and limit lock-in throughtout your diff --git a/docs/features/creating_container.md b/docs/features/creating_container.md index ae1fea8d58..5035198775 100644 --- a/docs/features/creating_container.md +++ b/docs/features/creating_container.md @@ -74,7 +74,11 @@ func TestIntegrationNginxLatestReturn(t *testing.T) { } // Clean up the container after the test is complete - defer nginxC.Terminate(ctx) + t.Cleanup(func() { + if err := nginxC.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) resp, err := http.Get(nginxC.URI) if resp.StatusCode != http.StatusOK { @@ -193,7 +197,6 @@ func main() { } res, err := testcontainers.ParallelContainers(ctx, requests, testcontainers.ParallelContainersOptions{}) - if err != nil { e, ok := err.(testcontainers.ParallelContainersError) if !ok { @@ -207,7 +210,12 @@ func main() { } for _, c := range res { - defer c.Terminate(ctx) + c := c + defer func() { + if err := c.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", c) + } + }() } } ``` diff --git a/docs/quickstart/gotest.md b/docs/quickstart/gotest.md index 675fa03255..d5c0451423 100644 --- a/docs/quickstart/gotest.md +++ b/docs/quickstart/gotest.md @@ -35,7 +35,11 @@ func TestWithRedis(t *testing.T) { if err != nil { t.Error(err) } - defer redisC.Terminate(ctx) + defer func() { + if err := redisC.terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %w", err) + } + }() } ``` diff --git a/e2e/container_test.go b/e2e/container_test.go index f9dc377e04..3be69f315d 100644 --- a/e2e/container_test.go +++ b/e2e/container_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/docker/go-connections/nat" + "github.com/stretchr/testify/require" . "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -45,7 +46,7 @@ func TestContainerWithWaitForSQL(t *testing.T) { t.Fatal(err) } - defer container.Terminate(ctx) + terminateContainerOnEnd(t, ctx, container) }) t.Run("custom query", func(t *testing.T) { req := ContainerRequest{ @@ -65,7 +66,7 @@ func TestContainerWithWaitForSQL(t *testing.T) { t.Fatal(err) } - defer container.Terminate(ctx) + terminateContainerOnEnd(t, ctx, container) }) t.Run("custom bad query", func(t *testing.T) { req := ContainerRequest{ @@ -85,6 +86,17 @@ func TestContainerWithWaitForSQL(t *testing.T) { t.Fatal("expected error, but got a nil") } - defer container.Terminate(ctx) + terminateContainerOnEnd(t, ctx, container) + }) +} + +func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr Container) { + tb.Helper() + if ctr == nil { + return + } + tb.Cleanup(func() { + tb.Log("terminating container") + require.NoError(tb, ctr.Terminate(ctx)) }) } diff --git a/file.go b/file.go index 8e33a6ce75..1f402f3842 100644 --- a/file.go +++ b/file.go @@ -40,7 +40,7 @@ func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { tw := tar.NewWriter(zr) // walk through every file in the folder - filepath.Walk(src, func(file string, fi os.FileInfo, errFn error) error { + err := filepath.Walk(src, func(file string, fi os.FileInfo, errFn error) error { if errFn != nil { return fmt.Errorf("error traversing the file system: %w", errFn) } @@ -80,6 +80,9 @@ func tarDir(src string, fileMode int64) (*bytes.Buffer, error) { } return nil }) + if err != nil { + return buffer, err + } // produce tar if err := tw.Close(); err != nil { diff --git a/generic_test.go b/generic_test.go index ee58667efb..4762e0c726 100644 --- a/generic_test.go +++ b/generic_test.go @@ -29,7 +29,6 @@ func TestGenericReusableContainer(t *testing.T) { }) require.NoError(t, err) require.True(t, n1.IsRunning()) - terminateContainerOnEnd(t, ctx, n1) copiedFileName := "hello_copy.sh" diff --git a/logconsumer_test.go b/logconsumer_test.go index 29c9e16f19..32233e0633 100644 --- a/logconsumer_test.go +++ b/logconsumer_test.go @@ -107,7 +107,7 @@ func Test_LogConsumerGetsCalled(t *testing.T) { g.Msgs = g.Msgs[1:] assert.DeepEqual(t, []string{"echo hello\n", "echo there\n"}, g.Msgs) - _ = c.Terminate(ctx) + terminateContainerOnEnd(t, ctx, c) } type TestLogTypeConsumer struct { @@ -144,7 +144,7 @@ func Test_ShouldRecognizeLogTypes(t *testing.T) { if err != nil { t.Fatal(err) } - defer func() { _ = c.Terminate(ctx) }() + terminateContainerOnEnd(t, ctx, c) ep, err := c.Endpoint(ctx, "http") if err != nil { @@ -248,7 +248,7 @@ func TestContainerLogWithErrClosed(t *testing.T) { if err := nginx.Start(ctx); err != nil { t.Fatal(err) } - defer nginx.Terminate(ctx) + terminateContainerOnEnd(t, ctx, nginx) port, err := nginx.MappedPort(ctx, "80/tcp") if err != nil { @@ -259,7 +259,9 @@ func TestContainerLogWithErrClosed(t *testing.T) { if err = nginx.StartLogProducer(ctx); err != nil { t.Fatal(err) } - defer nginx.StopLogProducer() + defer func() { + _ = nginx.StopLogProducer() + }() nginx.FollowOutput(&consumer) // Gather the initial container logs @@ -320,7 +322,7 @@ func TestContainerLogsShouldBeWithoutStreamHeader(t *testing.T) { if err != nil { t.Fatal(err) } - defer container.Terminate(ctx) + terminateContainerOnEnd(t, ctx, container) r, err := container.Logs(ctx) if err != nil { t.Fatal(err) diff --git a/network_test.go b/network_test.go index 39888e51b5..23d41656cb 100644 --- a/network_test.go +++ b/network_test.go @@ -3,6 +3,7 @@ package testcontainers import ( "context" "fmt" + "log" "testing" "time" @@ -22,7 +23,9 @@ func ExampleNetworkProvider_CreateNetwork() { CheckDuplicate: true, }, }) - defer net.Remove(ctx) + defer func() { + _ = net.Remove(ctx) + }() nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ ContainerRequest: ContainerRequest{ @@ -35,7 +38,12 @@ func ExampleNetworkProvider_CreateNetwork() { }, }, }) - defer nginxC.Terminate(ctx) + defer func() { + if err := nginxC.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() + nginxC.GetContainerID() } @@ -66,7 +74,9 @@ func Test_NetworkWithIPAM(t *testing.T) { t.Fatal("cannot create network: ", err) } - defer net.Remove(ctx) + defer func() { + _ = net.Remove(ctx) + }() nginxC, _ := GenericContainer(ctx, GenericContainerRequest{ ContainerRequest: ContainerRequest{ @@ -79,7 +89,7 @@ func Test_NetworkWithIPAM(t *testing.T) { }, }, }) - defer nginxC.Terminate(ctx) + terminateContainerOnEnd(t, ctx, nginxC) nginxC.GetContainerID() provider, err := ProviderDocker.GetProvider() @@ -123,7 +133,9 @@ func Test_MultipleContainersInTheNewNetwork(t *testing.T) { t.Fatal("cannot create network") } - defer net.Remove(ctx) + defer func() { + _ = net.Remove(ctx) + }() postgres, err := GenericContainer(ctx, GenericContainerRequest{ ContainerRequest: dbContainerRequest, @@ -133,7 +145,7 @@ func Test_MultipleContainersInTheNewNetwork(t *testing.T) { t.Fatal(err) } - defer postgres.Terminate(ctx) + terminateContainerOnEnd(t, ctx, postgres) env = make(map[string]string) env["RABBITMQ_ERLANG_COOKIE"] = "f2a2d3d27c75" @@ -158,7 +170,7 @@ func Test_MultipleContainersInTheNewNetwork(t *testing.T) { return } - defer rabbitmq.Terminate(ctx) + terminateContainerOnEnd(t, ctx, rabbitmq) fmt.Println(postgres.GetContainerID()) fmt.Println(rabbitmq.GetContainerID()) } diff --git a/parallel_test.go b/parallel_test.go index 84d86552be..3a23c47e57 100644 --- a/parallel_test.go +++ b/parallel_test.go @@ -103,7 +103,6 @@ func TestParallelContainers(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { res, err := ParallelContainers(context.Background(), tc.reqs, ParallelContainersOptions{}) - if err != nil { require.NotZero(t, tc.expErrors) e, _ := err.(ParallelContainersError) @@ -114,7 +113,8 @@ func TestParallelContainers(t *testing.T) { } for _, c := range res { - defer c.Terminate(context.Background()) + c := c + terminateContainerOnEnd(t, context.Background(), c) } if len(res) != tc.resLen { @@ -165,8 +165,6 @@ func TestParallelContainersWithReuse(t *testing.T) { e, _ := err.(ParallelContainersError) t.Fatalf("expected errors: %d, got: %d\n", 0, len(e.Errors)) } - - for _, c := range res { - defer c.Terminate(ctx) - } + // Container is reused, only terminate first container + terminateContainerOnEnd(t, ctx, res[0]) } diff --git a/reaper.go b/reaper.go index 5a81336597..a82a8ba54f 100644 --- a/reaper.go +++ b/reaper.go @@ -126,8 +126,14 @@ func (r *Reaper) Connect() (chan bool, error) { for retryLimit > 0 { retryLimit-- - sock.WriteString(strings.Join(labelFilters, "&")) - sock.WriteString("\n") + if _, err := sock.WriteString(strings.Join(labelFilters, "&")); err != nil { + continue + } + + if _, err := sock.WriteString("\n"); err != nil { + continue + } + if err := sock.Flush(); err != nil { continue } @@ -136,6 +142,7 @@ func (r *Reaper) Connect() (chan bool, error) { if err != nil { continue } + if resp == "ACK\n" { break } diff --git a/testresources/echoserver.go b/testresources/echoserver.go index 0ab8d5c31f..1b331e8890 100644 --- a/testresources/echoserver.go +++ b/testresources/echoserver.go @@ -11,7 +11,7 @@ import ( func envHandler() http.HandlerFunc { return func(rw http.ResponseWriter, req *http.Request) { - rw.Write([]byte(os.Getenv("FOO"))) + _, _ = rw.Write([]byte(os.Getenv("FOO"))) rw.WriteHeader(http.StatusAccepted) } @@ -44,5 +44,5 @@ func main() { fmt.Println("ready") - http.Serve(ln, mux) + _ = http.Serve(ln, mux) } diff --git a/wait/exec_test.go b/wait/exec_test.go index f97d799295..f796c8893a 100644 --- a/wait/exec_test.go +++ b/wait/exec_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "io" + "log" "testing" "time" @@ -29,7 +30,12 @@ func ExampleExecStrategy() { panic(err) } - defer localstack.Terminate(ctx) // nolint: errcheck + defer func() { + if err := localstack.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() + // Here you have a running container } diff --git a/wait/exit_test.go b/wait/exit_test.go index d7a6b476af..7e00136e90 100644 --- a/wait/exit_test.go +++ b/wait/exit_test.go @@ -12,7 +12,6 @@ import ( type exitStrategyTarget struct { isRunning bool - err error } func (st exitStrategyTarget) Host(ctx context.Context) (string, error) { diff --git a/wait/http_test.go b/wait/http_test.go index 9187e549b2..f176bd1b7e 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "fmt" "io" + "log" "net" "net/http" "os" @@ -34,9 +35,13 @@ func ExampleHTTPStrategy() { panic(err) } - defer gogs.Terminate(ctx) // nolint: errcheck - // Here you have a running container + defer func() { + if err := gogs.Terminate(ctx); err != nil { + log.Fatalf("failed to terminate container: %s", err) + } + }() + // Here you have a running container } func TestHTTPStrategyWaitUntilReady(t *testing.T) { @@ -79,20 +84,24 @@ func TestHTTPStrategyWaitUntilReady(t *testing.T) { WithMethod(http.MethodPost).WithBody(bytes.NewReader([]byte("ping"))), } - container, err := testcontainers.GenericContainer(context.Background(), - testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true}) + ctx := context.Background() + container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ContainerRequest: dockerReq, Started: true}) if err != nil { t.Error(err) return } - defer container.Terminate(context.Background()) // nolint: errcheck + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) - host, err := container.Host(context.Background()) + host, err := container.Host(ctx) if err != nil { t.Error(err) return } - port, err := container.MappedPort(context.Background(), "6443/tcp") + port, err := container.MappedPort(ctx, "6443/tcp") if err != nil { t.Error(err) return diff --git a/wait/log.go b/wait/log.go index 709497a4a8..aa3d711475 100644 --- a/wait/log.go +++ b/wait/log.go @@ -81,12 +81,17 @@ LOOP: return ctx.Err() default: reader, err := target.Logs(ctx) - if err != nil { time.Sleep(ws.PollInterval) continue } + b, err := io.ReadAll(reader) + if err != nil { + time.Sleep(ws.PollInterval) + continue + } + logs := string(b) if strings.Count(logs, ws.Log) >= ws.Occurrence { break LOOP