@ -301,6 +301,11 @@ options:
- List of ports to publish from the container to the host .
- List of ports to publish from the container to the host .
- " Use docker CLI syntax: C(8000), C(9000:8000), or C(0.0.0.0:9000:8000), where 8000 is a
- " Use docker CLI syntax: C(8000), C(9000:8000), or C(0.0.0.0:9000:8000), where 8000 is a
container port , 9000 is a host port , and 0.0 .0 .0 is a host interface . "
container port , 9000 is a host port , and 0.0 .0 .0 is a host interface . "
- Port ranges can be used for source and destination ports . If two ranges with
different lengths are specified , the shorter range will be used .
- " Bind addresses must be either IPv4 or IPv6 addresses. Hostnames are I(not) allowed. This
is different from the C ( docker ) command line utility . Use the L ( dig lookup , . . / lookup / dig . html )
to resolve hostnames . "
- Container ports must be exposed either in the Dockerfile or via the C ( expose ) option .
- Container ports must be exposed either in the Dockerfile or via the C ( expose ) option .
- A value of C ( all ) will publish all exposed container ports to random host ports , ignoring
- A value of C ( all ) will publish all exposed container ports to random host ports , ignoring
any other mappings .
any other mappings .
@ -736,6 +741,51 @@ def is_volume_permissions(input):
return True
return True
def parse_port_range ( range_or_port , module ) :
'''
Parses a string containing either a single port or a range of ports .
Returns a list of integers for each port in the list .
'''
if ' - ' in range_or_port :
start , end = [ int ( port ) for port in range_or_port . split ( ' - ' ) ]
if end < start :
module . fail_json ( msg = ' Invalid port range: {0} ' . format ( range_or_port ) )
return list ( range ( start , end + 1 ) )
else :
return [ int ( range_or_port ) ]
def split_colon_ipv6 ( input , module ) :
'''
Split string by ' : ' , while keeping IPv6 addresses in square brackets in one component .
'''
if ' [ ' not in input :
return input . split ( ' : ' )
start = 0
result = [ ]
while start < len ( input ) :
i = input . find ( ' [ ' , start )
if i < 0 :
result . extend ( input [ start : ] . split ( ' : ' ) )
break
j = input . find ( ' ] ' , i )
if j < 0 :
module . fail_json ( msg = ' Cannot find closing " ] " in input " {0} " for opening " [ " at index {1} ! ' . format ( input , i + 1 ) )
result . extend ( input [ start : i ] . split ( ' : ' ) )
k = input . find ( ' : ' , j )
if k < 0 :
result [ - 1 ] + = input [ i : ]
start = len ( input )
else :
result [ - 1 ] + = input [ i : k ]
if k == len ( input ) :
result . append ( ' ' )
break
start = k + 1
return result
class TaskParameters ( DockerBaseClass ) :
class TaskParameters ( DockerBaseClass ) :
'''
'''
Access and parse module parameters
Access and parse module parameters
@ -1107,27 +1157,38 @@ class TaskParameters(DockerBaseClass):
binds = { }
binds = { }
for port in self . published_ports :
for port in self . published_ports :
parts = str ( port ) . split ( ' : ' )
parts = split_colon_ipv6 ( str ( port ) , self . client . module )
container_port = parts [ - 1 ]
container_port = parts [ - 1 ]
if ' / ' not in container_port :
protocol = ' '
container_port = int ( parts [ - 1 ] )
if ' / ' in container_port :
container_port , protocol = parts [ - 1 ] . split ( ' / ' )
container_ports = parse_port_range ( container_port , self . client . module )
p_len = len ( parts )
p_len = len ( parts )
if p_len == 1 :
if p_len == 1 :
bind = ( default_ip , )
port_ binds = len ( container_ports ) * [ ( default_ip , ) ]
elif p_len == 2 :
elif p_len == 2 :
bind = ( default_ip , int ( parts [ 0 ] ) )
port_binds = [ ( default_ip , port ) for port in parse_port_range ( parts [ 0 ] , self . client . module ) ]
elif p_len == 3 :
elif p_len == 3 :
bind = ( parts [ 0 ] , int ( parts [ 1 ] ) ) if parts [ 1 ] else ( parts [ 0 ] , )
# We only allow IPv4 and IPv6 addresses for the bind address
if not re . match ( r ' ^[0-9]+ \ .[0-9]+ \ .[0-9]+ \ .[0-9]+$ ' , parts [ 0 ] ) and not re . match ( r ' ^ \ [[0-9a-fA-F:]+ \ ]$ ' , parts [ 0 ] ) :
if container_port in binds :
self . fail ( ( ' Bind addresses for published ports must be IPv4 or IPv6 addresses, not hostnames. '
old_bind = binds [ container_port ]
' Use the dig lookup to resolve hostnames. (Found hostname: {0} ) ' ) . format ( parts [ 0 ] ) )
if isinstance ( old_bind , list ) :
if parts [ 1 ] :
old_bind. append ( bind )
port_binds = [ ( parts [ 0 ] , port ) for port in parse_port_range ( parts [ 1 ] , self . client . module ) ]
else :
else :
binds [ container_port ] = [ binds [ container_port ] , bind ]
port_binds = len ( container_ports ) * [ ( parts [ 0 ] , ) ]
else :
binds [ container_port ] = bind
for bind , container_port in zip ( port_binds , container_ports ) :
idx = ' {0} / {1} ' . format ( container_port , protocol ) if protocol else container_port
if idx in binds :
old_bind = binds [ idx ]
if isinstance ( old_bind , list ) :
old_bind . append ( bind )
else :
binds [ idx ] = [ old_bind , bind ]
else :
binds [ idx ] = bind
return binds
return binds
def _get_volume_binds ( self , volumes ) :
def _get_volume_binds ( self , volumes ) :