Skip to content

Commit 2740634

Browse files
authored
feat: support adding wait strategies as functional option (testcontainers#3161)
* feat: add a new functional option to append wait strategies to modules * chore: update modules * docs: make it mkore clear
1 parent 17c223f commit 2740634

File tree

11 files changed

+166
-24
lines changed

11 files changed

+166
-24
lines changed

docs/features/common_functional_options.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,14 @@ You can also use `testcontainers.WithAdditionalLifecycleHooks`, which appends th
242242

243243
#### Wait Strategies
244244

245-
If you need to set a different wait strategy for the container, you can use `testcontainers.WithWaitStrategy` with a valid wait strategy.
245+
If you need to set a different wait strategy for the container, replacing the existing one, you can use `testcontainers.WithWaitStrategy` with a valid wait strategy.
246246

247247
!!!info
248248
The default deadline for the wait strategy is 60 seconds.
249249

250-
At the same time, it's possible to set a wait strategy and a custom deadline with `testcontainers.WithWaitStrategyAndDeadline`.
250+
At the same time, it's possible to replace the wait strategy with a new one and a custom deadline, using `testcontainers.WithWaitStrategyAndDeadline`.
251+
252+
Finally, you can also append a wait strategy to the existing wait strategy, using `testcontainers.WithAdditionalWaitStrategy` and `testcontainers.WithAdditionalWaitStrategyAndDeadline`.
251253

252254
#### Startup Commands
253255

docs/modules/index.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ In order to simplify the creation of the container for a given module, `Testcont
210210
- `testcontainers.WithAdditionalLifecycleHooks`: a function that appends lifecycle hooks to the existing ones for the container request.
211211
- `testcontainers.WithAlwaysPull`: a function that pulls the image before starting the container.
212212
- `testcontainers.WithImagePlatform`: a function that sets the image platform for the container request.
213-
- `testcontainers.WithWaitStrategy`: a function that sets the wait strategy for the container request.
214-
- `testcontainers.WithWaitStrategyAndDeadline`: a function that sets the wait strategy for the container request with a deadline.
213+
- `testcontainers.WithWaitStrategy`: a function that replaces the wait strategy for the container request.
214+
- `testcontainers.WithAdditionalWaitStrategy`: a function that appends the wait strategy for the container request.
215+
- `testcontainers.WithWaitStrategyAndDeadline`: a function that replaces the wait strategy for the container request with a deadline.
216+
- `testcontainers.WithAdditionalWaitStrategyAndDeadline`: a function that appends the wait strategy for the container request with a deadline.
215217
- `testcontainers.WithStartupCommand`: a function that sets the execution of a command when the container starts.
216218
- `testcontainers.WithAfterReadyCommand`: a function that sets the execution of a command right after the container is ready (its wait strategy is satisfied).
217219
- `testcontainers.WithDockerfile`: a function that sets the build from a Dockerfile for the container request.

modules/k3s/k3s_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func Test_WithManifestOption(t *testing.T) {
146146
k3sContainer, err := k3s.Run(ctx,
147147
"rancher/k3s:v1.27.1-k3s1",
148148
k3s.WithManifest("nginx-manifest.yaml"),
149-
testcontainers.WithWaitStrategy(wait.ForExec([]string{"kubectl", "wait", "pod", "nginx", "--for=condition=Ready"})),
149+
testcontainers.WithAdditionalWaitStrategy(wait.ForExec([]string{"kubectl", "wait", "pod", "nginx", "--for=condition=Ready"})),
150150
)
151151
testcontainers.CleanupContainer(t, k3sContainer)
152152
require.NoError(t, err)

modules/localstack/examples_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func ExampleRun_legacyMode() {
101101
ctx,
102102
"localstack/localstack:0.10.0",
103103
testcontainers.WithEnv(map[string]string{"SERVICES": "s3,sqs"}),
104-
testcontainers.WithWaitStrategy(wait.ForLog("Ready.").WithStartupTimeout(5*time.Minute).WithOccurrence(1)),
104+
testcontainers.WithAdditionalWaitStrategy(wait.ForLog("Ready.").WithStartupTimeout(5*time.Minute).WithOccurrence(1)),
105105
)
106106
defer func() {
107107
if err := testcontainers.TerminateContainer(ctr); err != nil {

modules/mongodb/examples_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/testcontainers/testcontainers-go"
1313
"github.com/testcontainers/testcontainers-go/modules/mongodb"
14-
"github.com/testcontainers/testcontainers-go/wait"
1514
)
1615

1716
func ExampleRun() {
@@ -89,7 +88,6 @@ func ExampleRun_withCredentials() {
8988
"mongo:6",
9089
mongodb.WithUsername("root"),
9190
mongodb.WithPassword("password"),
92-
testcontainers.WithWaitStrategy(wait.ForLog("Waiting for connections")),
9391
)
9492
defer func() {
9593
if err := testcontainers.TerminateContainer(ctr); err != nil {

modules/mssql/mssql_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestMSSQLServerWithMissingEulaOption(t *testing.T) {
5050
t.Run("empty", func(t *testing.T) {
5151
ctr, err := mssql.Run(ctx,
5252
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04",
53-
testcontainers.WithWaitStrategy(
53+
testcontainers.WithAdditionalWaitStrategy(
5454
wait.ForLog("The SQL Server End-User License Agreement (EULA) must be accepted")),
5555
)
5656
testcontainers.CleanupContainer(t, ctr)
@@ -61,7 +61,7 @@ func TestMSSQLServerWithMissingEulaOption(t *testing.T) {
6161
ctr, err := mssql.Run(ctx,
6262
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04",
6363
testcontainers.WithEnv(map[string]string{"ACCEPT_EULA": "yes"}),
64-
testcontainers.WithWaitStrategy(
64+
testcontainers.WithAdditionalWaitStrategy(
6565
wait.ForLog("The SQL Server End-User License Agreement (EULA) must be accepted")),
6666
)
6767
testcontainers.CleanupContainer(t, ctr)

modules/postgres/examples_test.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import (
55
"fmt"
66
"log"
77
"path/filepath"
8-
"time"
98

109
"github.com/testcontainers/testcontainers-go"
1110
"github.com/testcontainers/testcontainers-go/modules/postgres"
12-
"github.com/testcontainers/testcontainers-go/wait"
1311
)
1412

1513
func ExampleRun() {
@@ -27,10 +25,7 @@ func ExampleRun() {
2725
postgres.WithDatabase(dbName),
2826
postgres.WithUsername(dbUser),
2927
postgres.WithPassword(dbPassword),
30-
testcontainers.WithWaitStrategy(
31-
wait.ForLog("database system is ready to accept connections").
32-
WithOccurrence(2).
33-
WithStartupTimeout(5*time.Second)),
28+
postgres.BasicWaitStrategies(),
3429
)
3530
defer func() {
3631
if err := testcontainers.TerminateContainer(postgresContainer); err != nil {

modules/postgres/postgres_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func TestContainerWithWaitForSQL(t *testing.T) {
155155
postgres.WithDatabase(dbname),
156156
postgres.WithUsername(user),
157157
postgres.WithPassword(password),
158-
testcontainers.WithWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL)),
158+
testcontainers.WithAdditionalWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL)),
159159
)
160160
testcontainers.CleanupContainer(t, ctr)
161161
require.NoError(t, err)
@@ -168,7 +168,7 @@ func TestContainerWithWaitForSQL(t *testing.T) {
168168
postgres.WithDatabase(dbname),
169169
postgres.WithUsername(user),
170170
postgres.WithPassword(password),
171-
testcontainers.WithWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL).WithStartupTimeout(time.Second*5).WithQuery("SELECT 10")),
171+
testcontainers.WithAdditionalWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL).WithStartupTimeout(time.Second*5).WithQuery("SELECT 10")),
172172
)
173173
testcontainers.CleanupContainer(t, ctr)
174174
require.NoError(t, err)
@@ -181,7 +181,7 @@ func TestContainerWithWaitForSQL(t *testing.T) {
181181
postgres.WithDatabase(dbname),
182182
postgres.WithUsername(user),
183183
postgres.WithPassword(password),
184-
testcontainers.WithWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL).WithStartupTimeout(time.Second*5).WithQuery("SELECT 'a' from b")),
184+
testcontainers.WithAdditionalWaitStrategy(wait.ForSQL(nat.Port(port), "postgres", dbURL).WithStartupTimeout(time.Second*5).WithQuery("SELECT 'a' from b")),
185185
)
186186
testcontainers.CleanupContainer(t, ctr)
187187
require.Error(t, err)
@@ -225,7 +225,7 @@ func TestWithSSL(t *testing.T) {
225225
postgres.WithDatabase(dbname),
226226
postgres.WithUsername(user),
227227
postgres.WithPassword(password),
228-
testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
228+
testcontainers.WithAdditionalWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
229229
postgres.WithSSLCert(caCert.CertPath, serverCerts.CertPath, serverCerts.KeyPath),
230230
)
231231

@@ -255,7 +255,7 @@ func TestSSLValidatesKeyMaterialPath(t *testing.T) {
255255
postgres.WithDatabase(dbname),
256256
postgres.WithUsername(user),
257257
postgres.WithPassword(password),
258-
testcontainers.WithWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
258+
testcontainers.WithAdditionalWaitStrategy(wait.ForLog("database system is ready to accept connections").WithOccurrence(2).WithStartupTimeout(5*time.Second)),
259259
postgres.WithSSLCert("", "", ""),
260260
)
261261

modules/postgres/wait_strategies.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
// Without this, the tests will be flaky on those OSes!
1515
func BasicWaitStrategies() testcontainers.CustomizeRequestOption {
1616
// waitStrategy {
17-
return testcontainers.WithWaitStrategy(
17+
return testcontainers.WithAdditionalWaitStrategy(
1818
// First, we wait for the container to log readiness twice.
1919
// This is because it will restart itself after the first startup.
2020
wait.ForLog("database system is ready to accept connections").WithOccurrence(2),

options.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,17 @@ func WithAfterReadyCommand(execs ...Executable) CustomizeRequestOption {
373373
}
374374
}
375375

376-
// WithWaitStrategy sets the wait strategy for a container, using 60 seconds as deadline
376+
// WithWaitStrategy replaces the wait strategy for a container, using 60 seconds as deadline
377377
func WithWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
378378
return WithWaitStrategyAndDeadline(60*time.Second, strategies...)
379379
}
380380

381-
// WithWaitStrategyAndDeadline sets the wait strategy for a container, including deadline
381+
// WithAdditionalWaitStrategy appends the wait strategy for a container, using 60 seconds as deadline
382+
func WithAdditionalWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
383+
return WithAdditionalWaitStrategyAndDeadline(60*time.Second, strategies...)
384+
}
385+
386+
// WithWaitStrategyAndDeadline replaces the wait strategy for a container, including deadline
382387
func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
383388
return func(req *GenericContainerRequest) error {
384389
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
@@ -387,6 +392,24 @@ func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Stra
387392
}
388393
}
389394

395+
// WithAdditionalWaitStrategyAndDeadline appends the wait strategy for a container, including deadline
396+
func WithAdditionalWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
397+
return func(req *GenericContainerRequest) error {
398+
if req.WaitingFor == nil {
399+
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
400+
return nil
401+
}
402+
403+
wss := make([]wait.Strategy, 0, len(strategies)+1)
404+
wss = append(wss, req.WaitingFor)
405+
wss = append(wss, strategies...)
406+
407+
req.WaitingFor = wait.ForAll(wss...).WithDeadline(deadline)
408+
409+
return nil
410+
}
411+
}
412+
390413
// WithImageMount mounts an image to a container, passing the source image name,
391414
// the relative subpath to mount in that image, and the mount point in the target container.
392415
// This option validates that the subpath is a relative path, raising an error otherwise.

0 commit comments

Comments
 (0)