Skip to content

Commit

Permalink
Fix for IP per task in Marathon 1.3+. (#329)
Browse files Browse the repository at this point in the history
This should resolve #313.
  • Loading branch information
brndnmtthws authored Oct 4, 2016
1 parent 32fa113 commit 56df70c
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 5 deletions.
86 changes: 86 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,65 @@ def test_get_task_ip_and_ports_ip_per_task_no_ip(self):

self.assertEquals(result, expected)

def test_get_task_ip_and_ports_ip_per_task_marathon13(self):
app = {
'ipAddress': {},
'container': {
'type': 'DOCKER',
'docker': {
'network': 'USER',
'portMappings': [
{
'containerPort': 80,
'servicePort': 10000,
},
{
'containerPort': 81,
'servicePort': 10001,
},
],
},
},
}
task = {
"id": "testtaskid",
"ipAddresses": [{"ipAddress": "1.2.3.4"}]
}

result = utils.get_task_ip_and_ports(app, task)
expected = ("1.2.3.4", [80, 81])

self.assertEquals(result, expected)

def test_get_task_ip_and_ports_ip_per_task_no_ip_marathon13(self):
app = {
'ipAddress': {},
'container': {
'type': 'DOCKER',
'docker': {
'network': 'USER',
'portMappings': [
{
'containerPort': 80,
'servicePort': 10000,
},
{
'containerPort': 81,
'servicePort': 10001,
},
],
},
},
}
task = {
"id": "testtaskid",
}

result = utils.get_task_ip_and_ports(app, task)
expected = (None, None)

self.assertEquals(result, expected)

def test_get_task_ip_and_ports_port_map(self):
app = {}
task = {
Expand Down Expand Up @@ -182,6 +241,33 @@ def test_ip_per_task_exhausted(self):
self.assertEquals(ports[-3:], [None] * 3)
self.assertEquals(sorted(ports[:-3]), list(range(10000, 10021)))

def test_ip_per_task_marathon13(self):
app = {
'ipAddress': {},
'container': {
'type': 'DOCKER',
'docker': {
'network': 'USER',
'portMappings': [
{
'containerPort': 80,
'servicePort': 10000,
},
{
'containerPort': 81,
'servicePort': 10001,
},
],
},
},
'tasks': [{
"id": "testtaskid",
"ipAddresses": [{"ipAddress": "1.2.3.4"}]
}],
}
self.assertEquals(self.assigner.get_service_ports(app),
[10000, 10001])


def _get_app(idx=1, num_ports=3, num_tasks=1, ip_per_task=True,
inc_service_ports=False):
Expand Down
46 changes: 41 additions & 5 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,19 @@ def get_service_ports(self, app):
:return: The list of ports. Note that if auto-assigning and ports
become exhausted, a port may be returned as None.
"""
# Are we using 'USER' network?
if is_user_network(app):
# Here we must use portMappings
portMappings = app.get('container', {})\
.get('docker', {})\
.get('portMappings', [])
ports = filter(lambda p: p is not None,
map(lambda p: p.get('servicePort', None),
portMappings))
ports = list(ports)
if ports:
return list(ports)

ports = app.get('ports', [])
if 'portDefinitions' in app:
ports = filter(lambda p: p is not None,
Expand All @@ -119,8 +132,6 @@ def get_service_ports(self, app):
ports = list(ports) # wtf python?
if not ports and is_ip_per_task(app) and self.can_assign \
and len(app['tasks']) > 0:
logger.warning("Auto assigning service port for "
"IP-per-container task")
task = app['tasks'][0]
_, task_ports = get_task_ip_and_ports(app, task)
if task_ports is not None:
Expand Down Expand Up @@ -158,6 +169,18 @@ def is_ip_per_task(app):
return app.get('ipAddress') is not None


def is_user_network(app):
"""
Returns True if container network mode is set to USER
:param app: The application to check.
:return: True if using USER network, False otherwise.
"""
c = app.get('container', {})
return c is not None and c.get('type', '') == 'DOCKER' and \
c.get('docker', {})\
.get('network', '') == 'USER'


def get_task_ip_and_ports(app, task):
"""
Return the IP address and list of ports used to access a task. For a
Expand All @@ -175,16 +198,29 @@ def get_task_ip_and_ports(app, task):
# single IP address, so just take the first IP in the list.
if is_ip_per_task(app):
logger.debug("Using IP per container")

task_ip_addresses = task.get('ipAddresses')
if not task_ip_addresses:
logger.warning("Task %s does not yet have an ip address allocated",
task['id'])
return None, None

task_ip = task_ip_addresses[0]['ipAddress']

discovery = app['ipAddress'].get('discovery', {})
task_ports = [int(port['number'])
for port in discovery.get('ports', [])]
# Are we using 'USER' network?
if is_user_network(app):
# in this case, we pull the port from portMappings
portMappings = app.get('container', {})\
.get('docker', {})\
.get('portMappings', [])
ports = filter(lambda p: p is not None,
map(lambda p: p.get('containerPort', None),
portMappings))
task_ports = list(ports)
else:
discovery = app['ipAddress'].get('discovery', {})
task_ports = [int(port['number'])
for port in discovery.get('ports', [])]
else:
logger.debug("Using host port mapping")
task_ports = task.get('ports', [])
Expand Down

0 comments on commit 56df70c

Please sign in to comment.