Class: Testcontainers::ComposeContainer

Inherits:
Object
  • Object
show all
Defined in:
lib/testcontainers/compose.rb

Overview

ComposeContainer class is used to manage a large number of containers in a synchronous environment

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command: ["docker compose"], filepath: ".", compose_filenames: ["docker-compose.yml"], pull: false, build: false, env_file: nil, services: nil) ⇒ ComposeContainer

Initializes a new instance of ComposeContainer

Parameters:

  • image (String)

    the image to use

  • filepath (String) (defaults to: ".")

    the filepath of the configuration files for the configuration of docker compose

  • compose_filename (String, List)

    the names of the files with yml extencion for custom configuration

  • pull (Boolean) (defaults to: false)

    is the option for decide if there should be a pull request to generate the image for the containers

  • build (Boolean) (defaults to: false)

    is the option for decide if there have to use a build command for the images used for the containers

  • env_file (String) (defaults to: nil)

    is the name of the envieroment configuration

  • services (List) (defaults to: nil)

    are the names of the services that gonna use in the images of the containers



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/testcontainers/compose.rb', line 27

def initialize(command: ["docker compose"], filepath: ".", compose_filenames: ["docker-compose.yml"],
  pull: false, build: false, env_file: nil, services: nil)

  @command = command
  @filepath = filepath
  @compose_filenames = compose_filenames
  @pull = pull
  @build = build
  @services = services
  @env_file = env_file
  @_container_started = false
end

Instance Attribute Details

#buildObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def build
  @build
end

#compose_filenamesObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def compose_filenames
  @compose_filenames
end

#env_fileObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def env_file
  @env_file
end

#filepathObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def filepath
  @filepath
end

#pullObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def pull
  @pull
end

#servicesObject

Default image used by the container



16
17
18
# File 'lib/testcontainers/compose.rb', line 16

def services
  @services
end

Instance Method Details

#exec(service_name:, command:) ⇒ Object

Execute a command in the given service using the ‘docker-compose exec` command

Parameters:

  • service_name (String)
  • command (String)

Raises:

  • (ContainerNotStartedError)


97
98
99
100
101
102
# File 'lib/testcontainers/compose.rb', line 97

def exec(service_name:, command:)
  raise ContainerNotStartedError unless @_container_started

  exec_cmd = compose_command(["exec", "-T", service_name, command])
  exec_command(exec_cmd)
end

#exited?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/testcontainers/compose.rb', line 78

def exited?
  !running?
end

#logsString

Return the logs of the containers using the ‘docker-compose logs` command

Returns:

  • (String)

    logs

Raises:

  • (ContainerNotStartedError)


85
86
87
88
89
90
91
# File 'lib/testcontainers/compose.rb', line 85

def logs
  raise ContainerNotStartedError unless @_container_started

  logs_command = compose_command("logs")
  stdout, _, _ = exec_command(logs_command)
  stdout
end

#running?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/testcontainers/compose.rb', line 74

def running?
  @_container_started
end

#service_host(service: nil, port: 0) ⇒ Object

Return the host for a given service and port using the ‘docker-compose port` command

Parameters:

  • service (String) (defaults to: nil)

Raises:

  • (ContainerNotStartedError)


118
119
120
121
122
# File 'lib/testcontainers/compose.rb', line 118

def service_host(service: nil, port: 0)
  raise ContainerNotStartedError unless @_container_started

  _service_info(service: service, port: port)["host"]
end

#service_port(service: nil, port: 0) ⇒ Object

Return the mapped port for a given service and port using the ‘docker-compose port` command

Parameters:

  • service (String) (defaults to: nil)

Raises:

  • (ContainerNotStartedError)


108
109
110
111
112
# File 'lib/testcontainers/compose.rb', line 108

def service_port(service: nil, port: 0)
  raise ContainerNotStartedError unless @_container_started

  _service_info(service: service, port: port)["port"]
end

#startComposeContainer

Run the containers using the ‘docker-compose up` command

Returns:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/testcontainers/compose.rb', line 43

def start
  return self if @_container_started

  if @pull
    pull_cmd = compose_command("pull")
    exec_command(pull_cmd)
  end

  args = ["up", "-d"]
  args << "--build" if @build
  args << @services.join(" ") if @services
  up_cmd = compose_command(args)
  _, _, status = exec_command(up_cmd)
  @_container_started = true if status.success?

  self
end

#stopComposeContainer

Stop the containers using the ‘docker-compose down` command

Returns:

Raises:

  • (ContainerNotStartedError)


64
65
66
67
68
69
70
71
72
# File 'lib/testcontainers/compose.rb', line 64

def stop
  raise ContainerNotStartedError unless @_container_started

  down_cmd = compose_command("down -v")
  _, _, status = exec_command(down_cmd)
  @_container_started = false if status.success?

  self
end

#wait_for_http(url:, timeout: 60, interval: 0.1, status: 200) ⇒ Boolean

Waits for the container to respond to HTTP requests.

Parameters:

  • service (String)

    The name of the service.

  • timeout (Integer) (defaults to: 60)

    The number of seconds to wait for the TCP connection to be established.

  • interval (Float) (defaults to: 0.1)

    The number of seconds to wait between checks.

  • path (String)

    The path to request.

  • container_port (Integer)

    The container port to request.

  • https (Boolean)

    Whether to use TLS.

  • status (Integer) (defaults to: 200)

    The expected HTTP status code.

Returns:

  • (Boolean)

    Whether the container is responding to HTTP requests.

Raises:

  • (ContainerNotStartedError)

    If the container has not been started.

  • (TimeoutError)

    If the timeout is reached.

  • (PortNotMappedError)

    If the port is not mapped.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/testcontainers/compose.rb', line 187

def wait_for_http(url:, timeout: 60, interval: 0.1, status: 200)
  raise ContainerNotStartedError unless @_container_started

  Timeout.timeout(timeout) do
    loop do
      begin
        response = Excon.get(url)
        return true if response.status == status
      rescue Excon::Error::Socket
        # The container may not be ready to accept connections yet
      end

      sleep interval
    end
  end
rescue Timeout::Error
  raise TimeoutError, "Timed out waiting for HTTP status #{status} on #{path}"
end

#wait_for_logs(matcher:, timeout: 60, interval: 0.1) ⇒ Boolean

Waits for the container logs to match the given regex.

Parameters:

  • matcher (Regexp)

    The regex to match.

  • timeout (Integer) (defaults to: 60)

    The number of seconds to wait for the logs to match.

  • interval (Float) (defaults to: 0.1)

    The number of seconds to wait between checks.

Returns:

  • (Boolean)

    Whether the logs matched the regex.

Raises:

  • (ContainerNotStartedError)

    If the container has not been started.

  • (TimeoutError)

    If the timeout is reached.

  • (ConnectionError)

    If the connection to the Docker daemon fails.



133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/testcontainers/compose.rb', line 133

def wait_for_logs(matcher:, timeout: 60, interval: 0.1)
  raise ContainerNotStartedError unless @_container_started

  Timeout.timeout(timeout) do
    loop do
      return true if logs&.match?(matcher)

      sleep interval
    end
  end
rescue Timeout::Error
  raise TimeoutError, "Timed out waiting for logs to match #{matcher}"
end

#wait_for_tcp_port(host:, port:, timeout: 60, interval: 0.1) ⇒ Boolean

Waits for the container to open the given port.

Parameters:

  • port (Integer)

    The port to wait for.

  • timeout (Integer) (defaults to: 60)

    The number of seconds to wait for the port to open.

  • interval (Float) (defaults to: 0.1)

    The number of seconds to wait between checks.

Returns:

  • (Boolean)

    Whether the port is open.

Raises:

  • (ContainerNotStartedError)

    If the container has not been started.

  • (TimeoutError)

    If the timeout is reached.

  • (ConnectionError)

    If the connection to the Docker daemon fails.

  • (PortNotMappedError)

    If the port is not mapped.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/testcontainers/compose.rb', line 157

def wait_for_tcp_port(host:, port:, timeout: 60, interval: 0.1)
  raise ContainerNotStartedError unless @_container_started

  Timeout.timeout(timeout) do
    loop do
      Timeout.timeout(interval) do
        TCPSocket.new(host, port).close
        return true
      end
    rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Timeout::Error
      sleep interval
    end
  end
rescue Timeout::Error
  raise TimeoutError, "Timed out waiting for port #{port} to open"
end