Skip to content

Commit

Permalink
fix: support parallel execution of reusable containers (#593)
Browse files Browse the repository at this point in the history
* chore: add a test demonstrating the bug

* fix: protect reusable containers with a mutex

* fix: typo
  • Loading branch information
mdelapenya committed Oct 28, 2022
1 parent ab2f0be commit 53f6ee9
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
7 changes: 7 additions & 0 deletions generic.go
Expand Up @@ -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")
)

Expand Down Expand Up @@ -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 invoked
// 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)
Expand Down
50 changes: 50 additions & 0 deletions parallel_test.go
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}

0 comments on commit 53f6ee9

Please sign in to comment.