Skip to content

Commit 553e925

Browse files
committed
Merge branch 'main' into host-port-binding-ip-family
* main: chore: print Docker Info labels in banner (testcontainers#2681) fix: incorrect parsing of exposedPorts in readiness check (testcontainers#2658) feat: add grafana-lgtm module (testcontainers#2660) Added valkey module (testcontainers#2639) fix: container.Endpoint and wait.FortHTTP to use lowest internal port (testcontainers#2641)
2 parents 6d43e19 + b78a351 commit 553e925

29 files changed

+4188
-72
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
matrix:
9595
go-version: [1.21.x, 1.x]
9696
platform: [ubuntu-latest]
97-
module: [artemis, azurite, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, dolt, elasticsearch, gcloud, inbucket, influxdb, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, vault, vearch, weaviate]
97+
module: [artemis, azurite, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, dolt, elasticsearch, gcloud, grafana-lgtm, inbucket, influxdb, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, valkey, vault, vearch, weaviate]
9898
uses: ./.github/workflows/ci-test-go.yml
9999
with:
100100
go-version: ${{ matrix.go-version }}

.vscode/.testcontainers-go.code-workspace

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@
6161
"name": "module / gcloud",
6262
"path": "../modules/gcloud"
6363
},
64+
{
65+
"name": "module / grafana-lgtm",
66+
"path": "../modules/grafana-lgtm"
67+
},
6468
{
6569
"name": "module / inbucket",
6670
"path": "../modules/inbucket"
@@ -169,6 +173,10 @@
169173
"name": "module / surrealdb",
170174
"path": "../modules/surrealdb"
171175
},
176+
{
177+
"name": "module / valkey",
178+
"path": "../modules/valkey"
179+
},
172180
{
173181
"name": "module / vault",
174182
"path": "../modules/vault"

container.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type DeprecatedContainer interface {
3636
// Container allows getting info about and controlling a single container instance
3737
type Container interface {
3838
GetContainerID() string // get the container id from the provider
39-
Endpoint(context.Context, string) (string, error) // get proto://ip:port string for the first exposed port
39+
Endpoint(context.Context, string) (string, error) // get proto://ip:port string for the lowest exposed port
4040
PortEndpoint(context.Context, nat.Port, string) (string, error) // get proto://ip:port string for the given exposed port
4141
Host(context.Context) (string, error) // get host where the container port is exposed
4242
Inspect(context.Context) (*types.ContainerJSON, error) // get container info

docker.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,23 @@ func (c *DockerContainer) IsRunning() bool {
113113
return c.isRunning
114114
}
115115

116-
// Endpoint gets proto://host:port string for the first exposed port
116+
// Endpoint gets proto://host:port string for the lowest numbered exposed port
117117
// Will returns just host:port if proto is ""
118118
func (c *DockerContainer) Endpoint(ctx context.Context, proto string) (string, error) {
119119
inspect, err := c.Inspect(ctx)
120120
if err != nil {
121121
return "", err
122122
}
123123

124-
ports := inspect.NetworkSettings.Ports
125-
126-
// get first port
127-
var firstPort nat.Port
128-
for p := range ports {
129-
firstPort = p
130-
break
124+
// Get lowest numbered bound port.
125+
var lowestPort nat.Port
126+
for port := range inspect.NetworkSettings.Ports {
127+
if lowestPort == "" || port.Int() < lowestPort.Int() {
128+
lowestPort = port
129+
}
131130
}
132131

133-
return c.PortEndpoint(ctx, firstPort, proto)
132+
return c.PortEndpoint(ctx, lowestPort, proto)
134133
}
135134

136135
// PortEndpoint gets proto://host:port string for the given exposed port

docker_client.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,29 @@ func (c *DockerClient) Info(ctx context.Context) (system.Info, error) {
5757
Server Version: %v
5858
API Version: %v
5959
Operating System: %v
60-
Total Memory: %v MB
60+
Total Memory: %v MB%s
6161
Testcontainers for Go Version: v%s
6262
Resolved Docker Host: %s - %s
6363
Resolved Docker Socket Path: %s
6464
Test SessionID: %s
6565
Test ProcessID: %s
6666
`
67+
infoLabels := ""
68+
if len(dockerInfo.Labels) > 0 {
69+
infoLabels = `
70+
Labels:`
71+
for _, lb := range dockerInfo.Labels {
72+
infoLabels += "\n " + lb
73+
}
74+
}
6775

6876
dockerHost := core.ExtractDockerHost(ctx)
6977

7078
Logger.Printf(infoMessage, packagePath,
71-
dockerInfo.ServerVersion, c.Client.ClientVersion(),
79+
dockerInfo.ServerVersion,
80+
c.Client.ClientVersion(),
7281
dockerInfo.OperatingSystem, dockerInfo.MemTotal/1024/1024,
82+
infoLabels,
7383
internal.Version,
7484
dockerHost,
7585
core.GetDockerHostIPs(),

docs/features/wait/host_port.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The host-port wait strategy will check if the container is listening to a specific port and allows to set the following conditions:
44

55
- a port exposed by the container. The port and protocol to be used, which is represented by a string containing the port number and protocol in the format "80/tcp".
6-
- alternatively, wait for the first exposed port in the container.
6+
- alternatively, wait for the lowest exposed port in the container.
77
- the startup timeout to be used, default is 60 seconds.
88
- the poll interval to be used, default is 100 milliseconds.
99

@@ -19,9 +19,9 @@ req := ContainerRequest{
1919
}
2020
```
2121

22-
## First exposed port in the container
22+
## Lowest exposed port in the container
2323

24-
The wait strategy will use the first exposed port from the container configuration.
24+
The wait strategy will use the lowest exposed port from the container configuration.
2525

2626
```golang
2727
req := ContainerRequest{
@@ -30,12 +30,12 @@ req := ContainerRequest{
3030
}
3131
```
3232

33-
Said that, it could be the case that the container request included ports to be exposed. Therefore using `wait.ForExposedPort` will wait for the first exposed port in the request, because the container configuration retrieved from Docker will already include them.
33+
Said that, it could be the case that the container request included ports to be exposed. Therefore using `wait.ForExposedPort` will wait for the lowest exposed port in the request, because the container configuration retrieved from Docker will already include them.
3434

3535
```golang
3636
req := ContainerRequest{
3737
Image: "docker.io/nginx:alpine",
3838
ExposedPorts: []string{"80/tcp", "9080/tcp"},
3939
WaitingFor: wait.ForExposedPort(),
4040
}
41-
```
41+
```

docs/features/wait/http.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The HTTP wait strategy will check the result of an HTTP(S) request against the container and allows to set the following conditions:
44

5-
- the port to be used. If no port is passed, it will use the first exposed port in the image.
5+
- the port to be used. If no port is passed, it will use the lowest exposed port in the image.
66
- the path to be used.
77
- the HTTP method to be used.
88
- the HTTP request body to be sent.

docs/modules/grafana-lgtm.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Grafana LGTM
2+
3+
Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
4+
5+
## Introduction
6+
7+
The Testcontainers module for Grafana LGTM.
8+
9+
## Adding this module to your project dependencies
10+
11+
Please run the following command to add the Grafana module to your Go dependencies:
12+
13+
```
14+
go get github.com/testcontainers/testcontainers-go/modules/grafanalgtm
15+
```
16+
17+
## Usage example
18+
19+
<!--codeinclude-->
20+
[Creating a Grafana LGTM container](../../modules/grafana-lgtm/examples_test.go) inside_block:runGrafanaLGTMContainer
21+
<!--/codeinclude-->
22+
23+
## Module Reference
24+
25+
### Run function
26+
27+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
28+
29+
!!!info
30+
The `RunContainer(ctx, opts...)` function is deprecated and will be removed in the next major release of _Testcontainers for Go_.
31+
32+
The Grafana LGTM module exposes one entrypoint function to create the Grafana LGTM container, and this function receives three parameters:
33+
34+
```golang
35+
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*GrafanaLGTMContainer, error)
36+
```
37+
38+
- `context.Context`, the Go context.
39+
- `string`, the Docker image to use.
40+
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
41+
42+
### Container Options
43+
44+
When starting the Grafana LGTM container, you can pass options in a variadic way to configure it.
45+
46+
#### Image
47+
48+
If you need to set a different Grafana LGTM Docker image, you can set a valid Docker image as the second argument in the `Run` function.
49+
E.g. `Run(context.Background(), "grafana/otel-lgtm:0.6.0")`.
50+
51+
#### Admin Credentials
52+
53+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
54+
55+
If you need to set different admin credentials in the Grafana LGTM container, you can set them using the `WithAdminCredentials(user, password)` option.
56+
57+
{% include "../features/common_functional_options.md" %}
58+
59+
### Container Methods
60+
61+
The Grafana LGTM container exposes the following methods:
62+
63+
!!!info
64+
All the endpoint methods return their endpoints in the format `<host>:<port>`, so please use them accordingly to configure your client.
65+
66+
#### Grafana Endpoint
67+
68+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
69+
70+
The `HttpEndpoint(ctx)` method returns the HTTP endpoint to connect to Grafana, using the default `3000` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
71+
72+
#### Loki Endpoint
73+
74+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
75+
76+
The `LokiEndpoint(ctx)` method returns the HTTP endpoint to connect to Loki, using the default `3100` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
77+
78+
#### Tempo Endpoint
79+
80+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
81+
82+
The `TempoEndpoint(ctx)` method returns the HTTP endpoint to connect to Tempo, using the default `3200` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
83+
84+
#### Otel HTTP Endpoint
85+
86+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
87+
88+
The `OtelHTTPEndpoint(ctx)` method returns the endpoint to connect to Otel using HTTP, using the default `4318` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
89+
90+
#### Otel gRPC Endpoint
91+
92+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
93+
94+
The `OtelGRPCEndpoint(ctx)` method returns the endpoint to connect to Otel using gRPC, using the default `4317` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
95+
96+
#### Prometheus Endpoint
97+
98+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
99+
100+
The `PrometheusHttpEndpoint(ctx)` method returns the endpoint to connect to Prometheus, using the default `9090` port. The same method with the `Must` prefix returns just the endpoing, and panics if an error occurs.
101+
102+
## Examples
103+
104+
### Traces, Logs and Prometheus metrics for a simple Go process
105+
106+
In this example, a simple application is created to generate traces, logs, and Prometheus metrics.
107+
The application sends data to Grafana LGTM, and the Otel SDK is used to send the data.
108+
The example demonstrates how to set up the Otel SDK and run the Grafana LGTM module,
109+
configuring the Otel library to send data to Grafana LGTM thanks to the endpoints provided by the Grafana LGTM container.
110+
111+
<!--codeinclude-->
112+
[App sending Otel data](../../modules/grafana-lgtm/examples_test.go) inside_block:rollDiceApp
113+
[Setup Otel SDK](../../modules/grafana-lgtm/examples_test.go) inside_block:setupOTelSDK
114+
[Run the Grafana LGTM container](../../modules/grafana-lgtm/examples_test.go) inside_block:ExampleRun_otelCollector
115+
<!--/codeinclude-->

docs/modules/valkey.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Valkey
2+
3+
Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
4+
5+
## Introduction
6+
7+
The Testcontainers module for Valkey.
8+
9+
## Adding this module to your project dependencies
10+
11+
Please run the following command to add the Valkey module to your Go dependencies:
12+
13+
```
14+
go get github.com/testcontainers/testcontainers-go/modules/valkey
15+
```
16+
17+
## Usage example
18+
19+
<!--codeinclude-->
20+
[Creating a Valkey container](../../modules/valkey/examples_test.go) inside_block:runValkeyContainer
21+
<!--/codeinclude-->
22+
23+
## Module Reference
24+
25+
### Run function
26+
27+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
28+
29+
!!!info
30+
The `RunContainer(ctx, opts...)` function is deprecated and will be removed in the next major release of _Testcontainers for Go_.
31+
32+
The Valkey module exposes one entrypoint function to create the Valkey container, and this function receives three parameters:
33+
34+
```golang
35+
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*ValkeyContainer, error)
36+
```
37+
38+
- `context.Context`, the Go context.
39+
- `string`, the Docker image to use.
40+
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
41+
42+
### Container Options
43+
44+
When starting the Valkey container, you can pass options in a variadic way to configure it.
45+
46+
#### Image
47+
48+
If you need to set a different Valkey Docker image, you can set a valid Docker image as the second argument in the `Run` function.
49+
E.g. `Run(context.Background(), "valkey/valkey:7.2.5")`.
50+
51+
{% include "../features/common_functional_options.md" %}
52+
53+
#### Snapshotting
54+
55+
By default Valkey saves snapshots of the dataset on disk, in a binary file called dump.rdb. You can configure Valkey to have it save the dataset every `N` seconds if there are at least `M` changes in the dataset. E.g. `WithSnapshotting(10, 1)`.
56+
57+
#### Log Level
58+
59+
You can easily set the valkey logging level. E.g. `WithLogLevel(LogLevelDebug)`.
60+
61+
#### Valkey configuration
62+
63+
In the case you have a custom config file for Valkey, it's possible to copy that file into the container before it's started. E.g. `WithConfigFile(filepath.Join("testdata", "valkey.conf"))`.
64+
65+
### Container Methods
66+
67+
The Valkey container exposes the following methods:
68+
69+
#### ConnectionString
70+
71+
This method returns the connection string to connect to the Valkey container, using the default `6379` port.
72+
73+
<!--codeinclude-->
74+
[Get connection string](../../modules/valkey/valkey_test.go) inside_block:connectionString
75+
<!--/codeinclude-->

0 commit comments

Comments
 (0)