Skip to content

Commit db70ad3

Browse files
committed
Merge branch 'main' into etcd-module
* main: chore: use a much smaller image for testing (testcontainers#2795) fix: parallel containers clean race (testcontainers#2790) fix(registry): wait for (testcontainers#2793) fix: container timeout test (testcontainers#2792) docs: document redpanda options (testcontainers#2789)
2 parents dda3352 + 738e8fc commit db70ad3

File tree

7 files changed

+122
-77
lines changed

7 files changed

+122
-77
lines changed

docker_auth_test.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func prepareLocalRegistryWithAuth(t *testing.T) string {
286286
ContainerFilePath: "/data",
287287
},
288288
},
289-
WaitingFor: wait.ForExposedPort(),
289+
WaitingFor: wait.ForHTTP("/").WithPort("5000/tcp"),
290290
}
291291
// }
292292

@@ -311,9 +311,6 @@ func prepareLocalRegistryWithAuth(t *testing.T) string {
311311
removeImageFromLocalCache(t, addr+"/redis:5.0-alpine")
312312
})
313313

314-
_, cancel := context.WithCancel(context.Background())
315-
t.Cleanup(cancel)
316-
317314
return addr
318315
}
319316

docker_test.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -658,14 +658,12 @@ func TestContainerCreationTimesOutWithHttp(t *testing.T) {
658658
ExposedPorts: []string{
659659
nginxDefaultPort,
660660
},
661-
WaitingFor: wait.ForHTTP("/").WithStartupTimeout(1 * time.Second),
661+
WaitingFor: wait.ForHTTP("/").WithStartupTimeout(time.Millisecond * 500),
662662
},
663663
Started: true,
664664
})
665665
CleanupContainer(t, nginxC)
666-
if err == nil {
667-
t.Error("Expected timeout")
668-
}
666+
require.Error(t, err, "expected timeout")
669667
}
670668

671669
func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) {

docs/modules/redpanda.md

+71
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ If you need to enable TLS use `WithTLS` with a valid PEM encoded certificate and
6161

6262
#### Additional Listener
6363

64+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.28.0"><span class="tc-version">:material-tag: v0.28.0</span></a>
65+
6466
There are scenarios where additional listeners are needed, for example if you
6567
want to consume/from another container in the same network
6668

@@ -79,12 +81,77 @@ Produce messages using the new registered listener
7981
[Produce/consume via registered listener](../../modules/redpanda/redpanda_test.go) inside_block:withListenerExec
8082
<!--/codeinclude-->
8183

84+
#### Adding Service Accounts
85+
86+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
87+
88+
It's possible to add service accounts to the Redpanda container using the `WithNewServiceAccount` option, setting the service account name and its password.
89+
E.g. `WithNewServiceAccount("service-account", "password")`.
90+
91+
#### Adding Super Users
92+
93+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
94+
95+
When a super user is needed, you can use the `WithSuperusers` option, passing a variadic list of super users.
96+
E.g. `WithSuperusers("superuser-1", "superuser-2")`.
97+
98+
#### Enabling SASL
99+
100+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
101+
102+
The `WithEnableSASL()` option enables SASL scram sha authentication. By default, no authentication (plaintext) is used.
103+
When setting an authentication method, make sure to add users as well and authorize them using the `WithSuperusers()` option.
104+
105+
#### WithEnableKafkaAuthorization
106+
107+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
108+
109+
The `WithEnableKafkaAuthorization` enables authorization for connections on the Kafka API.
110+
111+
#### WithEnableWasmTransform
112+
113+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.28.0"><span class="tc-version">:material-tag: v0.28.0</span></a>
114+
115+
The `WithEnableWasmTransform` enables wasm transform.
116+
117+
!!!warning
118+
Should not be used with RP versions before 23.3
119+
120+
#### WithEnableSchemaRegistryHTTPBasicAuth
121+
122+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
123+
124+
The `WithEnableSchemaRegistryHTTPBasicAuth` enables HTTP basic authentication for the Schema Registry.
125+
126+
#### WithAutoCreateTopics
127+
128+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.22.0"><span class="tc-version">:material-tag: v0.22.0</span></a>
129+
130+
The `WithAutoCreateTopics` option enables the auto-creation of topics.
131+
132+
#### WithTLS
133+
134+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.24.0"><span class="tc-version">:material-tag: v0.24.0</span></a>
135+
136+
The `WithTLS` option enables TLS encryption. It requires a valid PEM encoded certificate and key, passed as byte slices.
137+
E.g. `WithTLS([]byte(cert), []byte(key))`.
138+
139+
#### WithBootstrapConfig
140+
141+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.33.0"><span class="tc-version">:material-tag: v0.33.0</span></a>
142+
143+
`WithBootstrapConfig` adds an arbitrary config key-value pair to the Redpanda container. Per the name, this config will be interpolated into the generated bootstrap
144+
config file, which is particularly useful for configs requiring a restart when otherwise applied to a running Redpanda instance.
145+
E.g. `WithBootstrapConfig("config_key", config_value)`, where `config_value` is of type `any`.
146+
82147
### Container Methods
83148
84149
The Redpanda container exposes the following methods:
85150
86151
#### KafkaSeedBroker
87152
153+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
154+
88155
KafkaSeedBroker returns the seed broker that should be used for connecting
89156
to the Kafka API with your Kafka client. It'll be returned in the format:
90157
"host:port" - for example: "localhost:55687".
@@ -95,6 +162,8 @@ to the Kafka API with your Kafka client. It'll be returned in the format:
95162

96163
#### SchemaRegistryAddress
97164

165+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
166+
98167
SchemaRegistryAddress returns the address to the schema registry API. This
99168
is an HTTP-based API and thus the returned format will be: http://host:port.
100169

@@ -105,6 +174,8 @@ is an HTTP-based API and thus the returned format will be: http://host:port.
105174

106175
#### AdminAPIAddress
107176

177+
- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.20.0"><span class="tc-version">:material-tag: v0.20.0</span></a>
178+
108179
AdminAPIAddress returns the address to the Redpanda Admin API. This
109180
is an HTTP-based API and thus the returned format will be: http://host:port.
110181

modules/registry/registry.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,9 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
220220
// convenient for testing
221221
"REGISTRY_STORAGE_DELETE_ENABLED": "true",
222222
},
223-
WaitingFor: wait.ForAll(
224-
wait.ForExposedPort(),
225-
wait.ForLog("listening on [::]:5000").WithStartupTimeout(10*time.Second),
226-
),
223+
WaitingFor: wait.ForHTTP("/").
224+
WithPort(registryPort).
225+
WithStartupTimeout(10 * time.Second),
227226
}
228227

229228
genericContainerReq := testcontainers.GenericContainerRequest{

parallel.go

+29-41
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package testcontainers
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"sync"
87
)
@@ -32,24 +31,27 @@ func (gpe ParallelContainersError) Error() string {
3231
return fmt.Sprintf("%v", gpe.Errors)
3332
}
3433

34+
// parallelContainersResult represents result.
35+
type parallelContainersResult struct {
36+
ParallelContainersRequestError
37+
Container Container
38+
}
39+
3540
func parallelContainersRunner(
3641
ctx context.Context,
3742
requests <-chan GenericContainerRequest,
38-
errorsCh chan<- ParallelContainersRequestError,
39-
containers chan<- Container,
43+
results chan<- parallelContainersResult,
4044
wg *sync.WaitGroup,
4145
) {
4246
defer wg.Done()
4347
for req := range requests {
4448
c, err := GenericContainer(ctx, req)
49+
res := parallelContainersResult{Container: c}
4550
if err != nil {
46-
errorsCh <- ParallelContainersRequestError{
47-
Request: req,
48-
Error: errors.Join(err, TerminateContainer(c)),
49-
}
50-
continue
51+
res.Request = req
52+
res.Error = err
5153
}
52-
containers <- c
54+
results <- res
5355
}
5456
}
5557

@@ -65,41 +67,26 @@ func ParallelContainers(ctx context.Context, reqs ParallelContainerRequest, opt
6567
}
6668

6769
tasksChan := make(chan GenericContainerRequest, tasksChanSize)
68-
errsChan := make(chan ParallelContainersRequestError)
69-
resChan := make(chan Container)
70-
waitRes := make(chan struct{})
71-
72-
containers := make([]Container, 0)
73-
errors := make([]ParallelContainersRequestError, 0)
70+
resultsChan := make(chan parallelContainersResult, tasksChanSize)
71+
done := make(chan struct{})
7472

75-
wg := sync.WaitGroup{}
73+
var wg sync.WaitGroup
7674
wg.Add(tasksChanSize)
7775

7876
// run workers
7977
for i := 0; i < tasksChanSize; i++ {
80-
go parallelContainersRunner(ctx, tasksChan, errsChan, resChan, &wg)
78+
go parallelContainersRunner(ctx, tasksChan, resultsChan, &wg)
8179
}
8280

81+
var errs []ParallelContainersRequestError
82+
containers := make([]Container, 0, len(reqs))
8383
go func() {
84-
for {
85-
select {
86-
case c, ok := <-resChan:
87-
if !ok {
88-
resChan = nil
89-
} else {
90-
containers = append(containers, c)
91-
}
92-
case e, ok := <-errsChan:
93-
if !ok {
94-
errsChan = nil
95-
} else {
96-
errors = append(errors, e)
97-
}
98-
}
99-
100-
if resChan == nil && errsChan == nil {
101-
waitRes <- struct{}{}
102-
break
84+
defer close(done)
85+
for res := range resultsChan {
86+
if res.Error != nil {
87+
errs = append(errs, res.ParallelContainersRequestError)
88+
} else {
89+
containers = append(containers, res.Container)
10390
}
10491
}
10592
}()
@@ -108,14 +95,15 @@ func ParallelContainers(ctx context.Context, reqs ParallelContainerRequest, opt
10895
tasksChan <- req
10996
}
11097
close(tasksChan)
98+
11199
wg.Wait()
112-
close(resChan)
113-
close(errsChan)
114100

115-
<-waitRes
101+
close(resultsChan)
102+
103+
<-done
116104

117-
if len(errors) != 0 {
118-
return containers, ParallelContainersError{Errors: errors}
105+
if len(errs) != 0 {
106+
return containers, ParallelContainersError{Errors: errs}
119107
}
120108

121109
return containers, nil

parallel_test.go

+10-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package testcontainers
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"testing"
87
"time"
@@ -99,23 +98,18 @@ func TestParallelContainers(t *testing.T) {
9998
for _, tc := range tests {
10099
t.Run(tc.name, func(t *testing.T) {
101100
res, err := ParallelContainers(context.Background(), tc.reqs, ParallelContainersOptions{})
102-
if err != nil {
103-
require.NotZero(t, tc.expErrors)
104-
var e ParallelContainersError
105-
errors.As(err, &e)
106-
if len(e.Errors) != tc.expErrors {
107-
t.Fatalf("expected errors: %d, got: %d\n", tc.expErrors, len(e.Errors))
108-
}
109-
}
110-
111101
for _, c := range res {
112-
c := c
113102
CleanupContainer(t, c)
114103
}
115104

116-
if len(res) != tc.resLen {
117-
t.Fatalf("expected containers: %d, got: %d\n", tc.resLen, len(res))
105+
if tc.expErrors != 0 {
106+
require.Error(t, err)
107+
var errs ParallelContainersError
108+
require.ErrorAs(t, err, &errs)
109+
require.Len(t, errs.Errors, tc.expErrors)
118110
}
111+
112+
require.Len(t, res, tc.resLen)
119113
})
120114
}
121115
}
@@ -157,11 +151,8 @@ func TestParallelContainersWithReuse(t *testing.T) {
157151
ctx := context.Background()
158152

159153
res, err := ParallelContainers(ctx, parallelRequest, ParallelContainersOptions{})
160-
if err != nil {
161-
var e ParallelContainersError
162-
errors.As(err, &e)
163-
t.Fatalf("expected errors: %d, got: %d\n", 0, len(e.Errors))
154+
for _, c := range res {
155+
CleanupContainer(t, c)
164156
}
165-
// Container is reused, only terminate first container
166-
CleanupContainer(t, res[0])
157+
require.NoError(t, err)
167158
}

wait/exec_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ import (
2222
func ExampleExecStrategy() {
2323
ctx := context.Background()
2424
req := testcontainers.ContainerRequest{
25-
Image: "localstack/localstack:latest",
26-
WaitingFor: wait.ForExec([]string{"awslocal", "dynamodb", "list-tables"}),
25+
Image: "alpine:latest",
26+
Entrypoint: []string{"tail", "-f", "/dev/null"}, // needed for the container to stay alive
27+
WaitingFor: wait.ForExec([]string{"ls", "/"}).WithStartupTimeout(1 * time.Second),
2728
}
2829

29-
localstack, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
30+
ctr, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
3031
ContainerRequest: req,
3132
Started: true,
3233
})
3334
defer func() {
34-
if err := testcontainers.TerminateContainer(localstack); err != nil {
35+
if err := testcontainers.TerminateContainer(ctr); err != nil {
3536
log.Printf("failed to terminate container: %s", err)
3637
}
3738
}()
@@ -40,7 +41,7 @@ func ExampleExecStrategy() {
4041
return
4142
}
4243

43-
state, err := localstack.State(ctx)
44+
state, err := ctr.State(ctx)
4445
if err != nil {
4546
log.Printf("failed to get container state: %s", err)
4647
return

0 commit comments

Comments
 (0)