11require "java-properties"
22
33module Testcontainers
4+
45 # The DockerContainer class is used to manage Docker containers.
56 # It provides an interface to create, start, stop, and manipulate containers
67 # using the Docker API.
@@ -21,8 +22,22 @@ module Testcontainers
2122 # @attr_reader _container [Docker::Container, nil] the underlying Docker::Container object
2223 # @attr_reader _id [String, nil] the container's ID
2324 class DockerContainer
25+ class << self
26+ def setup_docker
27+ expanded_path = File . expand_path ( "~/.testcontainers.properties" )
28+
29+ properties = File . exist? ( expanded_path ) ? JavaProperties . load ( expanded_path ) : { }
30+
31+ tc_host = ENV [ "TESTCONTAINERS_HOST" ] || properties [ :"tc.host" ]
32+
33+ if tc_host && !tc_host . empty?
34+ Docker . url = tc_host
35+ end
36+ end
37+ end
38+
2439 attr_accessor :name , :image , :command , :entrypoint , :exposed_ports , :port_bindings , :volumes , :filesystem_binds ,
25- :env , :labels , :working_dir , :healthcheck , :wait_for
40+ :env , :labels , :working_dir , :healthcheck , :wait_for , :aliases , :network
2641 attr_accessor :logger
2742 attr_reader :_container , :_id
2843
@@ -39,9 +54,11 @@ class DockerContainer
3954 # @param env [Array<String>, Hash, nil] an array or a hash of environment variables for the container in the format KEY=VALUE
4055 # @param labels [Hash, nil] a hash of labels to be applied to the container
4156 # @param working_dir [String, nil] the working directory for the container
57+ # @param network [Testcontainers::Network, nil] the network to attach the container to
58+ # @param aliases [Array<String>, nil] the aliases for the container in the network
4259 # @param logger [Logger] a logger instance for the container
4360 def initialize ( image , name : nil , command : nil , entrypoint : nil , exposed_ports : nil , image_create_options : { } , port_bindings : nil , volumes : nil , filesystem_binds : nil ,
44- env : nil , labels : nil , working_dir : nil , healthcheck : nil , wait_for : nil , logger : Testcontainers . logger )
61+ env : nil , labels : nil , working_dir : nil , healthcheck : nil , wait_for : nil , network : nil , aliases : nil , logger : Testcontainers . logger )
4562
4663 @image = image
4764 @name = name
@@ -61,6 +78,8 @@ def initialize(image, name: nil, command: nil, entrypoint: nil, exposed_ports: n
6178 @_container = nil
6279 @_id = nil
6380 @_created_at = nil
81+ @aliases = aliases
82+ @network = network
6483 end
6584
6685 # Add environment variables to the container configuration.
@@ -87,7 +106,7 @@ def add_exposed_port(port)
87106 @exposed_ports ||= { }
88107 @port_bindings ||= { }
89108 @exposed_ports [ port ] ||= { }
90- @port_bindings [ port ] ||= [ { "HostPort" => "" } ]
109+ @port_bindings [ port ] ||= [ { "HostPort" => "" } ]
91110 @exposed_ports
92111 end
93112
@@ -119,7 +138,7 @@ def add_fixed_exposed_port(container_port, host_port = nil)
119138 @exposed_ports ||= { }
120139 @port_bindings ||= { }
121140 @exposed_ports [ container_port ] = { }
122- @port_bindings [ container_port ] = [ { "HostPort" => host_port . to_s } ]
141+ @port_bindings [ container_port ] = [ { "HostPort" => host_port . to_s } ]
123142 @port_bindings
124143 end
125144
@@ -229,7 +248,7 @@ def add_healthcheck(options = {})
229248 test = options [ :test ]
230249
231250 if test . nil?
232- @healthcheck = { "Test" => [ "NONE" ] }
251+ @healthcheck = { "Test" => [ "NONE" ] }
233252 return @healthcheck
234253 end
235254
@@ -457,6 +476,34 @@ def with_wait_for(method = nil, *args, **kwargs, &block)
457476 self
458477 end
459478
479+ # Returns the container's ID.
480+ #
481+ def id
482+ @_id
483+ end
484+
485+ # Returns the container's aliases within the network.
486+ #
487+ def aliases
488+ @aliases ||= [ ]
489+ end
490+
491+ # Sets the container's network.
492+ #
493+ # @param network [Testcontainers::Network] The network to attach the container to.
494+ def with_network ( network )
495+ @network = network
496+ self
497+ end
498+
499+ # Sets the container's network aliases.
500+ #
501+ # @param aliases [Array<String>] The aliases for the container in the network.
502+ def with_network_aliases ( *aliases )
503+ self . aliases += aliases &.flatten
504+ self
505+ end
506+
460507 # Starts the container, yields the container instance to the block, and stops the container.
461508 #
462509 # @yield [DockerContainer] The container instance.
@@ -474,19 +521,13 @@ def use
474521 # @raise [ConnectionError] If the connection to the Docker daemon fails.
475522 # @raise [NotFoundError] If Docker is unable to find the image.
476523 def start
477- expanded_path = File . expand_path ( "~/.testcontainers.properties" )
478-
479- properties = File . exist? ( expanded_path ) ? JavaProperties . load ( expanded_path ) : { }
480-
481- tc_host = ENV [ "TESTCONTAINERS_HOST" ] || properties [ :"tc.host" ]
482-
483- if tc_host && !tc_host . empty?
484- Docker . url = tc_host
485- end
524+ self . class . setup_docker
486525
487526 connection = Docker ::Connection . new ( Docker . url , Docker . options )
488527
489- Docker ::Image . create ( { "fromImage" => @image } . merge ( @image_create_options ) , connection )
528+ @network &.create
529+
530+ Docker ::Image . create ( { "fromImage" => @image } . merge ( @image_create_options ) , connection )
490531
491532 @_container ||= Docker ::Container . create ( _container_create_options )
492533 @_container . start
@@ -516,6 +557,7 @@ def start
516557 def stop ( force : false )
517558 raise ContainerNotStartedError unless @_container
518559 @_container . stop ( force : force )
560+ @network &.close
519561 self
520562 rescue Excon ::Error ::Socket => e
521563 raise ConnectionError , e . message
@@ -1026,7 +1068,7 @@ def normalize_port_bindings(port_bindings)
10261068 return port_bindings if port_bindings . is_a? ( Hash ) && port_bindings . values . all? { |v | v . is_a? ( Array ) }
10271069
10281070 port_bindings . each_with_object ( { } ) do |( container_port , host_port ) , hash |
1029- hash [ normalize_port ( container_port ) ] = [ { "HostPort" => host_port . to_s } ]
1071+ hash [ normalize_port ( container_port ) ] = [ { "HostPort" => host_port . to_s } ]
10301072 end
10311073 end
10321074
@@ -1126,6 +1168,10 @@ def docker_host
11261168 nil
11271169 end
11281170
1171+ def network_name
1172+ @network_name ||= network &.name
1173+ end
1174+
11291175 def _container_create_options
11301176 {
11311177 "name" => @name ,
@@ -1139,11 +1185,24 @@ def _container_create_options
11391185 "WorkingDir" => @working_dir ,
11401186 "Healthcheck" => @healthcheck ,
11411187 "HostConfig" => {
1188+ "NetworkMode" => network_name ,
11421189 "PortBindings" => @port_bindings ,
11431190 "Binds" => @filesystem_binds
1144- } . compact
1191+ } . compact ,
1192+ "NetworkingConfig" : _networking_config
11451193 } . compact
11461194 end
1195+
1196+ def _networking_config
1197+ return nil unless network_name && !aliases &.empty?
1198+ {
1199+ "EndpointsConfig" => {
1200+ network_name => {
1201+ "Aliases" => aliases
1202+ }
1203+ }
1204+ }
1205+ end
11471206 end
11481207
11491208 # Alias for forward-compatibility
0 commit comments