Skip to content

Commit 8a498b7

Browse files
jm96441nmdelapenya
andauthored
feat: add WithReuseByName for modifying Generic Container Requests (#3064)
* added WithReuse and WithContainerName functions for modifying the GenericContainerRequest * Simplify API surface for WithReuse * docs: document new option * fix: lint * chore: rename option to WithReuseByName * chore: add test for the new option * chore: simplify test assertions --------- Co-authored-by: Manuel de la Peña <mdelapenya@gmail.com>
1 parent 1c9b01b commit 8a498b7

5 files changed

Lines changed: 88 additions & 0 deletions

File tree

docs/features/common_functional_options.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,19 @@ The above example is updating the predefined command of the image, **appending**
302302

303303
!!!info
304304
This can't be used to replace the command, only to append options.
305+
306+
#### WithReuseByName
307+
308+
- 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>
309+
310+
This option marks a container to be reused if it exists or create a new one if it doesn't.
311+
With the current implementation, the container name must be provided to identify the container to be reused.
312+
313+
```golang
314+
ctr, err := mymodule.Run(ctx, "docker.io/myservice:1.2.3",
315+
testcontainers.WithReuseByName("my-container-name"),
316+
)
317+
```
318+
319+
!!!warning
320+
Reusing a container is experimental and the API is subject to change for a more robust implementation that is not based on container names.

docs/modules/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ In order to simplify the creation of the container for a given module, `Testcont
216216
- `testcontainers.WithHostConfigModifier`: a function that sets the host config Docker type for the container request. Please see [Advanced Settings](../features/creating_container.md#advanced-settings) for more information.
217217
- `testcontainers.WithEndpointSettingsModifier`: a function that sets the endpoint settings Docker type for the container request. Please see [Advanced Settings](../features/creating_container.md#advanced-settings) for more information.
218218
- `testcontainers.CustomizeRequest`: a function that merges the default options with the ones provided by the user. Recommended for completely customizing the container request.
219+
- `testcontainers.WithReuseByName`: a function that marks a container to be reused if it exists or create a new one if it doesn't.
219220

220221
### Update Go dependencies in the modules
221222

modules/ollama/ollama_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,38 @@ func TestOllama(t *testing.T) {
7171
})
7272
}
7373

74+
func TestOllama_withReuse(t *testing.T) {
75+
ctx := context.Background()
76+
77+
ctr, err := ollama.Run(ctx, "ollama/ollama:0.5.7", testcontainers.WithReuseByName("ollama-container"))
78+
testcontainers.CleanupContainer(t, ctr)
79+
require.NoError(t, err)
80+
81+
model := "all-minilm"
82+
83+
_, _, err = ctr.Exec(context.Background(), []string{"ollama", "pull", model})
84+
require.NoError(t, err)
85+
86+
_, _, err = ctr.Exec(context.Background(), []string{"ollama", "run", model})
87+
require.NoError(t, err)
88+
89+
assertLoadedModel(t, ctr)
90+
91+
t.Run("reuse-container", func(t *testing.T) {
92+
ctr2, err := ollama.Run(ctx, "ollama/ollama:0.5.7", testcontainers.WithReuseByName("ollama-container"))
93+
testcontainers.CleanupContainer(t, ctr2)
94+
require.NoError(t, err)
95+
96+
_, _, err = ctr2.Exec(context.Background(), []string{"ollama", "pull", model})
97+
require.NoError(t, err)
98+
99+
_, _, err = ctr2.Exec(context.Background(), []string{"ollama", "run", model})
100+
require.NoError(t, err)
101+
102+
assertLoadedModel(t, ctr2)
103+
})
104+
}
105+
74106
// assertLoadedModel checks if the model is loaded in the container.
75107
// For that, it checks if the response of the /api/tags endpoint
76108
// contains the model name.

options.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package testcontainers
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net/url"
78
"time"
@@ -105,6 +106,19 @@ func WithHostPortAccess(ports ...int) CustomizeRequestOption {
105106
}
106107
}
107108

109+
// WithReuseByName will mark a container to be reused if it exists or create a new one if it doesn't.
110+
// A container name must be provided to identify the container to be reused.
111+
func WithReuseByName(containerName string) CustomizeRequestOption {
112+
return func(req *GenericContainerRequest) error {
113+
if containerName == "" {
114+
return errors.New("container name must be provided for reuse")
115+
}
116+
req.Name = containerName
117+
req.Reuse = true
118+
return nil
119+
}
120+
}
121+
108122
// WithImage sets the image for a container
109123
func WithImage(image string) CustomizeRequestOption {
110124
return func(req *GenericContainerRequest) error {

options_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,28 @@ func TestWithDockerfile(t *testing.T) {
551551
require.Equal(t, "latest", req.Tag)
552552
require.Equal(t, map[string]*string{"ARG1": nil, "ARG2": nil}, req.BuildArgs)
553553
}
554+
555+
func TestWithReuseByName_Succeeds(t *testing.T) {
556+
t.Parallel()
557+
req := &testcontainers.GenericContainerRequest{}
558+
containerName := "pg-test"
559+
560+
opt := testcontainers.WithReuseByName(containerName)
561+
err := opt.Customize(req)
562+
563+
require.NoError(t, err)
564+
require.True(t, req.Reuse)
565+
require.Equal(t, containerName, req.Name)
566+
}
567+
568+
func TestWithReuseByName_ErrorsWithoutContainerNameProvided(t *testing.T) {
569+
t.Parallel()
570+
req := &testcontainers.GenericContainerRequest{}
571+
572+
opt := testcontainers.WithReuseByName("")
573+
err := opt.Customize(req)
574+
575+
require.ErrorContains(t, err, "container name must be provided for reuse")
576+
require.False(t, req.Reuse)
577+
require.Empty(t, req.Name)
578+
}

0 commit comments

Comments
 (0)