Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sherif akoush issue/2621/refactor env var retrieval #3420

Merged
merged 14 commits into from
Jul 28, 2021
Merged

Sherif akoush issue/2621/refactor env var retrieval #3420

merged 14 commits into from
Jul 28, 2021

Conversation

sakoush
Copy link
Member

@sakoush sakoush commented Jul 21, 2021

Consolidate the logic of getting values from enviornment variables as they were duplicated in two places.

What this PR does / why we need it:

Tidy up code.

Which issue(s) this PR fixes:

Fixes #2621

Special notes for your reviewer:

I have introduced env_utils.py to contain the refactored logic. Otherwise there will be circular dependencies.

Does this PR introduce a user-facing change?:

@seldondev
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
To complete the pull request process, please assign majolo
You can assign the PR to them by writing /assign @majolo in a comment when ready.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link
Contributor

@RafalSkolasinski RafalSkolasinski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Just a few questions

@@ -0,0 +1,35 @@
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just have these in the utils.py? Is there a particular reason to have a separate utils file/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be some circular dependencies if we do that

seldon_core/metrics.py:16: in <module>
    from seldon_core.utils import get_image_name, get_model_name, get_deployment_name, get_predictor_name, \
seldon_core/utils.py:16: in <module>
    from seldon_core.user_model import (
seldon_core/user_model.py:10: in <module>
    from seldon_core.metrics import SeldonMetrics, validate_metrics

I have not investigated a lot into that though. I think that having a separate module to have all env variables handling is ok. isnt it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's already an improvement to have them in one place so if there is no obvious solution to circular dependency let's leave it as it is 👍

logger = logging.getLogger(__name__)

NONIMPLEMENTED_MSG = "NOT_IMPLEMENTED"
from seldon_core.env_utils import (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could just (if we put this into utils)

from seldon_core import utils

and then use utils.get_image_name wherever needed

Though both works and and usually over-import so does not require a change now.

python/seldon_core/utils.py Show resolved Hide resolved
@RafalSkolasinski
Copy link
Contributor

/test integration

@RafalSkolasinski
Copy link
Contributor

/test notebooks

@seldondev seldondev added size/L and removed size/XL labels Jul 21, 2021
Copy link
Contributor

@adriangonz adriangonz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one @sakoush ! Just had a look and, other than a small typo, everything looks good 👍

NONIMPLEMENTED_IMAGE_MSG = f"{NONIMPLEMENTED_MSG}:{NONIMPLEMENTED_MSG}"


def get_predictior_version(default_str: str = NONIMPLEMENTED_MSG) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: get_predictor_version instead of get_predictior_version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch

def get_request_path():
model_name = get_model_name()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conscious that it falls slightly outside the scope of this PR, but it would make sense to return None rather than NONIMPLEMENTED_MSG?

Copy link
Contributor

@RafalSkolasinski RafalSkolasinski Jul 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some of these are sometimes used for a key in a dictionary. None cannot be a key as far as I remember.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually... it can.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it can but if we read it back it will be "None" and not None so we need to convert anyway

@@ -348,6 +352,7 @@ def array_to_list_value(array: np.ndarray, lv: Optional[ListValue] = None) -> Li

Returns
-------
ListValue protobuf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch 👍

@@ -240,6 +240,22 @@ def send_feedback_grpc(self, request):
logging.info("Feedback called")


@pytest.fixture(name="mock_get_model_name")
def fixture_get_model_name(mocker):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make more sense to mock the environment variables rather than the methods?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having said that, I just realised that you are testing the environment as well down below so maybe you're right in that it makes more sense to mock only the methods here.

@RafalSkolasinski
Copy link
Contributor

/test integration

@RafalSkolasinski
Copy link
Contributor

/test notebooks

@RafalSkolasinski
Copy link
Contributor

Integration tests

================== 4 failed, 31 passed in 3246.50s (0:54:06) ===================
_______________________ TestPrepack.test_openapi_sklearn _______________________
[gw1] linux -- Python 3.7.10 /opt/conda/bin/python

self = <test_prepackaged_servers.TestPrepack object at 0x7f636d7f1110>
namespace = 'test-openapi-sklearn'

    def test_openapi_sklearn(self, namespace):
        spec = "../../servers/sklearnserver/samples/iris.yaml"
        retry_run(f"kubectl apply -f {spec} -n {namespace}")
        wait_for_status("sklearn", namespace)
>       wait_for_rollout("sklearn", namespace)

test_prepackaged_servers.py:250: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
seldon_e2e_utils.py:161: in wait_for_rollout
    wait_for_deployment(deployment_name, namespace, attempts, sleep)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

deployment_name = 'sklearn-default-0-classifier'
namespace = 'test-openapi-sklearn', attempts = 50, sleep = 5

    def wait_for_deployment(deployment_name, namespace, attempts=50, sleep=5):
        logging.info(f"Waiting for deployment {deployment_name}")
        for _ in range(attempts):
            ret = run(
                f"kubectl rollout status -n {namespace} deploy/{deployment_name}",
                shell=True,
            )
            if ret.returncode == 0:
                logging.info(f"Successfully waited for deployment {deployment_name}")
                break
            logging.warning(f"Unsuccessful wait command but retrying for {deployment_name}")
            time.sleep(sleep)
        assert (
            ret.returncode == 0
>       ), f"Wait for rollout of {deployment_name} failed: non-zero return code"
E       AssertionError: Wait for rollout of sklearn-default-0-classifier failed: non-zero return code

seldon_e2e_utils.py:140: AssertionError
____________________ TestPrepack.test_text_alibi_explainer _____________________
[gw1] linux -- Python 3.7.10 /opt/conda/bin/python

self = <test_prepackaged_servers.TestPrepack object at 0x7f636d674610>
namespace = 'test-text-alibi-explainer'

    def test_text_alibi_explainer(self, namespace):
        spec = "../resources/movies-text-explainer.yaml"
        retry_run(f"kubectl apply -f {spec} -n {namespace}")
        wait_for_status("movie", namespace)
>       wait_for_rollout("movie", namespace, expected_deployments=2)

test_prepackaged_servers.py:207: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
seldon_e2e_utils.py:161: in wait_for_rollout
    wait_for_deployment(deployment_name, namespace, attempts, sleep)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

deployment_name = 'movie-movies-predictor-0-classifier'
namespace = 'test-text-alibi-explainer', attempts = 50, sleep = 5

    def wait_for_deployment(deployment_name, namespace, attempts=50, sleep=5):
        logging.info(f"Waiting for deployment {deployment_name}")
        for _ in range(attempts):
            ret = run(
                f"kubectl rollout status -n {namespace} deploy/{deployment_name}",
                shell=True,
            )
            if ret.returncode == 0:
                logging.info(f"Successfully waited for deployment {deployment_name}")
                break
            logging.warning(f"Unsuccessful wait command but retrying for {deployment_name}")
            time.sleep(sleep)
        assert (
            ret.returncode == 0
>       ), f"Wait for rollout of {deployment_name} failed: non-zero return code"
E       AssertionError: Wait for rollout of movie-movies-predictor-0-classifier failed: non-zero return code

seldon_e2e_utils.py:140: AssertionError
___________________________ TestPrepack.test_sklearn ___________________________
[gw1] linux -- Python 3.7.10 /opt/conda/bin/python

self = <test_prepackaged_servers.TestPrepack object at 0x7f636d6d3f90>
namespace = 'test-sklearn'

    def test_sklearn(self, namespace):
        spec = "../../servers/sklearnserver/samples/iris.yaml"
        retry_run(f"kubectl apply -f {spec} -n {namespace}")
        wait_for_status("sklearn", namespace)
>       wait_for_rollout("sklearn", namespace)

test_prepackaged_servers.py:30: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
seldon_e2e_utils.py:161: in wait_for_rollout
    wait_for_deployment(deployment_name, namespace, attempts, sleep)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

deployment_name = 'sklearn-default-0-classifier', namespace = 'test-sklearn'
attempts = 50, sleep = 5

    def wait_for_deployment(deployment_name, namespace, attempts=50, sleep=5):
        logging.info(f"Waiting for deployment {deployment_name}")
        for _ in range(attempts):
            ret = run(
                f"kubectl rollout status -n {namespace} deploy/{deployment_name}",
                shell=True,
            )
            if ret.returncode == 0:
                logging.info(f"Successfully waited for deployment {deployment_name}")
                break
            logging.warning(f"Unsuccessful wait command but retrying for {deployment_name}")
            time.sleep(sleep)
        assert (
            ret.returncode == 0
>       ), f"Wait for rollout of {deployment_name} failed: non-zero return code"
E       AssertionError: Wait for rollout of sklearn-default-0-classifier failed: non-zero return code

seldon_e2e_utils.py:140: AssertionError
______________________ TestBatchWorker.test_batch_worker _______________________
[gw0] linux -- Python 3.7.10 /opt/conda/bin/python

self = <test_batch_processor.TestBatchWorker object at 0x7f7145b52650>
namespace = 'test-batch-worker'

    def test_batch_worker(self, namespace):
        spec = "../../servers/sklearnserver/samples/iris.yaml"
        retry_run(f"kubectl apply -f {spec} -n {namespace}")
        wait_for_status("sklearn", namespace)
>       wait_for_rollout("sklearn", namespace)

test_batch_processor.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
seldon_e2e_utils.py:161: in wait_for_rollout
    wait_for_deployment(deployment_name, namespace, attempts, sleep)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

deployment_name = 'sklearn-default-0-classifier'
namespace = 'test-batch-worker', attempts = 50, sleep = 5

    def wait_for_deployment(deployment_name, namespace, attempts=50, sleep=5):
        logging.info(f"Waiting for deployment {deployment_name}")
        for _ in range(attempts):
            ret = run(
                f"kubectl rollout status -n {namespace} deploy/{deployment_name}",
                shell=True,
            )
            if ret.returncode == 0:
                logging.info(f"Successfully waited for deployment {deployment_name}")
                break
            logging.warning(f"Unsuccessful wait command but retrying for {deployment_name}")
            time.sleep(sleep)
        assert (
            ret.returncode == 0
>       ), f"Wait for rollout of {deployment_name} failed: non-zero return code"
E       AssertionError: Wait for rollout of sklearn-default-0-classifier failed: non-zero return code

seldon_e2e_utils.py:140: AssertionError

@sakoush
Copy link
Member Author

sakoush commented Jul 27, 2021

/retest

@sakoush
Copy link
Member Author

sakoush commented Jul 27, 2021

/test integration

@sakoush
Copy link
Member Author

sakoush commented Jul 27, 2021

/test notebooks

1 similar comment
@sakoush
Copy link
Member Author

sakoush commented Jul 28, 2021

/test notebooks

@RafalSkolasinski
Copy link
Contributor

The failure is on TestNotebooks.test_custom_metrics so most likely flakiness 👍

@adriangonz
Copy link
Contributor

Thanks for the heads up @RafalSkolasinski! In that case, is it alright to merge this one?

@RafalSkolasinski
Copy link
Contributor

Should be alright but the new tests should finish soon so we can wait a few minutes and see

@RafalSkolasinski
Copy link
Contributor

/test notebooks

@sakoush sakoush merged commit 6229ab9 into SeldonIO:master Jul 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor env var retrieval for model_name / image_name in python wrapper so it's centralised in util
4 participants