testcontainers-go icon indicating copy to clipboard operation
testcontainers-go copied to clipboard

[Enhancement]: Reuse docker-compose stack started outside of Testcontainers

Open sooncj opened this issue 4 months ago • 4 comments

User Story

As a test developer with a docker-compose stack that take a while to start up, I want to be able to start my containers prior to running my integration tests and reuse them for many test runs, so that I can iterate faster because I don't need to wait for each test start and stabilize its containers.

There is a workaround by using ReuseByName on individual containers instead of docker-compose. But, the ergonomics aren't as good. The docker-compose configuration needs to be decomposed into individual containers in the test. It would be better to use docker-compose on the CLI and in the tests.

Testcontainers version

0.37.0

Using the latest Testcontainers version?

Yes

Host OS

MacOS

Host arch

ARM

Go version

1.23.8

Docker version

Client: Docker Engine - Community
 Version:           28.1.1
 API version:       1.47 (downgraded from 1.49)
 Go version:        go1.24.2
 Git commit:        4eba377327
 Built:             Fri Apr 18 09:44:47 2025
 OS/Arch:           darwin/arm64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          27.4.0
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.22.10
  Git commit:       92a8393
  Built:            Sat Dec  7 10:39:01 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.7.24
  GitCommit:        88bf19b2105c8b17560993bee28a01ddc2f97182
 runc:
  Version:          1.2.2
  GitCommit:        v1.2.2-0-g7cb3632
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker info

Client: Docker Engine - Community
 Version:    28.1.1
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.23.0
    Path:     /Users/csoon/.docker/cli-plugins/docker-buildx

Server:
 Containers: 28
  Running: 13
  Paused: 0
  Stopped: 15
 Images: 30
 Server Version: 27.4.0
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 88bf19b2105c8b17560993bee28a01ddc2f97182
 runc version: v1.2.2-0-g7cb3632
 init version: de40ad0
 Security Options:
  apparmor
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.8.0-50-generic
 Operating System: Ubuntu 24.04.1 LTS
 OSType: linux
 Architecture: aarch64
 CPUs: 4
 Total Memory: 11.66GiB
 Name: colima
 ID: 4425450c-be0d-4954-bb81-88622ce0ce16
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

What happened?

I expect testcontainers-go docker-compose module to be able to reuse a docker-compose stack when specifying RecreateNever/RecreateDiverged and StackIdentifier.

Run in isolation without a running docker-compose stack the test passes.

This scenario does not work:

docker-compose up -d
go test

For a running stack (started by docker-compose or a previous go test), the docker-compose module:

  1. uses the StackIdentifer
  2. attempts to restart services
  3. deletes original docker network and creates a new one
  4. fails because the services are still pointing at the original network

Note: I am using colima 0.8.1 to run docker.

Relevant log output

% docker-compose up -d
[+] Running 2/2
 ✔ Network nginx-compose_default    Created                                                                                      0.1s 
 ✔ Container nginx-compose-nginx-1  Started                                                                                      0.1s 

% docker ps -a | grep nginx-compose-nginx-1
de37e713809d   nginx                                                 "/docker-entrypoint.…"   About a minute ago   Up About a minute          0.0.0.0:8080->80/tcp, [::]:8080->80/tcp                                nginx-compose-nginx-1

% docker network ls | grep nginx-compose_default 
718ed86c15ab   nginx-compose_default                          bridge    local

% go test
[+] Running 1/2
 ⠙ Container nginx-compose-nginx-1  Starting                                                                                     0.3s 
 ✔ Network nginx-compose_default    Created                                                                                      0.1s 
--- FAIL: TestDockerCompose (0.38s)
    main_test.go:33: Failed to start stack: compose up: Error response from daemon: network 718ed86c15ab3328ccd260d6f8f529ed7e74c70f85cb602768dd056a9330cd20 not found
FAIL
exit status 1
FAIL	example.com/testcontainers-go/compose	0.919s

% docker ps -a | grep nginx-compose-nginx-1      
de37e713809d   nginx                                                 "/docker-entrypoint.…"   2 minutes ago   Exited (128) 13 seconds ago                                                                          nginx-compose-nginx-1

% docker network ls | grep nginx-compose_default
c357dc5d5f97   nginx-compose_default                          bridge    local

Additional information

go test

func TestDockerCompose(t *testing.T) {
	ctx := context.Background()

	dockerCompose, err := compose.NewDockerComposeWith(
		compose.WithStackFiles("docker-compose.yml"),
		compose.StackIdentifier("nginx-compose"),
	)
	if err != nil {
		t.Logf("Failed to create stack: %v", err)
		t.FailNow()
	}

	err = dockerCompose.
		WaitForService("nginx", wait.NewHTTPStrategy("/").
			WithPort(nat.Port("80/tcp")).
			WithStartupTimeout(20*time.Second),
		).
		Up(ctx, compose.Wait(true), compose.WithRecreate(api.RecreateNever))
	if err != nil {
		t.Logf("Failed to start stack: %v", err)
		t.FailNow()
	}

	// no cleanup
}
name: nginx-compose
services:
  nginx:
    image: nginx
    ports:
    - 8080:80

sooncj avatar Jun 02 '25 18:06 sooncj