Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
da818b9
chore!: migrate to moby modules
thaJeztah Feb 13, 2026
7d43e2a
fix: WIP
thaJeztah Mar 11, 2026
4dd57b9
chore: use authconfig.Encode
thaJeztah Mar 30, 2026
fd787a6
chore: modules/compose: bump compose v5.1.2 and docker/cli v29.3.1
thaJeztah Mar 30, 2026
c397706
chore: WIP: update modules to master
thaJeztah Mar 31, 2026
525515d
chore: use network.Port.Port again
thaJeztah Mar 31, 2026
6e9420b
fix: correct inverted port condition in HTTP wait strategy
aausch Mar 30, 2026
e3c8ee5
fix: re-enable build log streaming with moby jsonmessage API
aausch Mar 30, 2026
d04e052
chore: mod tidy
thaJeztah Mar 31, 2026
602e37f
fix(ollama): restore stdcopy multiplexing for local process
aausch Mar 30, 2026
665be6a
chore: resolve FIXME comment on MappedPort empty string handling
aausch Mar 30, 2026
d0d757b
fix: restore correct container state in HTTP wait strategy tests
aausch Mar 30, 2026
d46e19d
fix: use network.List.Items in network test assertion
aausch Mar 30, 2026
b411d20
fix: make MergeCustomLabels validate all keys before merging
aausch Mar 30, 2026
815f415
fix(nebulagraph): retry network removal on active endpoints race
aausch Mar 30, 2026
92cbae3
fix(ollama): correct local process Inspect and test type mismatches
aausch Mar 30, 2026
2518902
fix: skip host network tests on colima
aausch Mar 30, 2026
43541d0
fix(test): use correct port constant in nginx PortEndpoint calls
mdelapenya Apr 1, 2026
f2271dd
fix(test): return non-empty ContainerListResult in mock
mdelapenya Apr 1, 2026
c6edac8
fix(test): update TestPreCreateModifierHook expectation for preserved…
mdelapenya Apr 1, 2026
f599eab
fix(test): use .Items for ContainerListResult len and uint16 for port
mdelapenya Apr 1, 2026
253bb5d
fix: protect isRunning with atomic.Bool to prevent data race
mdelapenya Apr 1, 2026
0008ace
chore(chroma): bump go toolchain
mdelapenya Apr 1, 2026
1eb8b1e
chore: update moby/client, moby/api to master
thaJeztah Apr 1, 2026
35ee1eb
chore: use jsonmessage.DisplayStream utility
thaJeztah Apr 1, 2026
bb76dfe
chore: remove redundant assert
thaJeztah Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ formatters:
- standard
- default
- prefix(github.com/testcontainers)

linters:
enable:
- errorlint
Expand Down Expand Up @@ -115,4 +116,5 @@ output:
path: stdout
run:
relative-path-mode: gitroot

version: "2"
8 changes: 5 additions & 3 deletions cleanup.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"reflect"
"time"

"github.com/moby/moby/client"
)

// TerminateOptions is a type that holds the options for terminating a container.
Expand Down Expand Up @@ -48,15 +50,15 @@ func (o *TerminateOptions) Cleanup() error {
if len(o.volumes) == 0 {
return nil
}
client, err := NewDockerClientWithOpts(o.ctx)
apiClient, err := NewDockerClientWithOpts(o.ctx)
if err != nil {
return fmt.Errorf("docker client: %w", err)
}
defer client.Close()
defer apiClient.Close()
// Best effort to remove all volumes.
var errs []error
for _, volume := range o.volumes {
if errRemove := client.VolumeRemove(o.ctx, volume, true); errRemove != nil {
if _, errRemove := apiClient.VolumeRemove(o.ctx, volume, client.VolumeRemoveOptions{Force: true}); errRemove != nil {
errs = append(errs, fmt.Errorf("volume remove %q: %w", volume, errRemove))
}
}
Expand Down
6 changes: 5 additions & 1 deletion commons-test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ $(GOBIN)/gotestsum:
$(GOBIN)/mockery:
$(call go_install,github.com/vektra/mockery/v2@v2.53.4)

$(GOBIN)/gci:
$(call go_install,github.com/daixiang0/gci@v0.13.5)

.PHONY: install
install: $(GOBIN)/golangci-lint $(GOBIN)/gotestsum $(GOBIN)/mockery

Expand All @@ -30,9 +33,10 @@ dependencies-scan:

.PHONY: lint
lint: $(GOBIN)/golangci-lint
golangci-lint run --verbose -c $(ROOT_DIR)/.golangci.yml --fix
golangci-lint run -c $(ROOT_DIR)/.golangci.yml --fix

.PHONY: generate
generate: $(GOBIN)/gci
generate: $(GOBIN)/mockery
go generate ./...

Expand Down
67 changes: 33 additions & 34 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import (
"time"

"github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/go-connections/nat"
"github.com/google/uuid"
"github.com/moby/go-archive"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/registry"
"github.com/moby/moby/client"
"github.com/moby/patternmatcher/ignorefile"

tcexec "github.com/testcontainers/testcontainers-go/exec"
Expand All @@ -34,23 +33,23 @@ import (
type DeprecatedContainer interface {
GetHostEndpoint(ctx context.Context, port string) (string, string, error)
GetIPAddress(ctx context.Context) (string, error)
LivenessCheckPorts(ctx context.Context) (nat.PortSet, error)
LivenessCheckPorts(ctx context.Context) (network.PortSet, error)
Terminate(ctx context.Context) error
}

// Container allows getting info about and controlling a single container instance
type Container interface {
GetContainerID() string // get the container id from the provider
Endpoint(context.Context, string) (string, error) // get proto://ip:port string for the lowest exposed port
PortEndpoint(ctx context.Context, port nat.Port, proto string) (string, error) // get proto://ip:port string for the given exposed port
Host(context.Context) (string, error) // get host where the container port is exposed
Inspect(context.Context) (*container.InspectResponse, error) // get container info
MappedPort(context.Context, nat.Port) (nat.Port, error) // get externally mapped port for a container port
Ports(context.Context) (nat.PortMap, error) // Deprecated: Use c.Inspect(ctx).NetworkSettings.Ports instead
SessionID() string // get session id
IsRunning() bool // IsRunning returns true if the container is running, false otherwise.
Start(context.Context) error // start the container
Stop(context.Context, *time.Duration) error // stop the container
GetContainerID() string // get the container id from the provider
Endpoint(context.Context, string) (string, error) // get proto://ip:port string for the lowest exposed port
PortEndpoint(ctx context.Context, port string, proto string) (string, error) // get proto://ip:port string for the given exposed port
Host(context.Context) (string, error) // get host where the container port is exposed
Inspect(context.Context) (*container.InspectResponse, error) // get container info
MappedPort(context.Context, string) (network.Port, error) // get externally mapped port for a container port
Ports(context.Context) (network.PortMap, error) // Deprecated: Use c.Inspect(ctx).NetworkSettings.Ports instead
SessionID() string // get session id
IsRunning() bool // IsRunning returns true if the container is running, false otherwise.
Start(context.Context) error // start the container
Stop(context.Context, *time.Duration) error // stop the container

// Terminate stops and removes the container and its image if it was built and not flagged as kept.
Terminate(ctx context.Context, opts ...TerminateOption) error
Expand All @@ -75,15 +74,15 @@ type Container interface {

// ImageBuildInfo defines what is needed to build an image
type ImageBuildInfo interface {
BuildOptions() (build.ImageBuildOptions, error) // converts the ImageBuildInfo to a build.ImageBuildOptions
GetContext() (io.Reader, error) // the path to the build context
GetDockerfile() string // the relative path to the Dockerfile, including the file itself
GetRepo() string // get repo label for image
GetTag() string // get tag label for image
BuildLogWriter() io.Writer // for output of build log, use io.Discard to disable the output
ShouldBuildImage() bool // return true if the image needs to be built
GetBuildArgs() map[string]*string // return the environment args used to build the Dockerfile
GetAuthConfigs() map[string]registry.AuthConfig // Deprecated. Testcontainers will detect registry credentials automatically. Return the auth configs to be able to pull from an authenticated docker registry
BuildOptions() (client.ImageBuildOptions, error) // converts the ImageBuildInfo to a build.ImageBuildOptions
GetContext() (io.Reader, error) // the path to the build context
GetDockerfile() string // the relative path to the Dockerfile, including the file itself
GetRepo() string // get repo label for image
GetTag() string // get tag label for image
BuildLogWriter() io.Writer // for output of build log, use io.Discard to disable the output
ShouldBuildImage() bool // return true if the image needs to be built
GetBuildArgs() map[string]*string // return the environment args used to build the Dockerfile
GetAuthConfigs() map[string]registry.AuthConfig // Deprecated. Testcontainers will detect registry credentials automatically. Return the auth configs to be able to pull from an authenticated docker registry
}

// FromDockerfile represents the parameters needed to build an image from a Dockerfile
Expand All @@ -105,7 +104,7 @@ type FromDockerfile struct {
// BuildOptionsModifier Modifier for the build options before image build. Use it for
// advanced configurations while building the image. Please consider that the modifier
// is called after the default build options are set.
BuildOptionsModifier func(*build.ImageBuildOptions)
BuildOptionsModifier func(*client.ImageBuildOptions)
}

type ContainerFile struct {
Expand Down Expand Up @@ -435,8 +434,8 @@ func (c *ContainerRequest) BuildLogWriter() io.Writer {
// BuildOptions returns the image build options when building a Docker image from a Dockerfile.
// It will apply some defaults and finally call the BuildOptionsModifier from the FromDockerfile struct,
// if set.
func (c *ContainerRequest) BuildOptions() (build.ImageBuildOptions, error) {
buildOptions := build.ImageBuildOptions{
func (c *ContainerRequest) BuildOptions() (client.ImageBuildOptions, error) {
buildOptions := client.ImageBuildOptions{
Remove: true,
ForceRemove: true,
}
Expand All @@ -452,7 +451,7 @@ func (c *ContainerRequest) BuildOptions() (build.ImageBuildOptions, error) {
// Make sure the auth configs from the Dockerfile are set right after the user-defined build options.
authsFromDockerfile, err := getAuthConfigsFromDockerfile(c)
if err != nil {
return build.ImageBuildOptions{}, fmt.Errorf("auth configs from Dockerfile: %w", err)
return client.ImageBuildOptions{}, fmt.Errorf("auth configs from Dockerfile: %w", err)
}

if buildOptions.AuthConfigs == nil {
Expand All @@ -468,7 +467,7 @@ func (c *ContainerRequest) BuildOptions() (build.ImageBuildOptions, error) {
for _, is := range c.ImageSubstitutors {
modifiedTag, err := is.Substitute(tag)
if err != nil {
return build.ImageBuildOptions{}, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
return client.ImageBuildOptions{}, fmt.Errorf("failed to substitute image %s with %s: %w", tag, is.Description(), err)
}

if modifiedTag != tag {
Expand All @@ -487,18 +486,18 @@ func (c *ContainerRequest) BuildOptions() (build.ImageBuildOptions, error) {
if !c.ShouldKeepBuiltImage() {
dst := GenericLabels()
if err = core.MergeCustomLabels(dst, c.Labels); err != nil {
return build.ImageBuildOptions{}, err
return client.ImageBuildOptions{}, err
}
if err = core.MergeCustomLabels(dst, buildOptions.Labels); err != nil {
return build.ImageBuildOptions{}, err
return client.ImageBuildOptions{}, err
}
buildOptions.Labels = dst
}

// Do this as late as possible to ensure we don't leak the context on error/panic.
buildContext, err := c.GetContext()
if err != nil {
return build.ImageBuildOptions{}, err
return client.ImageBuildOptions{}, err
}

buildOptions.Context = buildContext
Expand Down
12 changes: 6 additions & 6 deletions container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"testing"
"time"

"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/stretchr/testify/require"

"github.com/testcontainers/testcontainers-go"
Expand Down Expand Up @@ -342,7 +342,7 @@ func TestCustomLabelsBuildOptionsModifier(t *testing.T) {
testcontainers.WithDockerfile(testcontainers.FromDockerfile{
Context: "./testdata",
Dockerfile: "Dockerfile",
BuildOptionsModifier: func(opts *build.ImageBuildOptions) {
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
opts.Labels = map[string]string{
myBuildOptionLabel: myBuildOptionValue,
}
Expand Down Expand Up @@ -377,8 +377,8 @@ func Test_GetLogsFromFailedContainer(t *testing.T) {
b, err := io.ReadAll(logs)
require.NoError(t, err)

log := string(b)
require.Contains(t, log, "I was not expecting this")
out := string(b)
require.Contains(t, out, "I was not expecting this")
}

// dockerImageSubstitutor {
Expand Down Expand Up @@ -497,7 +497,7 @@ func TestShouldStartContainersInParallel(t *testing.T) {
// }
require.NoError(t, err)

t.Logf("Parallel container [iteration_%d] listening on %d\n", i, port.Int())
t.Logf("Parallel container [iteration_%d] listening on %d\n", i, port.Num())
})
}
}
Expand Down
Loading
Loading