From 5b7bec40c4023d5af8dca95f053f118fbad2139c Mon Sep 17 00:00:00 2001 From: Brenden Matthews Date: Fri, 30 Sep 2016 12:29:08 +0200 Subject: [PATCH] Fix for IP per task in Marathon 1.3+. This should resolve #313. --- tests/test_utils.py | 83 +++++++++++++++++++++++++++++++++++++++++++++ utils.py | 43 ++++++++++++++++++++--- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 58d0f823..e466726b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -43,6 +43,63 @@ 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': { + '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': { + '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 = { @@ -182,6 +239,32 @@ 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': { + '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): diff --git a/utils.py b/utils.py index 6266e032..3b3b2d9d 100644 --- a/utils.py +++ b/utils.py @@ -110,6 +110,17 @@ 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)) + return list(ports) + ports = app.get('ports', []) if 'portDefinitions' in app: ports = filter(lambda p: p is not None, @@ -119,8 +130,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: @@ -158,6 +167,17 @@ 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. + """ + return app.get('container', {})\ + .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 @@ -175,16 +195,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', [])