From b6e3812ac0e9a60d2d304191e7cfe489258744ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Oct 2022 00:26:21 +0200 Subject: [PATCH 1/3] chore: add a test demonstrating the bug --- parallel_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/parallel_test.go b/parallel_test.go index 3ff6738a1b..84d86552be 100644 --- a/parallel_test.go +++ b/parallel_test.go @@ -2,9 +2,12 @@ package testcontainers import ( "context" + "fmt" "testing" + "time" "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go/wait" ) func TestParallelContainers(t *testing.T) { @@ -120,3 +123,50 @@ func TestParallelContainers(t *testing.T) { }) } } + +func TestParallelContainersWithReuse(t *testing.T) { + const ( + postgresPort = 5432 + postgresPassword = "test" + postgresUser = "test" + postgresDb = "test" + ) + + natPort := fmt.Sprintf("%d/tcp", postgresPort) + + req := GenericContainerRequest{ + ContainerRequest: ContainerRequest{ + Image: "postgis/postgis", + Name: "test-postgres", + ExposedPorts: []string{natPort}, + Env: map[string]string{ + "POSTGRES_PASSWORD": postgresPassword, + "POSTGRES_USER": postgresUser, + "POSTGRES_DATABASE": postgresDb, + }, + WaitingFor: wait.ForLog("database system is ready to accept connections"). + WithPollInterval(100 * time.Millisecond). + WithOccurrence(2), + }, + Started: true, + Reuse: true, + } + + parallelRequest := ParallelContainerRequest{ + req, + req, + req, + } + + ctx := context.Background() + + res, err := ParallelContainers(ctx, parallelRequest, ParallelContainersOptions{}) + if err != nil { + e, _ := err.(ParallelContainersError) + t.Fatalf("expected errors: %d, got: %d\n", 0, len(e.Errors)) + } + + for _, c := range res { + defer c.Terminate(ctx) + } +} From 5f3656c4817ed85e329cd45090793610aabedb91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Oct 2022 00:20:15 +0200 Subject: [PATCH 2/3] fix: protect reusable containers with a mutex --- generic.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/generic.go b/generic.go index cbd239f826..2abea1b341 100644 --- a/generic.go +++ b/generic.go @@ -4,9 +4,11 @@ import ( "context" "errors" "fmt" + "sync" ) var ( + reuseContainerMx sync.Mutex ErrReuseEmptyName = errors.New("with reuse option a container name mustn't be empty") ) @@ -56,6 +58,11 @@ func GenericContainer(ctx context.Context, req GenericContainerRequest) (Contain var c Container if req.Reuse { + // we must protect the reusability of the container in the case it's invoke + // in a parallel execution, via ParallelContainers or t.Parallel() + reuseContainerMx.Lock() + defer reuseContainerMx.Unlock() + c, err = provider.ReuseOrCreateContainer(ctx, req.ContainerRequest) } else { c, err = provider.CreateContainer(ctx, req.ContainerRequest) From 1e159d64ca655a69bb8f548cc8cae634f708531c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20de=20la=20Pe=C3=B1a?= Date: Fri, 28 Oct 2022 00:50:32 +0200 Subject: [PATCH 3/3] fix: typo --- generic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic.go b/generic.go index 2abea1b341..ce79aa1100 100644 --- a/generic.go +++ b/generic.go @@ -58,7 +58,7 @@ func GenericContainer(ctx context.Context, req GenericContainerRequest) (Contain var c Container if req.Reuse { - // we must protect the reusability of the container in the case it's invoke + // we must protect the reusability of the container in the case it's invoked // in a parallel execution, via ParallelContainers or t.Parallel() reuseContainerMx.Lock() defer reuseContainerMx.Unlock()