Class: Testcontainers::DockerContainer

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

Overview

The DockerContainer class is used to manage Docker containers. It provides an interface to create, start, stop, and manipulate containers using the Docker API.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(image, name: nil, command: nil, entrypoint: nil, exposed_ports: nil, image_create_options: {}, port_bindings: nil, volumes: nil, filesystem_binds: nil, env: nil, labels: nil, working_dir: nil, healthcheck: nil, wait_for: nil, logger: Testcontainers.logger) ⇒ DockerContainer

Initializes a new DockerContainer instance.

Parameters:

  • image (String)

    the container’s image name

  • command (Array<String>, nil) (defaults to: nil)

    the command to run in the container

  • name (String, nil) (defaults to: nil)

    the container’s name

  • exposed_ports (Hash, Array<String>, nil) (defaults to: nil)

    a hash or an array of exposed container ports

  • image_create_options (Hash) (defaults to: {})

    a hash of options to pass to Docker::Image.create.

  • port_bindings (Hash, Array<String>, nil) (defaults to: nil)

    a hash or an array of container ports to host port bindings

  • volumes (Hash, Array<String>, nil) (defaults to: nil)

    a hash or an array of volume paths in the container

  • filesystem_binds (Array<String>, Hash, nil) (defaults to: nil)

    an array of strings or a hash representing bind mounts from the host to the container

  • env (Array<String>, Hash, nil) (defaults to: nil)

    an array or a hash of environment variables for the container in the format KEY=VALUE

  • labels (Hash, nil) (defaults to: nil)

    a hash of labels to be applied to the container

  • working_dir (String, nil) (defaults to: nil)

    the working directory for the container

  • logger (Logger) (defaults to: Testcontainers.logger)

    a logger instance for the container



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/testcontainers/docker_container.rb', line 41

def initialize(image, name: nil, command: nil, entrypoint: nil, exposed_ports: nil, image_create_options: {}, port_bindings: nil, volumes: nil, filesystem_binds: nil,
  env: nil, labels: nil, working_dir: nil, healthcheck: nil, wait_for: nil, logger: Testcontainers.logger)

  @image = image
  @name = name
  @command = command
  @entrypoint = entrypoint
  @exposed_ports = add_exposed_ports(exposed_ports) if exposed_ports
  @image_create_options = image_create_options
  @port_bindings = add_fixed_exposed_ports(port_bindings) if port_bindings
  @volumes = add_volumes(volumes) if volumes
  @env = add_env(env) if env
  @filesystem_binds = add_filesystem_binds(filesystem_binds) if filesystem_binds
  @labels = add_labels(labels) if labels
  @working_dir = working_dir
  @healthcheck = add_healthcheck(healthcheck) if healthcheck
  @wait_for = add_wait_for(wait_for)
  @logger = logger
  @_container = nil
  @_id = nil
  @_created_at = nil
end

Instance Attribute Details

#_containerDocker::Container? (readonly)

the underlying Docker::Container object

Returns:

  • (Docker::Container, nil)

    the current value of _container



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def _container
  @_container
end

#_idString? (readonly)

the container’s ID

Returns:

  • (String, nil)

    the current value of _id



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def _id
  @_id
end

#commandArray<String>?

the command to run in the container

Returns:

  • (Array<String>, nil)

    the current value of command



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def command
  @command
end

#entrypointArray<String>?

the entrypoint to run in the container

Returns:

  • (Array<String>, nil)

    the current value of entrypoint



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def entrypoint
  @entrypoint
end

#envArray<String>?

an array of environment variables for the container in the format KEY=VALUE

Returns:

  • (Array<String>, nil)

    the current value of env



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def env
  @env
end

#exposed_portsHash?

a hash mapping exposed container ports to an empty hash (used for Docker API compatibility)

Returns:

  • (Hash, nil)

    the current value of exposed_ports



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def exposed_ports
  @exposed_ports
end

#filesystem_bindsArray<String>?

an array of strings representing bind mounts from the host to the container

Returns:

  • (Array<String>, nil)

    the current value of filesystem_binds



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def filesystem_binds
  @filesystem_binds
end

#healthcheckHash?

a hash of healthcheck options for the container

Returns:

  • (Hash, nil)

    the current value of healthcheck



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def healthcheck
  @healthcheck
end

#imageString

the container’s image name

Returns:

  • (String)

    the current value of image



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def image
  @image
end

#labelsHash?

a hash of labels to be applied to the container

Returns:

  • (Hash, nil)

    the current value of labels



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def labels
  @labels
end

#loggerLogger

a logger instance for the container

Returns:

  • (Logger)

    the current value of logger



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def logger
  @logger
end

#nameString

the container’s name

Returns:

  • (String)

    the current value of name



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def name
  @name
end

#port_bindingsHash?

a hash mapping container ports to host port bindings (used for Docker API compatibility)

Returns:

  • (Hash, nil)

    the current value of port_bindings



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def port_bindings
  @port_bindings
end

#volumesHash?

a hash mapping volume paths in the container to an empty hash (used for Docker API compatibility)

Returns:

  • (Hash, nil)

    the current value of volumes



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def volumes
  @volumes
end

#wait_forObject

Returns the value of attribute wait_for.



22
23
24
# File 'lib/testcontainers/docker_container.rb', line 22

def wait_for
  @wait_for
end

#working_dirString?

the working directory for the container

Returns:

  • (String, nil)

    the current value of working_dir



21
22
23
# File 'lib/testcontainers/docker_container.rb', line 21

def working_dir
  @working_dir
end

Instance Method Details

#add_env(env_or_key, value = nil) ⇒ Array<String>

Add environment variables to the container configuration.

Parameters:

  • env_or_key (String, Hash, Array)

    The environment variable(s) to add.

    • When passing a Hash, the keys and values represent the variable names and values.

    • When passing an Array, each element should be a String in the format “KEY=VALUE”.

    • When passing a String, it should be in the format “KEY=VALUE” or a key when a value is also provided.

  • value (String, nil) (defaults to: nil)

    The value for the environment variable if env_or_key is a key (String).

Returns:

  • (Array<String>)

    The updated list of environment variables in the format “KEY=VALUE”.



72
73
74
75
76
77
# File 'lib/testcontainers/docker_container.rb', line 72

def add_env(env_or_key, value = nil)
  @env ||= []
  new_env = process_env_input(env_or_key, value)
  @env.concat(new_env)
  @env
end

#add_exposed_port(port) ⇒ Hash

Add an exposed port to the container configuration.

Parameters:

  • port (String, Integer)

    The port to expose in the format “port/protocol” or as an integer.

Returns:

  • (Hash)

    The updated list of exposed ports.



83
84
85
86
87
88
89
90
# File 'lib/testcontainers/docker_container.rb', line 83

def add_exposed_port(port)
  port = normalize_port(port)
  @exposed_ports ||= {}
  @port_bindings ||= {}
  @exposed_ports[port] ||= {}
  @port_bindings[port] ||= [{"HostPort" => ""}]
  @exposed_ports
end

#add_exposed_ports(*ports) ⇒ Hash

Add multiple exposed ports to the container configuration.

Parameters:

  • ports (Array<String, Integer>)

    The list of ports to expose.

Returns:

  • (Hash)

    The updated list of exposed ports



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

def add_exposed_ports(*ports)
  ports = ports.first if ports.first.is_a?(Array)

  ports.each do |port|
    add_exposed_port(port)
  end
  @exposed_ports
end

#add_filesystem_bind(host_or_hash, container_path = nil, mode = "rw") ⇒ Array<String>

Add a filesystem bind to the container configuration.

Parameters:

  • host_or_hash (String, Hash)

    The host path or a Hash with a single key-value pair representing the host and container paths.

  • container_path (String, nil) (defaults to: nil)

    The container path if host_or_hash is a String.

  • mode (String) (defaults to: "rw")

    The access mode for the bind (“rw” for read-write, “ro” for read-only). Default is “rw”.

Returns:

  • (Array<String>)

    The updated list of filesystem binds in the format “host_path:container_path:mode”.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/testcontainers/docker_container.rb', line 162

def add_filesystem_bind(host_or_hash, container_path = nil, mode = "rw")
  @filesystem_binds ||= []

  if host_or_hash.is_a?(Hash)
    host_path, container_path = host_or_hash.first
  elsif host_or_hash.is_a?(String)
    if container_path
      host_path = host_or_hash
    else
      host_path, container_path, mode = host_or_hash.split(":")
      mode ||= "rw"
    end
  else
    raise ArgumentError, "Invalid input format for add_filesystem_bind"
  end

  @filesystem_binds << "#{host_path}:#{container_path}:#{mode}"
  add_volume(container_path)
  @filesystem_binds
end

#add_filesystem_binds(filesystem_binds) ⇒ Array<String>

Add multiple filesystem binds to the container configuration.

Parameters:

  • filesystem_binds (Array<String>, Array<Array<String>>, Hash)

    The list of filesystem binds.

Returns:

  • (Array<String>)

    The updated list of filesystem binds in the format “host_path:container_path:mode”.



187
188
189
190
191
192
193
194
# File 'lib/testcontainers/docker_container.rb', line 187

def add_filesystem_binds(filesystem_binds)
  @filesystem_binds ||= []
  binds = normalize_filesystem_binds(filesystem_binds)
  binds.each do |bind|
    add_filesystem_bind(*bind)
  end
  @filesystem_binds
end

#add_fixed_exposed_port(container_port, host_port = nil) ⇒ Hash

Add a fixed exposed port to the container configuration.

Parameters:

  • container_port (String, Integer, Hash)

    The container port in the format “port/protocol” or as an integer. When passing a Hash, it should contain a single key-value pair with the container port as the key and the host port as the value.

  • host_port (Integer, nil) (defaults to: nil)

    The host port to bind the container port to.

Returns:

  • (Hash)

    The updated list of port bindings.



111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/testcontainers/docker_container.rb', line 111

def add_fixed_exposed_port(container_port, host_port = nil)
  if container_port.is_a?(Hash)
    container_port, host_port = container_port.first
  end

  container_port = normalize_port(container_port)
  @exposed_ports ||= {}
  @port_bindings ||= {}
  @exposed_ports[container_port] = {}
  @port_bindings[container_port] = [{"HostPort" => host_port.to_s}]
  @port_bindings
end

#add_fixed_exposed_ports(port_mappings = {}) ⇒ Hash

Add multiple fixed exposed ports to the container configuration.

Parameters:

  • port_mappings (Hash) (defaults to: {})

    The list of container ports and host ports to bind them to.

Returns:

  • (Hash)

    The updated list of port bindings.



128
129
130
131
132
133
# File 'lib/testcontainers/docker_container.rb', line 128

def add_fixed_exposed_ports(port_mappings = {})
  port_mappings.each do |container_port, host_port|
    add_fixed_exposed_port(container_port, host_port)
  end
  @port_bindings
end

#add_healthcheck(options = {}) ⇒ Hash

Adds a healthcheck to the container.

Parameters:

  • options (Hash) (defaults to: {})

    the healthcheck options.

Options Hash (options):

  • :test (Array|String)

    the command to run to check the health of the container.

  • :interval (Float)

    the time in seconds between health checks. (default: 30)

  • :timeout (Float)

    the time in seconds to wait for a health check to complete. (default: 30)

  • :retries (Integer)

    the number of times to retry a failed health check before giving up. (default: 3)

  • :shell (Boolean)

    whether or not to run the health check in a shell. (default: false)

Returns:

  • (Hash)

    the healthcheck options for Docker.



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/testcontainers/docker_container.rb', line 226

def add_healthcheck(options = {})
  test = options[:test]

  if test.nil?
    @healthcheck = {"Test" => ["NONE"]}
    return @healthcheck
  end

  interval = options[:interval]&.to_f || 30.0
  timeout = options[:timeout]&.to_f || 30.0
  retries = options[:retries]&.to_i || 3
  shell = options[:shell] || false

  test = test.split(" ") if test.is_a?(String)
  test = shell ? test.unshift("CMD-SHELL") : test.unshift("CMD")

  @healthcheck = {
    "Test" => test,
    "Interval" => (interval * 1_000_000_000).to_i,
    "Timeout" => (timeout * 1_000_000_000).to_i,
    "Retries" => retries,
    "StartPeriod" => 0
  }
end

#add_label(label, value) ⇒ Hash

Add a label to the container configuration.

Parameters:

  • label (String)

    The label to add.

  • value (String)

    The value of the label.

Returns:

  • (Hash)

    The updated list of labels.



201
202
203
204
205
# File 'lib/testcontainers/docker_container.rb', line 201

def add_label(label, value)
  @labels ||= {}
  @labels[label] = value
  @labels
end

#add_labels(labels) ⇒ Hash

Add multiple labels to the container configuration.

Parameters:

  • labels (Hash)

    The labels to add.

Returns:

  • (Hash)

    The updated list of labels.



211
212
213
214
215
# File 'lib/testcontainers/docker_container.rb', line 211

def add_labels(labels)
  @labels ||= {}
  @labels.merge!(labels)
  @labels
end

#add_volume(volume) ⇒ Hash

Add a volume to the container configuration.

Parameters:

  • volume (String)

    The volume to add.

Returns:

  • (Hash)

    The updated list of volumes.



139
140
141
142
143
# File 'lib/testcontainers/docker_container.rb', line 139

def add_volume(volume)
  @volumes ||= {}
  @volumes[volume] = {}
  @volumes
end

#add_volumes(volumes = []) ⇒ Hash

Add multiple volumes to the container configuration.

Parameters:

  • volumes (Array<String>) (defaults to: [])

    The list of volumes to add.

Returns:

  • (Hash)

    The updated list of volumes.



149
150
151
152
153
154
# File 'lib/testcontainers/docker_container.rb', line 149

def add_volumes(volumes = [])
  volumes = normalize_volumes(volumes)
  @volumes ||= {}
  @volumes.merge!(volumes)
  @volumes
end

#add_wait_for(method = nil, *args, **kwargs, &block) ⇒ Proc

Add a wait_for strategy to the container configuration.

Parameters:

  • method (Symbol, String, Proc, Array) (defaults to: nil)

    The method to call on the container to wait for it to be ready.

  • args (Array)

    The arguments to pass to the method if it is a symbol or string.

  • kwargs (Hash)

    The keyword arguments to pass to the method if it is a symbol or string.

  • block (Proc)

    The block to call on the container to wait for it to be ready.

Returns:

  • (Proc)

    The wait_for strategy.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/testcontainers/docker_container.rb', line 258

def add_wait_for(method = nil, *args, **kwargs, &block)
  if method.nil?
    if block
      if block.arity == 1
        @wait_for = block
      else
        raise ArgumentError, "Invalid wait_for block: #{block}"
      end
    elsif @exposed_ports && !@exposed_ports.empty?
      port = @exposed_ports.keys.first.split("/").first
      @wait_for = ->(container) { container.wait_for_tcp_port(port) }
    end
  elsif method.is_a?(Proc)
    if method.arity == 1
      @wait_for = method
    else
      raise ArgumentError, "Invalid wait_for method: #{method}"
    end
  elsif method.is_a?(Array)
    method_name = "wait_for_#{method[0]}".to_sym
    args = method[1] || []
    kwargs = method[2] || {}
    if respond_to?(method_name)
      @wait_for = ->(container) { container.send(method_name, *args, **kwargs) }
    else
      raise ArgumentError, "Invalid wait_for method: #{method_name}"
    end
  else
    method_name = "wait_for_#{method}".to_sym
    if respond_to?(method_name)
      @wait_for = ->(container) { container.send(method_name, *args, **kwargs) }
    else
      raise ArgumentError, "Invalid wait_for method: #{method_name}"
    end
  end
  @wait_for
end

#copy_file_from_container(container_path, host_path_or_io) ⇒ String

Copies a file from the container to the host.

Parameters:

  • container_path (String)

    The path to the file inside the container.

  • host_path_or_io (String, IO)

    The path to the file on the host or a IO object.

Returns:

  • (String)

    The contents of the file inside the container.

Raises:



951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
# File 'lib/testcontainers/docker_container.rb', line 951

def copy_file_from_container(container_path, host_path_or_io)
  raise ContainerNotStartedError, "Container has not been started" unless running?
  raise ArgumentError, "Container path must be a non-empty string" if container_path.to_s.empty?

  begin
    io = host_path_or_io.is_a?(String) ? File.open(host_path_or_io, "w") : host_path_or_io
    io.rewind if io.pos != 0
    content = read_file(container_path)
    io.write(content)
    io.rewind
  rescue => e
    puts "Error while copying file from container: #{e.message}"
    raise e # Optionally re-raise the exception or handle it according to your needs
  ensure
    io.close if io.respond_to?(:close)
  end

  content
end

#copy_file_to_container(container_path, host_path_or_io) ⇒ self

Copies a IO object or a file from the host to the container.

Parameters:

  • container_path (String)

    The path to the file inside the container.

  • host_path_or_io (String, IO)

    The path to the file on the host or a IO object.

Returns:

  • (self)

Raises:



925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
# File 'lib/testcontainers/docker_container.rb', line 925

def copy_file_to_container(container_path, host_path_or_io)
  raise ContainerNotStartedError, "Container has not been started" unless running?
  raise ArgumentError, "Container path must be a non-empty string" if container_path.to_s.empty?

  begin
    io = host_path_or_io.is_a?(String) ? File.open(host_path_or_io) : host_path_or_io
    io.rewind if io.pos != 0
    store_file(container_path, io.read)
    io.rewind
  rescue => e
    puts "Error while copying file to container: #{e.message}"
    return false
  ensure
    io.close if io.respond_to?(:close)
  end

  true
end

#created_atString?

Returns the container’s created at timestamp. The timestamp is in UTC and formatted as ISO 8601. Example: “2014-10-31T23:22:05.430Z”.

Returns:

  • (String)

    The container’s created at timestamp.

  • (nil)

    If the container does not exist.



673
674
675
# File 'lib/testcontainers/docker_container.rb', line 673

def created_at
  @_created_at
end

#dead?Boolean

Returns whether the container is running.

Returns:

  • (Boolean)

    Whether the container is running.

Raises:



583
584
585
# File 'lib/testcontainers/docker_container.rb', line 583

def dead?
  status == "dead"
end

#exec(cmd, options = {}, &block) ⇒ Array, Integer

Executes a command in the container. See docs.docker.com/engine/api/v1.42/#operation/ContainerExec for all available options.

Parameters:

  • cmd (Array<String>)

    The command to execute.

  • options (Hash) (defaults to: {})

    Additional options to pass to the Docker Exec API. (e.g ‘Env`)

Options Hash (options):

  • :tty (Boolean)

    Allocate a pseudo-TTY.

  • :detach (Boolean) — default: false

    Whether to attach to STDOUT/STDERR.

  • :stdin (Object)

    Attach to stdin of the exec command.

  • :wait (Integer)

    The number of seconds to wait for the command to finish.

  • :user (String)

    The user to execute the command as.

Returns:

  • (Array, Array, Integer)

    The STDOUT, STDERR and exit code.



796
797
798
799
800
801
# File 'lib/testcontainers/docker_container.rb', line 796

def exec(cmd, options = {}, &block)
  raise ContainerNotStartedError unless @_container
  @_container.exec(cmd, options, &block)
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#exists?Boolean

Returns whether the container exists.

Returns:

  • (Boolean)

    Whether the container exists.

Raises:



657
658
659
660
661
662
663
664
665
666
# File 'lib/testcontainers/docker_container.rb', line 657

def exists?
  return false unless @_id

  Docker::Container.get(@_id)
  true
rescue Docker::Error::NotFoundError
  false
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#exited?Boolean

Returns whether the container is stopped.

Returns:

  • (Boolean)

    Whether the container is stopped.

Raises:



621
622
623
# File 'lib/testcontainers/docker_container.rb', line 621

def exited?
  status == "exited"
end

#first_mapped_portInteger

Returns the container’s first mapped port.

Returns:

  • (Integer)

    The container’s first mapped port.

Raises:



736
737
738
739
# File 'lib/testcontainers/docker_container.rb', line 736

def first_mapped_port
  raise ContainerNotStartedError unless @_container
  container_ports.map { |port| mapped_port(port) }.first
end

#get_env(key) ⇒ String?

Returns the value for the given environment variable.

Parameters:

  • key (String)

    The environment variable’s key.

Returns:

  • (String)

    The environment variable’s value.

  • (nil)

    If the environment variable does not exist.



764
765
766
767
# File 'lib/testcontainers/docker_container.rb', line 764

def get_env(key)
  env_entry = env.find { |entry| entry.start_with?("#{key}=") }
  env_entry&.split("=")&.last
end

#healthy?Boolean, false

Returns whether the container is healthy.

Returns:

  • (Boolean)

    Whether the container is healthy.

  • (false)

    If the container has not been started instead of raising an ContainerNotStartedError.

Raises:



631
632
633
634
635
636
637
638
639
640
641
# File 'lib/testcontainers/docker_container.rb', line 631

def healthy?
  if supports_healthcheck?
    @_container&.json&.dig("State", "Health", "Status") == "healthy"
  else
    raise HealthcheckNotSupportedError
  end
rescue ContainerNotStartedError
  false
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#hostString

Returns the container’s host.

Returns:

  • (String)

    The container’s host.

Raises:



696
697
698
699
700
701
702
703
704
705
706
707
708
709
# File 'lib/testcontainers/docker_container.rb', line 696

def host
  host = docker_host
  return "localhost" if host.nil?
  raise ContainerNotStartedError unless @_container

  if inside_container? && ENV["DOCKER_HOST"].nil?
    gateway_ip = container_gateway_ip
    return bridge_ip if gateway_ip == host
    return gateway_ip
  end
  host
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#infoHash?

Returns:

  • (Hash)

    The container’s info.

  • (nil)

    If the container does not exist.

Raises:



684
685
686
687
688
689
# File 'lib/testcontainers/docker_container.rb', line 684

def info
  raise ContainerNotStartedError unless @_container
  @_container.json
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#inside_container?Boolean

Returns whether this is running inside a container.

Returns:

  • (Boolean)

    Whether this is running inside a container.



914
915
916
# File 'lib/testcontainers/docker_container.rb', line 914

def inside_container?
  File.exist?("/.dockerenv")
end

#kill(signal: "SIGKILL") ⇒ DockerContainer?

Kills the container with the specified signal

Parameters:

  • signal (String) (defaults to: "SIGKILL")

    The signal to send to the container.

Returns:

  • (DockerContainer)

    The DockerContainer instance.

  • (nil)

    If the container does not exist.

Raises:



528
529
530
531
532
533
534
# File 'lib/testcontainers/docker_container.rb', line 528

def kill(signal: "SIGKILL")
  raise ContainerNotStartedError unless @_container
  @_container.kill(signal: signal)
  self
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#logs(stdout: true, stderr: true) ⇒ Array<String>

Returns the container’s logs.

Parameters:

  • stdout (Boolean) (defaults to: true)

    Whether to return stdout.

  • stderr (Boolean) (defaults to: true)

    Whether to return stderr.

Returns:

  • (Array<String>)

    The container’s logs.

Raises:



776
777
778
779
780
781
782
783
# File 'lib/testcontainers/docker_container.rb', line 776

def logs(stdout: true, stderr: true)
  raise ContainerNotStartedError unless @_container
  stdout = @_container.logs(stdout: stdout)
  stderr = @_container.logs(stderr: stderr)
  [stdout, stderr]
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#mapped_port(port) ⇒ Integer

Returns the mapped host port for the given container port.

Parameters:

  • port (Integer | String)

    The container port.

Returns:

  • (Integer)

    The mapped host port.

Raises:



717
718
719
720
721
722
723
724
725
726
727
728
729
730
# File 'lib/testcontainers/docker_container.rb', line 717

def mapped_port(port)
  raise ContainerNotStartedError unless @_container
  mapped_port = container_port(port)

  if inside_container?
    gateway_ip = container_gateway_ip
    host = docker_host

    return port.to_i if gateway_ip == host
  end
  mapped_port.to_i
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#mount_namesArray<String>

Returns the container’s mount names.

Returns:

  • (Array<String>)

    The container’s mount names.

Raises:



755
756
757
# File 'lib/testcontainers/docker_container.rb', line 755

def mount_names
  mounts.map { |mount| mount["Name"] }
end

#mountsArray<Hash>

Returns the container’s mounts.

Returns:

  • (Array<Hash>)

    An array of the container’s mounts.

Raises:



746
747
748
# File 'lib/testcontainers/docker_container.rb', line 746

def mounts
  info["Mounts"]
end

#paused?Boolean

Returns whether the container is paused.

Returns:

  • (Boolean)

    Whether the container is paused.

Raises:



592
593
594
# File 'lib/testcontainers/docker_container.rb', line 592

def paused?
  status == "paused"
end

#read_file(path) ⇒ String

Reads the contents of a file inside the container.

Parameters:

  • path (String)

    The path to the file.

Returns:

  • (String)

    The contents of the file.

Raises:



975
976
977
978
979
# File 'lib/testcontainers/docker_container.rb', line 975

def read_file(path)
  raise ContainerNotStartedError unless @_container

  @_container.read_file(path)
end

#remove(options = {}) ⇒ DockerContainer? Also known as: delete

Removes the container.

Parameters:

  • options (Hash) (defaults to: {})

    Additional options to send to the container remove command.

Returns:

  • (DockerContainer)

    The DockerContainer instance.

  • (nil)

    If the container does not exist.

Raises:



542
543
544
545
546
547
548
# File 'lib/testcontainers/docker_container.rb', line 542

def remove(options = {})
  @_container&.remove(options)
  @_container = nil
  self
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#restartDockerContainer

Restarts the container.

Returns:

Raises:



558
559
560
561
562
563
564
# File 'lib/testcontainers/docker_container.rb', line 558

def restart
  raise ContainerNotStartedError unless @_container
  @_container.restart
  self
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#restarting?Boolean

Returns whether the container is restarting.

Returns:

  • (Boolean)

    Whether the container is restarting.

Raises:



601
602
603
# File 'lib/testcontainers/docker_container.rb', line 601

def restarting?
  status == "restarting"
end

#running?Boolean, false

Returns whether the container is running.

Returns:

  • (Boolean)

    Whether the container is running.

  • (false)

    If the container has not been started instead of raising an ContainerNotStartedError.

Raises:



610
611
612
613
614
# File 'lib/testcontainers/docker_container.rb', line 610

def running?
  status == "running"
rescue ContainerNotStartedError
  false
end

#startDockerContainer Also known as: enter

Starts the container.

Returns:

Raises:



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/testcontainers/docker_container.rb', line 474

def start
  Docker::Image.create({"fromImage" => @image}.merge(@image_create_options))

  @_container ||= Docker::Container.create(_container_create_options)
  @_container.start

  @_id = @_container.id
  json = @_container.json
  @name = json["Name"]
  @_created_at = json["Created"]

  @wait_for&.call(self)

  self
rescue Docker::Error::NotFoundError => e
  raise NotFoundError, e.message
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#statusString

Returns the container’s status. Possible values are: “created”, “restarting”, “running”, “removing”, “paused”, “exited”, “dead”.

Returns:

  • (String)

    The container’s status.

Raises:



571
572
573
574
575
576
# File 'lib/testcontainers/docker_container.rb', line 571

def status
  raise ContainerNotStartedError unless @_container
  @_container.json["State"]["Status"]
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#stop(force: false) ⇒ DockerContainer Also known as: exit

Stops the container.

Parameters:

  • force (Boolean) (defaults to: false)

    Whether to force the container to stop.

Returns:

Raises:



502
503
504
505
506
507
508
# File 'lib/testcontainers/docker_container.rb', line 502

def stop(force: false)
  raise ContainerNotStartedError unless @_container
  @_container.stop(force: force)
  self
rescue Excon::Error::Socket => e
  raise ConnectionError, e.message
end

#stop!DockerContainer

Stops the container forcefully.

Returns:

See Also:



517
518
519
# File 'lib/testcontainers/docker_container.rb', line 517

def stop!
  stop(force: true)
end

#store_file(path, contents) ⇒ Object

Writes the contents of a file inside the container.

Parameters:

  • path (String)

    The path to the file.

  • contents (String)

    The contents of the file.

Raises:



987
988
989
990
991
# File 'lib/testcontainers/docker_container.rb', line 987

def store_file(path, contents)
  raise ContainerNotStartedError unless @_container

  @_container.store_file(path, contents)
end

#supports_healthcheck?Boolean

Returns whether the container supports healthchecks. This is determined by the presence of a healthcheck in the container’s configuration.

Returns:

  • (Boolean)

    Whether the container supports healthchecks.

Raises:



648
649
650
651
# File 'lib/testcontainers/docker_container.rb', line 648

def supports_healthcheck?
  raise ContainerNotStartedError unless @_container
  @_container.json["Config"]["Healthcheck"] != nil
end

#use {|DockerContainer| ... } ⇒ DockerContainer

Starts the container, yields the container instance to the block, and stops the container.

Yields:

Returns:



462
463
464
465
466
467
# File 'lib/testcontainers/docker_container.rb', line 462

def use
  start
  yield self
ensure
  stop
end

#wait_for_healthcheck(timeout: 60, interval: 0.1) ⇒ Boolean

Waits for the container to be healthy.

Parameters:

  • timeout (Integer) (defaults to: 60)

    The number of seconds to wait for the health check to be healthy.

  • interval (Float) (defaults to: 0.1)

    The number of seconds to wait between checks.

Returns:

  • (Boolean)

    Whether the container is healthy.

Raises:



836
837
838
839
840
841
842
843
844
845
846
847
848
849
# File 'lib/testcontainers/docker_container.rb', line 836

def wait_for_healthcheck(timeout: 60, interval: 0.1)
  raise ContainerNotStartedError unless @_container
  raise HealthcheckNotSupportedError unless supports_healthcheck?

  Timeout.timeout(timeout) do
    loop do
      return true if healthy?

      sleep interval
    end
  end
rescue Timeout::Error
  raise TimeoutError, "Timed out waiting for health check to be healthy"
end

#wait_for_http(timeout: 60, interval: 0.1, path: "/", container_port: 80, https: false, status: 200) ⇒ Boolean

Waits for the container to respond to HTTP requests.

Parameters:

  • 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) (defaults to: "/")

    The path to request.

  • container_port (Integer) (defaults to: 80)

    The container port to request.

  • https (Boolean) (defaults to: false)

    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:



891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
# File 'lib/testcontainers/docker_container.rb', line 891

def wait_for_http(timeout: 60, interval: 0.1, path: "/", container_port: 80, https: false, status: 200)
  raise ContainerNotStartedError unless @_container
  raise PortNotMappedError unless mapped_port(container_port)

  Timeout.timeout(timeout) do
    loop do
      begin
        response = Excon.get("#{https ? "https" : "http"}://#{host}:#{mapped_port(container_port)}#{path}")
        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:



812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'lib/testcontainers/docker_container.rb', line 812

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

  Timeout.timeout(timeout) do
    loop do
      stdout, stderr = @_container.logs(stdout: true, stderr: true)
      return true if stdout&.match?(matcher) || stderr&.match?(matcher)

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

#wait_for_tcp_port(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:



861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
# File 'lib/testcontainers/docker_container.rb', line 861

def wait_for_tcp_port(port, timeout: 60, interval: 0.1)
  raise ContainerNotStartedError unless @_container
  raise PortNotMappedError unless mapped_port(port)

  Timeout.timeout(timeout) do
    loop do
      Timeout.timeout(interval) do
        TCPSocket.new(host, mapped_port(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

#with(options) ⇒ DockerContainer

Set options for the container configuration using “with_” methods.

Parameters:

  • options (Hash)

    A hash of options where keys correspond to “with_” methods and values are the arguments for those methods.

Returns:



300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/testcontainers/docker_container.rb', line 300

def with(options)
  options.each do |key, value|
    method_name = "with_#{key}"
    if respond_to?(method_name)
      send(method_name, value)
    else
      raise ArgumentError, "Invalid with_ method: #{method_name}"
    end
  end

  self
end

#with_command(*parts) ⇒ DockerContainer

Set the command for the container.

Parameters:

  • parts (Array<String>)

    The command to run in the container as an array of strings.

Returns:



317
318
319
320
321
# File 'lib/testcontainers/docker_container.rb', line 317

def with_command(*parts)
  @command = parts.first.is_a?(Array) ? parts.first : parts

  self
end

#with_entrypoint(*parts) ⇒ DockerContainer

Set the entrypoint for the container.

Parameters:

  • parts (Array<String>)

    The entry point for the container as an array of strings.

Returns:



327
328
329
330
331
# File 'lib/testcontainers/docker_container.rb', line 327

def with_entrypoint(*parts)
  @entrypoint = parts.first.is_a?(Array) ? parts.first : parts

  self
end

#with_env(env_or_key, value = nil) ⇒ DockerContainer

Sets the container’s environment variables.

Parameters:

  • env_or_key (String, Hash, Array)

    The environment variable(s) to add.

    • When passing a Hash, the keys and values represent the variable names and values.

    • When passing an Array, each element should be a String in the format “KEY=VALUE”.

    • When passing a String, it should be in the format “KEY=VALUE” or a key when a value is also provided.

  • value (String, nil) (defaults to: nil)

    The value for the environment variable if env_or_key is a key (String).

Returns:



350
351
352
353
# File 'lib/testcontainers/docker_container.rb', line 350

def with_env(env_or_key, value = nil)
  add_env(env_or_key, value)
  self
end

#with_exposed_port(port) ⇒ DockerContainer

Adds a single exposed port to the container.

Parameters:

  • port (String, Integer)

    The port to expose.

Returns:



377
378
379
380
# File 'lib/testcontainers/docker_container.rb', line 377

def with_exposed_port(port)
  add_exposed_ports(port)
  self
end

#with_exposed_ports(*ports) ⇒ DockerContainer

Adds exposed ports to the container.

Parameters:

  • ports (Array<String, Integer>)

    The list of ports to expose.

Returns:



368
369
370
371
# File 'lib/testcontainers/docker_container.rb', line 368

def with_exposed_ports(*ports)
  add_exposed_ports(*ports)
  self
end

#with_filesystem_binds(filesystem_binds) ⇒ DockerContainer

Adds filesystem binds to the container.

Parameters:

  • filesystem_binds (Array, String, Hash)

    an array, string, or hash of filesystem binds.

Returns:



408
409
410
411
# File 'lib/testcontainers/docker_container.rb', line 408

def with_filesystem_binds(filesystem_binds)
  add_filesystem_binds(filesystem_binds)
  self
end

#with_fixed_exposed_port(container_port, host_port = nil) ⇒ DockerContainer Also known as: with_port_binding

Adds a fixed exposed port to the container.

Parameters:

  • container_port (String, Integer, Hash)

    The container port in the format “port/protocol” or as an integer. When passing a Hash, it should contain a single key-value pair with the container port as the key and the host port as the value.

  • host_port (Integer, nil) (defaults to: nil)

    The host port to bind the container port to.

Returns:



388
389
390
391
# File 'lib/testcontainers/docker_container.rb', line 388

def with_fixed_exposed_port(container_port, host_port = nil)
  add_fixed_exposed_port(container_port, host_port)
  self
end

#with_healthcheck(options = {}) ⇒ DockerContainer

Adds a healthcheck to the container.

Parameters:

  • options (Hash) (defaults to: {})

    the healthcheck options.

Options Hash (options):

  • :test (Array|String)

    the command to run to check the health of the container.

  • :interval (Float)

    the time in seconds between health checks. (default: 30)

  • :timeout (Float)

    the time in seconds to wait for a health check to complete. (default: 30)

  • :retries (Integer)

    the number of times to retry a failed health check before giving up. (default: 3)

  • :shell (Boolean)

    whether or not to run the health check in a shell. (default: false)

Returns:



441
442
443
444
# File 'lib/testcontainers/docker_container.rb', line 441

def with_healthcheck(options = {})
  add_healthcheck(options)
  self
end

#with_label(label, value) ⇒ DockerContainer

Adds a label to the container.

Parameters:

  • label (String)

    the label key.

  • value (String)

    the label value.

Returns:



427
428
429
430
# File 'lib/testcontainers/docker_container.rb', line 427

def with_label(label, value)
  add_label(label, value)
  self
end

#with_labels(labels) ⇒ DockerContainer

Adds labels to the container.

Parameters:

  • labels (Hash)

    the labels to add.

Returns:



417
418
419
420
# File 'lib/testcontainers/docker_container.rb', line 417

def with_labels(labels)
  add_labels(labels)
  self
end

#with_name(name) ⇒ DockerContainer

Set the name of the container.

Parameters:

  • name (String)

    The name of the container.

Returns:



337
338
339
340
# File 'lib/testcontainers/docker_container.rb', line 337

def with_name(name)
  @name = name
  self
end

#with_volumes(volumes = {}) ⇒ DockerContainer

Adds volumes to the container.

Parameters:

  • volumes (Hash) (defaults to: {})

    a hash of volume key-value pairs.

Returns:



400
401
402
403
# File 'lib/testcontainers/docker_container.rb', line 400

def with_volumes(volumes = {})
  add_volumes(volumes)
  self
end

#with_wait_for(method = nil, *args, **kwargs, &block) ⇒ DockerContainer

Add a wait_for strategy to the container configuration.

Parameters:

  • method (Symbol, String, Proc, Array) (defaults to: nil)

    The method to call on the container to wait for it to be ready.

  • args (Array)

    The arguments to pass to the method if it is a symbol or string.

  • kwargs (Hash)

    The keyword arguments to pass to the method if it is a symbol or string.

  • block (Proc)

    The block to call on the container to wait for it to be ready.

Returns:



453
454
455
456
# File 'lib/testcontainers/docker_container.rb', line 453

def with_wait_for(method = nil, *args, **kwargs, &block)
  add_wait_for(method, *args, **kwargs, &block)
  self
end

#with_working_dir(working_dir) ⇒ DockerContainer

Sets the container’s working directory.

Parameters:

  • working_dir (String)

    the working directory for the container.

Returns:



359
360
361
362
# File 'lib/testcontainers/docker_container.rb', line 359

def with_working_dir(working_dir)
  @working_dir = working_dir
  self
end