Skip to content

Commit 47c679f

Browse files
committed
Merge pull request #689 from kmala/proc
fix(port): retry a fixed number of times to find the port
2 parents 370c21a + 9995ca4 commit 47c679f

9 files changed

Lines changed: 51 additions & 19 deletions

File tree

rootfs/api/tests/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
from django.test.runner import DiscoverRunner
88

99

10+
def mock_port(*args, **kwargs):
11+
return 5000
12+
13+
1014
# Mock out router requests and add in some jitter
1115
# Used for application is available in router checks
1216
def fake_responses(request, context):

rootfs/api/tests/test_app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from scheduler import KubeException
1818

1919
from . import adapter
20+
from . import mock_port
2021
import requests_mock
2122

2223

@@ -30,6 +31,7 @@ def _mock_run(*args, **kwargs):
3031

3132
@requests_mock.Mocker(real_http=True, adapter=adapter)
3233
@mock.patch('api.models.release.publish_release', lambda *args: None)
34+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
3335
class AppTest(APITestCase):
3436
"""Tests creation of applications"""
3537

rootfs/api/tests/test_build.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
from scheduler import KubeException
2020

2121
from . import adapter
22+
from . import mock_port
2223
import requests_mock
2324

2425

2526
@requests_mock.Mocker(real_http=True, adapter=adapter)
2627
@mock.patch('api.models.release.publish_release', lambda *args: None)
28+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
2729
class BuildTest(APITransactionTestCase):
2830

2931
"""Tests build notification from build system"""

rootfs/api/tests/test_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
from api.models import App, Config
1616

1717
from . import adapter
18+
from . import mock_port
1819
import requests_mock
1920

2021

2122
@requests_mock.Mocker(real_http=True, adapter=adapter)
2223
@mock.patch('api.models.release.publish_release', lambda *args: None)
24+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
2325
class ConfigTest(APITransactionTestCase):
2426

2527
"""Tests setting and updating config values"""

rootfs/api/tests/test_hooks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from rest_framework.authtoken.models import Token
1212

1313
from . import adapter
14+
from . import mock_port
1415
import requests_mock
1516

1617
RSA_PUBKEY = (
@@ -34,6 +35,7 @@
3435

3536
@requests_mock.Mocker(real_http=True, adapter=adapter)
3637
@mock.patch('api.models.release.publish_release', lambda *args: None)
38+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
3739
class HookTest(APITransactionTestCase):
3840

3941
"""Tests API hooks used to trigger actions from external components"""

rootfs/api/tests/test_pods.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818
from scheduler import KubeException
1919

2020
from . import adapter
21+
from . import mock_port
2122
import requests_mock
2223

2324

2425
@requests_mock.Mocker(real_http=True, adapter=adapter)
2526
@mock.patch('api.models.release.publish_release', lambda *args: None)
27+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
2628
class PodTest(APITransactionTestCase):
2729
"""Tests creation of pods on nodes"""
2830

rootfs/api/tests/test_release.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
from api.models import Release
1818
from scheduler import KubeHTTPException
1919
from . import adapter
20+
from . import mock_port
2021
import requests_mock
2122

2223

2324
@requests_mock.Mocker(real_http=True, adapter=adapter)
2425
@mock.patch('api.models.release.publish_release', lambda *args: None)
26+
@mock.patch('scheduler.KubeHTTPClient._get_port', mock_port)
2527
class ReleaseTest(APITransactionTestCase):
2628

2729
"""Tests push notification from build system"""

rootfs/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ PyYAML==3.11
1616
requests==2.10.0
1717
requests-toolbelt==0.6.0
1818
simpleflock==0.0.3
19+
retrying==1.3.3

rootfs/scheduler/__init__.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import requests
1414
from requests_toolbelt import user_agent
1515
from .utils import dict_merge
16+
from retrying import retry
1617

1718
from deis import __version__ as deis_version
1819

@@ -347,7 +348,22 @@ def __init__(self):
347348
def deploy(self, namespace, name, image, command, **kwargs): # noqa
348349
logger.debug('deploy {}, img {}, params {}, cmd "{}"'.format(name, image, kwargs, command))
349350
app_type = kwargs.get('app_type')
351+
build_type = kwargs.get('build_type')
350352
routable = kwargs.get('routable', False)
353+
port = None
354+
355+
try:
356+
if routable:
357+
if build_type == "buildpack":
358+
logger.debug("Using default port 5000 for build pack app {}".format(name))
359+
port = 5000
360+
else:
361+
port = self._get_port(image)
362+
if port is None:
363+
raise Exception("Expose a port or make the app non routable by changing"
364+
" the process type")
365+
except Exception as e:
366+
raise KubeException('{} (scheduler::deploy): {}'.format(name, e))
351367

352368
# Fetch old RC and create the new one for a release
353369
old_rc = self._get_old_rc(namespace, app_type)
@@ -425,9 +441,9 @@ def deploy(self, namespace, name, image, command, **kwargs): # noqa
425441
# Make sure the application is routable and uses the correct port
426442
# Done after the fact to let initial deploy settle before routing
427443
# traffic to the application
428-
self._update_application_service(namespace, name, app_type, image, routable)
444+
self._update_application_service(namespace, name, app_type, port, routable)
429445

430-
def _update_application_service(self, namespace, name, app_type, image, routable=False):
446+
def _update_application_service(self, namespace, name, app_type, port, routable=False):
431447
"""Update application service with all the various required information"""
432448
try:
433449
# Fetch service
@@ -444,10 +460,6 @@ def _update_application_service(self, namespace, name, app_type, image, routable
444460

445461
# Find if target port exists already, update / create as required
446462
if routable:
447-
port = self._get_port(image)
448-
if port is None:
449-
logger.debug("Failed to find port for Docker image {}, defaulting to 5000".format(image)) # noqa
450-
port = 5000
451463
for pos, item in enumerate(service['spec']['ports']):
452464
if item['port'] == 80 and port != item['targetPort']:
453465
# port 80 is the only one we care about right now
@@ -689,18 +701,18 @@ def resolve_state(self, pod):
689701

690702
return states.get(pod_state, pod_state)
691703

704+
@retry(stop_max_attempt_number=3, wait_fixed=1000)
692705
def _get_port(self, image):
693-
try:
694-
image = self.registry + '/' + image
695-
repo = image.split(":")
696-
# image already includes the tag, so we split it out here
697-
docker_cli = Client(version="auto")
698-
docker_cli.pull(repo[0]+":"+repo[1], tag=repo[2], insecure_registry=True)
699-
image_info = docker_cli.inspect_image(image)
700-
port = int(list(image_info['Config']['ExposedPorts'].keys())[0].split("/")[0])
701-
except Exception:
702-
port = None
703-
706+
# try thrice to find the port before raising exception as docker-py is flaky
707+
imagepath = self.registry + '/' + image
708+
repo = imagepath.split(":")
709+
# image already includes the tag, so we split it out here
710+
docker_cli = Client(version="auto")
711+
docker_cli.pull(repo[0]+":"+repo[1], tag=repo[2], insecure_registry=True)
712+
image_info = docker_cli.inspect_image(imagepath)
713+
if 'ExposedPorts' not in image_info['Config']:
714+
return None
715+
port = int(list(image_info['Config']['ExposedPorts'].keys())[0].split("/")[0])
704716
return port
705717

706718
def _api(self, tmpl, *args):
@@ -1199,8 +1211,11 @@ def _default_buildpack_readiness_probe(self, delay=30, timeout=5, period_seconds
11991211
'''
12001212
def _default_dockerapp_readiness_probe(self, image, delay=5, timeout=5, period_seconds=5,
12011213
success_threshold=1, failure_threshold=1):
1202-
port = self._get_port(image)
1203-
if port is None:
1214+
try:
1215+
port = self._get_port(image)
1216+
if port is None:
1217+
return None
1218+
except Exception:
12041219
return None
12051220
readinessprobe = {
12061221
'readinessProbe': {

0 commit comments

Comments
 (0)