From 7671bd6efc44f071a5662bcbcffded3f1eb519ab Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Wed, 5 Sep 2018 21:14:13 +0300 Subject: [PATCH 01/13] [#438] Fix accessing unknown deployments --- legion/legion/k8s/services.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index a42de6e33..e315eaecd 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -289,7 +289,11 @@ def _load_deployment_data(self): if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] - self._deployment = model_deployments[0] if model_deployments else None + if model_deployments: + self._deployment = model_deployments[0] + else: + self._deployment = None + LOGGER.error('Cannot load model deployment: no one has been found') self._deployment_data_loaded = True @@ -310,7 +314,10 @@ def scale(self): :return: int -- current model scale """ self._load_deployment_data() - return self.deployment.status.available_replicas if self.deployment.status.available_replicas else 0 + if self.deployment and self.deployment.status.available_replicas: + return self.deployment.status.available_replicas + else: + return 0 @scale.setter def scale(self, new_scale): @@ -329,15 +336,18 @@ def scale(self, new_scale): extension_api = kubernetes.client.ExtensionsV1beta1Api(client) - old_scale = self._deployment.spec.replicas - self._deployment.spec.replicas = new_scale + if not self.deployment: + raise Exception('Cannot get model deployment: deployment is unknown') + + old_scale = self.deployment.spec.replicas + self.deployment.spec.replicas = new_scale LOGGER.info('Scaling service {} in namespace {} from {} to {} replicas' - .format(self._deployment.metadata.name, self._deployment.metadata.namespace, old_scale, new_scale)) + .format(self.deployment.metadata.name, self.deployment.metadata.namespace, old_scale, new_scale)) - extension_api.patch_namespaced_deployment(self._deployment.metadata.name, - self._deployment.metadata.namespace, - self._deployment) + extension_api.patch_namespaced_deployment(self.deployment.metadata.name, + self.deployment.metadata.namespace, + self.deployment) self.reload_cache() From 4bf5dd5e6b72516492f7e8c5bc47c24e76cfb436 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Mon, 10 Sep 2018 16:46:30 +0300 Subject: [PATCH 02/13] [#438] Add checking of deployment existence --- legion/legion/k8s/services.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index e315eaecd..e76fd21ab 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -17,6 +17,7 @@ legion k8s services classes """ import logging +import time import kubernetes import kubernetes.client @@ -30,6 +31,7 @@ from legion.k8s.definitions import ModelIdVersion from legion.k8s.definitions import LEGION_COMPONENT_LABEL, LEGION_SYSTEM_LABEL, LEGION_API_SERVICE_PORT from legion.k8s.definitions import STATUS_OK, STATUS_WARN, STATUS_FAIL +from legion.k8s.definitions import LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT import legion.k8s.utils from legion.utils import normalize_name @@ -284,16 +286,21 @@ def _load_deployment_data(self): client = legion.k8s.utils.build_client() extension_api = kubernetes.client.ExtensionsV1beta1Api(client) - all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) - model_deployments = [deployment for deployment in all_deployments.items - if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id - and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] - if model_deployments: - self._deployment = model_deployments[0] + for _ in range(LOAD_DATA_ITERATIONS): + all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) + model_deployments = [deployment for deployment in all_deployments.items + if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id + and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] + + if model_deployments: + self._deployment = model_deployments[0] + break + + LOGGER.debug('Waiting before next deployment analysis') + time.sleep(LOAD_DATA_TIMEOUT) else: - self._deployment = None - LOGGER.error('Cannot load model deployment: no one has been found') + raise Exception('Cannot load model deployment: no one has been found') self._deployment_data_loaded = True @@ -314,7 +321,7 @@ def scale(self): :return: int -- current model scale """ self._load_deployment_data() - if self.deployment and self.deployment.status.available_replicas: + if self.deployment.status.available_replicas: return self.deployment.status.available_replicas else: return 0 From 34ee576b39050d3c23377094a41694471ca78422 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Mon, 10 Sep 2018 17:21:37 +0300 Subject: [PATCH 03/13] [#438] Remove unaccessable code --- legion/legion/k8s/services.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index e76fd21ab..5f1e5150f 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -343,9 +343,6 @@ def scale(self, new_scale): extension_api = kubernetes.client.ExtensionsV1beta1Api(client) - if not self.deployment: - raise Exception('Cannot get model deployment: deployment is unknown') - old_scale = self.deployment.spec.replicas self.deployment.spec.replicas = new_scale From f63b37523e72492d084a3fcc6b8a2d1950af97e4 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Mon, 10 Sep 2018 20:41:24 +0300 Subject: [PATCH 04/13] [#438] Fix codestyle --- legion/legion/k8s/services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index 5f1e5150f..f94f0bf14 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -290,8 +290,8 @@ def _load_deployment_data(self): for _ in range(LOAD_DATA_ITERATIONS): all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) model_deployments = [deployment for deployment in all_deployments.items - if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id - and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] + if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id + and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] if model_deployments: self._deployment = model_deployments[0] From e508cfa1bdebdcc9cdb604f8df452f5ccac7c2a0 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Tue, 11 Sep 2018 00:54:50 +0300 Subject: [PATCH 05/13] [#438] Add missed difenitions --- legion/legion/k8s/definitions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/legion/legion/k8s/definitions.py b/legion/legion/k8s/definitions.py index 3b1b447c7..7fbc53471 100644 --- a/legion/legion/k8s/definitions.py +++ b/legion/legion/k8s/definitions.py @@ -42,6 +42,9 @@ EVENT_MODIFIED = 'MODIFIED' EVENT_DELETED = 'DELETED' +LOAD_DATA_ITERATIONS = 5 +LOAD_DATA_TIMEOUT = 2 + ModelContainerMetaInformation = typing.NamedTuple('ModelContainerMetaInformation', [ ('k8s_name', str), ('model_id', str), From ed10fd9a94ddcebacef49009cae05ec634ff0ece Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Mon, 10 Sep 2018 15:23:43 +0300 Subject: [PATCH 06/13] [#438] Move retry logic to external function --- legion/legion/k8s/services.py | 44 ++++++++++++++++++----------------- legion/legion/utils.py | 30 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index f94f0bf14..04d9681d3 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -17,7 +17,6 @@ legion k8s services classes """ import logging -import time import kubernetes import kubernetes.client @@ -33,7 +32,7 @@ from legion.k8s.definitions import STATUS_OK, STATUS_WARN, STATUS_FAIL from legion.k8s.definitions import LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT import legion.k8s.utils -from legion.utils import normalize_name +from legion.utils import normalize_name, retry_function_call LOGGER = logging.getLogger(__name__) @@ -274,35 +273,38 @@ def deployment(self): self._load_deployment_data() return self._deployment - def _load_deployment_data(self): + def _load_deployment_data_logic(self): """ - Load deployment data (lazy loading) + Logic (is called with retries) to load model service deployment - :return: None + :return: bool """ - if self._deployment_data_loaded: - return - client = legion.k8s.utils.build_client() extension_api = kubernetes.client.ExtensionsV1beta1Api(client) - for _ in range(LOAD_DATA_ITERATIONS): - all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) - model_deployments = [deployment for deployment in all_deployments.items - if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id - and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] + all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) + model_deployments = [deployment for deployment in all_deployments.items + if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id + and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] - if model_deployments: - self._deployment = model_deployments[0] - break - - LOGGER.debug('Waiting before next deployment analysis') - time.sleep(LOAD_DATA_TIMEOUT) + if model_deployments: + self._deployment = model_deployments[0] + self._deployment_data_loaded = True + return True else: - raise Exception('Cannot load model deployment: no one has been found') + return False + + def _load_deployment_data(self): + """ + Load deployment data (lazy loading) + + :return: None + """ + if self._deployment_data_loaded: + return - self._deployment_data_loaded = True + retry_function_call(self._load_deployment_data_logic, LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) def reload_cache(self): """ diff --git a/legion/legion/utils.py b/legion/legion/utils.py index efed3cb7e..0286a2129 100644 --- a/legion/legion/utils.py +++ b/legion/legion/utils.py @@ -22,10 +22,12 @@ import getpass import distutils.dir_util import re +import logging import shutil import socket import subprocess import sys +import time import tempfile import zipfile @@ -39,6 +41,7 @@ KUBERNETES_STRING_LENGTH_LIMIT = 63 +LOGGER = logging.getLogger(__name__) def render_template(template_name, values=None): @@ -588,3 +591,30 @@ def deduce_model_file_name(model_id, model_version): return os.path.join(default_prefix, file_name) return file_name + + +def retry_function_call(function_to_call, retries, timeout): + """ + Try to call function till it will return True-able object. + Raise if there are no retries left + + :param function_to_call: function to be called + :type function_to_call: Callable[[], bool] + :param retries: count of retries + :type retries: int + :param timeout: timeout between retries + :type timeout: int + :return: None + """ + for no in range(retries): + result = function_to_call() + if result: + break + + if no < retries - 1: + LOGGER.debug('Retry {}/{} was failed. Waiting {}s before next retry analysis'.format(no + 1, + retries, + timeout)) + time.sleep(timeout) + else: + raise Exception('No retries left') From aea847cf9faaa652dbfafd1a6e21e4f5b726914a Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Mon, 10 Sep 2018 15:58:06 +0300 Subject: [PATCH 07/13] [#438] Return result of successful retry --- legion/legion/k8s/services.py | 10 +++++----- legion/legion/utils.py | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index 04d9681d3..5d6e87950 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -289,11 +289,9 @@ def _load_deployment_data_logic(self): and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] if model_deployments: - self._deployment = model_deployments[0] - self._deployment_data_loaded = True - return True + return model_deployments[0] else: - return False + return None def _load_deployment_data(self): """ @@ -304,7 +302,9 @@ def _load_deployment_data(self): if self._deployment_data_loaded: return - retry_function_call(self._load_deployment_data_logic, LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) + self._deployment = retry_function_call(self._load_deployment_data_logic, + LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) + self._deployment_data_loaded = True def reload_cache(self): """ diff --git a/legion/legion/utils.py b/legion/legion/utils.py index 0286a2129..9587b4df2 100644 --- a/legion/legion/utils.py +++ b/legion/legion/utils.py @@ -595,26 +595,26 @@ def deduce_model_file_name(model_id, model_version): def retry_function_call(function_to_call, retries, timeout): """ - Try to call function till it will return True-able object. + Try to call function till it will return not None object. Raise if there are no retries left :param function_to_call: function to be called - :type function_to_call: Callable[[], bool] + :type function_to_call: Callable[[], any] :param retries: count of retries :type retries: int :param timeout: timeout between retries :type timeout: int - :return: None + :return: Any -- result of successful function call """ for no in range(retries): result = function_to_call() - if result: - break + if result is not None: + return result if no < retries - 1: LOGGER.debug('Retry {}/{} was failed. Waiting {}s before next retry analysis'.format(no + 1, retries, timeout)) time.sleep(timeout) - else: - raise Exception('No retries left') + + raise Exception('No retries left') From 957c9f14615406d8f634e894ea676df2964645c6 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Fri, 14 Sep 2018 11:28:48 +0300 Subject: [PATCH 08/13] [#438] Add tests and function inspecting --- legion/legion/utils.py | 33 +++++++++++++--- legion/tests/test_utils_other.py | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 legion/tests/test_utils_other.py diff --git a/legion/legion/utils.py b/legion/legion/utils.py index 9587b4df2..f8094d50c 100644 --- a/legion/legion/utils.py +++ b/legion/legion/utils.py @@ -30,6 +30,7 @@ import time import tempfile import zipfile +import inspect import legion.config import legion.containers.headers @@ -593,7 +594,24 @@ def deduce_model_file_name(model_id, model_version): return file_name -def retry_function_call(function_to_call, retries, timeout): +def get_function_description(callable_object): + """ + Gather information about callable object to string + + :param callable_object: callable object to analyze + :type callable_object: Callable[[], any] + :return: str -- object description + """ + object_class_name = callable_object.__class__.__name__ + if not callable(callable_object): + return ''.format(object_class_name) + + object_name = callable_object.__name__ + module_name = inspect.getmodule(callable_object) + return '<{} {} in {}>'.format(object_class_name, object_name, module_name) + + +def ensure_function_succeed(function_to_call, retries, timeout): """ Try to call function till it will return not None object. Raise if there are no retries left @@ -604,17 +622,20 @@ def retry_function_call(function_to_call, retries, timeout): :type retries: int :param timeout: timeout between retries :type timeout: int - :return: Any -- result of successful function call + :return: Any -- result of successful function call or None if no retries left """ + function_description = get_function_description(function_to_call) for no in range(retries): + LOGGER.debug('Calling {}'.format(function_description)) result = function_to_call() if result is not None: return result + if no < retries: + LOGGER.debug('Retry {}/{} was failed'.format(no + 1, retries)) if no < retries - 1: - LOGGER.debug('Retry {}/{} was failed. Waiting {}s before next retry analysis'.format(no + 1, - retries, - timeout)) + LOGGER.debug('Waiting {}s before next retry analysis'.format(timeout)) time.sleep(timeout) - raise Exception('No retries left') + LOGGER.error('No retries left for function {}'.format(function_description)) + return None diff --git a/legion/tests/test_utils_other.py b/legion/tests/test_utils_other.py new file mode 100644 index 000000000..fadb19e2a --- /dev/null +++ b/legion/tests/test_utils_other.py @@ -0,0 +1,68 @@ +# +# Copyright 2017 EPAM Systems +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import legion.utils as utils + +import unittest2 + + +class TestUtilsOther(unittest2.TestCase): + def test_lambda_analyzing(self): + l = lambda x: x**2 + description = utils.get_function_description(l) + self.assertIn('function ', description) + self.assertIn(__file__, description) + + def test_function_analyzing(self): + def tmp(): + return 42 + description = utils.get_function_description(tmp) + self.assertIn('function tmp', description) + self.assertIn(__file__, description) + + def test_non_callable_analyzing(self): + tmp = 42 + description = utils.get_function_description(tmp) + self.assertIn('not callable object', description) + + def test_ensure_retries_positive(self): + counter = 0 + + def func(): + nonlocal counter + if counter > 1: + return 42 + counter += 1 + + result = utils.ensure_function_succeed(func, 3, 1) + self.assertEqual(result, 42) + self.assertEqual(counter, 2) + + def test_ensure_retries_negative(self): + counter = 0 + + def func(): + nonlocal counter + if counter > 1: + return 42 + counter += 1 + + result = utils.ensure_function_succeed(func, 2, 1) + self.assertEqual(result, None) + self.assertEqual(counter, 2) + + +if __name__ == '__main__': + unittest2.main() From 496d941ed75a6022f5fa2b7ec60e383c518560cc Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Fri, 14 Sep 2018 11:30:01 +0300 Subject: [PATCH 09/13] [#438] Fix call signature --- legion/legion/k8s/services.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index 5d6e87950..fdf16f5b6 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -32,7 +32,7 @@ from legion.k8s.definitions import STATUS_OK, STATUS_WARN, STATUS_FAIL from legion.k8s.definitions import LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT import legion.k8s.utils -from legion.utils import normalize_name, retry_function_call +from legion.utils import normalize_name, ensure_function_succeed LOGGER = logging.getLogger(__name__) @@ -302,8 +302,8 @@ def _load_deployment_data(self): if self._deployment_data_loaded: return - self._deployment = retry_function_call(self._load_deployment_data_logic, - LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) + self._deployment = ensure_function_succeed(self._load_deployment_data_logic, + LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) self._deployment_data_loaded = True def reload_cache(self): From 21c1d5eac7269d180035a7cff76250b0a3045989 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Fri, 14 Sep 2018 12:06:37 +0300 Subject: [PATCH 10/13] [#438] Disable linting error in tests --- legion/tests/test_utils_other.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/legion/tests/test_utils_other.py b/legion/tests/test_utils_other.py index fadb19e2a..631f284b6 100644 --- a/legion/tests/test_utils_other.py +++ b/legion/tests/test_utils_other.py @@ -20,8 +20,8 @@ class TestUtilsOther(unittest2.TestCase): def test_lambda_analyzing(self): - l = lambda x: x**2 - description = utils.get_function_description(l) + lamb = lambda x: x**2 # pylint: disable=E731 + description = utils.get_function_description(lamb) self.assertIn('function ', description) self.assertIn(__file__, description) From 1075089531d4c888df6a61f3a03517ac5a40d35a Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Fri, 14 Sep 2018 12:15:10 +0300 Subject: [PATCH 11/13] [#438] Disable pylint E731 rule on top level for tests --- Jenkinsfile | 2 +- legion/analyze_code.sh | 2 +- legion/tests/test_utils_other.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 8ccffdc4e..a6338fc16 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -166,7 +166,7 @@ node { sh ''' cd legion ../.venv/bin/pycodestyle --show-source --show-pep8 legion - ../.venv/bin/pycodestyle --show-source --show-pep8 tests --ignore E402,E126,W503 + ../.venv/bin/pycodestyle --show-source --show-pep8 tests --ignore E402,E126,W503,E731 ../.venv/bin/pydocstyle --source legion export TERM="linux" diff --git a/legion/analyze_code.sh b/legion/analyze_code.sh index a66137e40..08267849f 100755 --- a/legion/analyze_code.sh +++ b/legion/analyze_code.sh @@ -1,6 +1,6 @@ #!/bin/bash pycodestyle --show-source --show-pep8 legion -pycodestyle --show-source --show-pep8 tests --ignore E402,E126,W503 +pycodestyle --show-source --show-pep8 tests --ignore E402,E126,W503,E731 pydocstyle --source legion pylint legion \ No newline at end of file diff --git a/legion/tests/test_utils_other.py b/legion/tests/test_utils_other.py index 631f284b6..642f51650 100644 --- a/legion/tests/test_utils_other.py +++ b/legion/tests/test_utils_other.py @@ -20,7 +20,7 @@ class TestUtilsOther(unittest2.TestCase): def test_lambda_analyzing(self): - lamb = lambda x: x**2 # pylint: disable=E731 + lamb = lambda x: x**2 description = utils.get_function_description(lamb) self.assertIn('function ', description) self.assertIn(__file__, description) From 6066ca077c1e2d13dda8d6fbcf001196ccdc22bf Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Tue, 18 Sep 2018 09:41:30 +0300 Subject: [PATCH 12/13] Add next function logic for searching first element --- legion/legion/k8s/services.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index fdf16f5b6..0ad37c8b5 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -284,13 +284,11 @@ def _load_deployment_data_logic(self): extension_api = kubernetes.client.ExtensionsV1beta1Api(client) all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) - model_deployments = [deployment for deployment in all_deployments.items - if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id - and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version] - - if model_deployments: - return model_deployments[0] - else: + try: + return next(deployment for deployment in all_deployments.items + if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id + and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version) + except StopIteration: return None def _load_deployment_data(self): @@ -304,6 +302,9 @@ def _load_deployment_data(self): self._deployment = ensure_function_succeed(self._load_deployment_data_logic, LOAD_DATA_ITERATIONS, LOAD_DATA_TIMEOUT) + if not self._deployment: + raise Exception('Failed to load deployment for {!r}'.format(self)) + self._deployment_data_loaded = True def reload_cache(self): From d99c47c971aeea3ce21d137807e17fbd82bc1d41 Mon Sep 17 00:00:00 2001 From: Kirill Makhonin Date: Tue, 18 Sep 2018 11:31:58 +0300 Subject: [PATCH 13/13] [#438] Use default value arg. in next function --- legion/legion/k8s/services.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/legion/legion/k8s/services.py b/legion/legion/k8s/services.py index 0ad37c8b5..1f40e59d8 100644 --- a/legion/legion/k8s/services.py +++ b/legion/legion/k8s/services.py @@ -284,12 +284,9 @@ def _load_deployment_data_logic(self): extension_api = kubernetes.client.ExtensionsV1beta1Api(client) all_deployments = extension_api.list_namespaced_deployment(self._k8s_service.metadata.namespace) - try: - return next(deployment for deployment in all_deployments.items - if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id - and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version) - except StopIteration: - return None + return next((deployment for deployment in all_deployments.items + if deployment.metadata.labels.get(DOMAIN_MODEL_ID) == self.id + and deployment.metadata.labels.get(DOMAIN_MODEL_VERSION) == self.version), None) def _load_deployment_data(self): """