Skip to content

Commit 1c48f05

Browse files
committed
Merge branch 'main' into dependabot/go_modules/github.com/magiconair/properties-1.8.10
* main: feat(postgres): add WithOrderedInitScripts for Postgres testcontainers (#3121) feat(redis): add TLS support (#3115) feat: add Docker Model Runner module (#3106)
2 parents 7349e9a + 66401ba commit 1c48f05

39 files changed

Lines changed: 1809 additions & 209 deletions

.github/dependabot.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ updates:
2929
- /modules/couchbase
3030
- /modules/databend
3131
- /modules/dind
32+
- /modules/dockermodelrunner
3233
- /modules/dolt
3334
- /modules/dynamodb
3435
- /modules/elasticsearch

.vscode/.testcontainers-go.code-workspace

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
"name": "example / nginx",
1010
"path": "../examples/nginx"
1111
},
12+
{
13+
"name": "module / aerospike",
14+
"path": "../modules/aerospike"
15+
},
1216
{
1317
"name": "module / arangodb",
1418
"path": "../modules/arangodb"
@@ -61,6 +65,10 @@
6165
"name": "module / dind",
6266
"path": "../modules/dind"
6367
},
68+
{
69+
"name": "module / dockermodelrunner",
70+
"path": "../modules/dockermodelrunner"
71+
},
6472
{
6573
"name": "module / dolt",
6674
"path": "../modules/dolt"

docs/modules/dockermodelrunner.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Docker Model Runner
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 DockerModelRunner.
8+
9+
## Adding this module to your project dependencies
10+
11+
Please run the following command to add the DockerModelRunner module to your Go dependencies:
12+
13+
```
14+
go get github.com/testcontainers/testcontainers-go/modules/dockermodelrunner
15+
```
16+
17+
## Usage example
18+
19+
<!--codeinclude-->
20+
[Creating a DockerModelRunner container](../../modules/dockermodelrunner/examples_test.go) inside_block:runWithModel
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+
The Docker Model Runner module exposes two entrypoint functions to create the Docker Model Runner container:
30+
31+
#### Run
32+
33+
This function receives two parameters:
34+
35+
```golang
36+
func Run(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*Container, error)
37+
```
38+
39+
- `context.Context`, the Go context.
40+
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
41+
42+
!!! info
43+
This function will use the default `socat` image under the hood. Please refer to the [socat module](../socat.md) for more information.
44+
45+
### Container Options
46+
47+
When starting the Docker Model Runner container, you can pass options in a variadic way to configure it.
48+
49+
#### Image
50+
51+
Use the second argument in the `Run` function to set a valid Docker image.
52+
In example: `Run(context.Background(), "alpine/socat:1.8.0.1")`.
53+
54+
{% include "../features/common_functional_options.md" %}
55+
56+
#### WithModel
57+
58+
- 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>
59+
60+
Use the `WithModel` option to set the model to pull when the container is started. Please be aware, that only Models as OCI Artifacts are compatible with Docker Model Runner.
61+
62+
```golang
63+
dockermodelrunner.WithModel("ai/llama3.2:latest")
64+
```
65+
66+
!!! warning
67+
Multiple calls to this function overrides the previous value.
68+
69+
You can find a curated collection of cutting-edge AI models as OCI Artifacts, from lightweight on-device models to high-performance LLMs on [Docker Hub](https://hub.docker.com/u/ai).
70+
71+
### Container Methods
72+
73+
The Docker Model Runner container exposes the following methods:
74+
75+
#### PullModel
76+
77+
- 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>
78+
79+
Use the `PullModel` method to pull a model from the Docker Model Runner.
80+
81+
<!--codeinclude-->
82+
[Pulling a model at runtime](../../modules/dockermodelrunner/examples_test.go) inside_block:runPullModel
83+
<!--/codeinclude-->
84+
85+
!!! info
86+
You can find a curated collection of cutting-edge AI models as OCI Artifacts, from lightweight on-device models to high-performance LLMs on [Docker Hub](https://hub.docker.com/u/ai).
87+
88+
#### InspectModel
89+
90+
- 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>
91+
92+
Use the `InspectModel` method to inspect a model from the Docker Model Runner, by providing the model namespace and name.
93+
94+
<!--codeinclude-->
95+
[Getting a model at runtime](../../modules/dockermodelrunner/examples_test.go) inside_block:runInspectModel
96+
<!--/codeinclude-->
97+
98+
The namespace and name of the model is in the format of `<name>:<tag>`, which defines Models as OCI Artifacts in Docker Hub, therefore the namespace is the organization and the name is the repository.
99+
100+
E.g. `ai/smollm2:360M-Q4_K_M`. See [Models as OCI Artifacts](https://hub.docker.com/u/ai) for more information.
101+
102+
#### ListModels
103+
104+
- 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>
105+
106+
Use the `ListModels` method to list all models that are already pulled locally, using the Docker Model Runner format.
107+
108+
<!--codeinclude-->
109+
[Listing all models](../../modules/dockermodelrunner/examples_test.go) inside_block:runListModels
110+
<!--/codeinclude-->

docs/modules/postgres.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ An example of a `*.sh` script that creates a user and database is shown below:
7070
[Init script content](../../modules/postgres/testdata/init-user-db.sh)
7171
<!--/codeinclude-->
7272
73+
#### Ordered Init Scripts
74+
75+
If you would like to run the init scripts in a specific order, you can use the `WithOrderedInitScripts` function, which copies the given scripts in the order they are provided to the container, prefixed with the order number so that Postgres executes them in the correct order.
76+
77+
<!--codeinclude-->
78+
[Ordered init scripts](../../modules/postgres/postgres_test.go) inside_block:orderedInitScripts
79+
<!--/codeinclude-->
80+
7381
#### Database configuration
7482
7583
In the case you have a custom config file for Postgres, it's possible to copy that file into the container before it's started, using the `WithConfigFile(cfgPath string)` function.

docs/modules/redis.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ By default Redis saves snapshots of the dataset on disk, in a binary file called
6262

6363
#### Log Level
6464

65-
By default Redis saves snapshots of the dataset on disk, in a binary file called dump.rdb. You can configure Redis to have it save the dataset every N seconds if there are at least M changes in the dataset. E.g. `WithLogLevel(LogLevelDebug)`.
65+
By default Redis produces a log message to the standard Redis log, the format accepts printf-alike specifiers, while level is a string describing the log level to use when emitting the log, and must be one of the following: `LogLevelDebug`, `LogLevelVerbose`, `LogLevelNotice`, `LogLevelWarning`. E.g. `WithLogLevel(LogLevelDebug)`. If the specified log level is invalid, verbose is used by default.
6666

6767
!!!tip
6868
Please check [Redis docs on logging](https://redis.io/docs/reference/modules/modules-api-ref/#redismodule_log) for more information.
@@ -71,24 +71,33 @@ By default Redis saves snapshots of the dataset on disk, in a binary file called
7171

7272
In the case you have a custom config file for Redis, it's possible to copy that file into the container before it's started. E.g. `WithConfigFile(filepath.Join("testdata", "redis7.conf"))`.
7373

74+
#### WithTLS
75+
76+
In the case you want to enable TLS for the Redis container, you can use the `WithTLS()` option. This options enables TLS on the `6379/tcp` port and uses a secure URL (e.g. `rediss://host:port`).
77+
78+
!!!info
79+
In case you want to use Non-mutual TLS (i.e. client authentication is not required), you can customize the CMD arguments by using the `WithCmdArgs` option. E.g. `WithCmdArgs("--tls-auth-clients no")`.
80+
81+
The module automatically generates three certificates, a CA certificate, a client certificate and a Redis certificate. Please use the `TLSConfig()` container method to get the TLS configuration and use it to configure the Redis client. See more details in the [TLSConfig](#tlsconfig) section.
82+
7483
### Container Methods
7584

7685
#### ConnectionString
7786

78-
This method returns the connection string to connect to the Redis container, using the default `6379` port.
87+
This method returns the connection string to connect to the Redis container, using the default `6379` port, and `redis` schema.
7988

8089
<!--codeinclude-->
8190
[Get connection string](../../modules/redis/redis_test.go) inside_block:connectionString
8291
<!--/codeinclude-->
8392

84-
### Redis variants
93+
If the container is started with TLS enabled, the connection string is `rediss://host:port`, using the `rediss` schema.
8594

86-
It's possible to use the Redis container with Redis-Stack. You simply need to update the image name.
95+
#### TLSConfig
8796

88-
<!--codeinclude-->
89-
[Image for Redis-Stack](../../modules/redis/redis_test.go) inside_block:redisStackImage
90-
<!--/codeinclude-->
97+
This method returns the TLS configuration for the Redis container, nil if TLS is not enabled.
9198

9299
<!--codeinclude-->
93-
[Image for Redis-Stack Server](../../modules/redis/redis_test.go) inside_block:redisStackServerImage
100+
[Get TLS config](../../modules/redis/redis_test.go) inside_block:tlsConfig
94101
<!--/codeinclude-->
102+
103+
In the above example, the options are used to configure a Redis client with TLS enabled.

docs/modules/socat.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ When starting the Socat container, you can pass options in a variadic way to con
4747

4848
#### Image
4949

50+
The module exposes a default image:
51+
52+
<!--codeinclude-->
53+
[Default Image](../../modules/socat/socat.go) inside_block:defaultImage
54+
<!--/codeinclude-->
55+
5056
Use the second argument in the `Run` function to set a valid Docker image.
51-
In example: `Run(context.Background(), "alpine/socat:1.8.0.1")`.
57+
In example: `Run(context.Background(), DefaultImage)`.
5258

5359
{% include "../features/common_functional_options.md" %}
5460

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,12 @@ nav:
8181
- modules/couchbase.md
8282
- modules/databend.md
8383
- modules/dind.md
84+
- modules/dockermodelrunner.md
8485
- modules/dolt.md
8586
- modules/dynamodb.md
8687
- modules/elasticsearch.md
8788
- modules/etcd.md
89+
- modules/firebase.md
8890
- modules/gcloud.md
8991
- modules/grafana-lgtm.md
9092
- modules/inbucket.md

modules/dockermodelrunner/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include ../../commons-test.mk
2+
3+
.PHONY: test
4+
test:
5+
$(MAKE) test-dockermodelrunner
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dockermodelrunner_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/testcontainers/testcontainers-go"
11+
"github.com/testcontainers/testcontainers-go/log"
12+
)
13+
14+
// skipIfDockerDesktopNotRunning skips the test if Docker Desktop is not running,
15+
// using the testing library and the log.TestLogger of Testcontainers
16+
func skipIfDockerDesktopNotRunning(t *testing.T) {
17+
t.Helper()
18+
isDDRunning, err := isDockerDesktopRunning(log.TestLogger(t))
19+
require.NoError(t, err)
20+
21+
if !isDDRunning {
22+
t.Skipf("Skipping because Docker Desktop is not running")
23+
}
24+
}
25+
26+
// isDockerDesktopRunning checks if Docker Desktop is running.
27+
func isDockerDesktopRunning(l log.Logger) (bool, error) {
28+
cli, err := testcontainers.NewDockerClientWithOpts(context.Background())
29+
if err != nil {
30+
return false, fmt.Errorf("failed to create docker client: %w", err)
31+
}
32+
33+
info, err := cli.Info(context.Background())
34+
if err != nil {
35+
return false, fmt.Errorf("failed to get docker info: %w", err)
36+
}
37+
38+
if info.OperatingSystem == "Docker Desktop" {
39+
return true, nil
40+
}
41+
42+
l.Printf("Skipping because Docker Desktop is not running")
43+
return false, nil
44+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package dockermodelrunner
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/testcontainers/testcontainers-go"
9+
"github.com/testcontainers/testcontainers-go/modules/dockermodelrunner/internal/sdk/client"
10+
"github.com/testcontainers/testcontainers-go/modules/socat"
11+
"github.com/testcontainers/testcontainers-go/wait"
12+
)
13+
14+
const (
15+
modelRunnerEntrypoint = "model-runner.docker.internal"
16+
modelRunnerPort = 80
17+
)
18+
19+
// Container represents the DockerModelRunner container type used in the module
20+
type Container struct {
21+
*socat.Container
22+
*client.Client
23+
model string
24+
baseURL string
25+
}
26+
27+
// Run creates an instance of the DockerModelRunner container type.
28+
func Run(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*Container, error) {
29+
settings := defaultOptions()
30+
31+
// Process model runner options.
32+
for _, opt := range opts {
33+
if apply, ok := opt.(Option); ok {
34+
if err := apply(&settings); err != nil {
35+
return nil, err
36+
}
37+
}
38+
}
39+
40+
// Add socat options, which are applied to the socat container.
41+
opts = append(opts, testcontainers.WithWaitStrategy(
42+
wait.ForListeningPort("80/tcp"),
43+
wait.ForHTTP("/").WithPort("80/tcp").WithStatusCodeMatcher(func(status int) bool {
44+
return status == http.StatusOK
45+
}),
46+
))
47+
opts = append(opts, socat.WithTarget(socat.NewTarget(modelRunnerPort, modelRunnerEntrypoint)))
48+
49+
socatCtr, err := socat.Run(ctx, socat.DefaultImage, opts...)
50+
var c *Container
51+
if socatCtr != nil {
52+
c = &Container{Container: socatCtr, model: settings.model}
53+
}
54+
55+
if err != nil {
56+
return c, fmt.Errorf("socat run: %w", err)
57+
}
58+
59+
c.baseURL = socatCtr.TargetURL(modelRunnerPort).String()
60+
61+
c.Client = client.NewClient(c.baseURL)
62+
63+
if settings.model != "" {
64+
err := c.PullModel(ctx, settings.model)
65+
if err != nil {
66+
return c, fmt.Errorf("pull model: %w", err)
67+
}
68+
}
69+
70+
return c, nil
71+
}

0 commit comments

Comments
 (0)