Skip to content
This repository was archived by the owner on Jun 16, 2021. It is now read-only.

Commit 09b94c5

Browse files
committed
Update with async management support
1 parent bb2919c commit 09b94c5

File tree

7 files changed

+23
-22
lines changed

7 files changed

+23
-22
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
language: python
22

33
python:
4+
- 3.8
5+
- 3.7
46
- 3.6
57
- 3.5
6-
- 3.4
78

89
install:
910
- pip install --upgrade setuptools pip flake8 pytest-cov codecov pytest-console-scripts

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ The Kubernetes Kernel Provider package provides support necessary for launching
1313
pip install kubenetes_kernel_provider
1414
```
1515

16-
##Usage
16+
## Usage
1717
Because this version of Jupyter kernel management is still in its experimental stages, a [special branch of Notebook](https://github.com/takluyver/notebook/tree/jupyter-kernel-mgmt) is required, which includes the machinery to leverage the new framework. An installable build of this branch is available as an asset on the [interim-dev release](https://github.com/gateway-experiments/remote_kernel_provider/releases/tag/v0.1-interim-dev) of the Remote Kernel Provider on which Kubernetes Kernel Provider depends.
1818

1919
### Kubernetes Kernel Specifications

kubernetes_kernel_provider/k8s.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
enterprise_gateway_namespace = os.environ.get('EG_NAMESPACE', 'default')
2020
default_kernel_service_account_name = os.environ.get('EG_DEFAULT_KERNEL_SERVICE_ACCOUNT_NAME', 'default')
2121
kernel_cluster_role = os.environ.get('EG_KERNEL_CLUSTER_ROLE', 'cluster-admin')
22-
share_gateway_namespace = bool(os.environ.get('EG_SHARED_NAMESPACE', 'False').lower() == 'true')
22+
23+
# TODO: The default for this value should probably flip for single-user/Notebook scenarios (True) vs.
24+
# multi-tenant/Gateway scenarios (False). The app config will be available from `kernel_manager.app_config`
25+
# so we should be able to move this into constructor (or after) and infer from that information.
26+
shared_namespace = bool(os.environ.get('EG_SHARED_NAMESPACE', 'False').lower() == 'true')
2327

2428
config.load_incluster_config()
2529

@@ -33,7 +37,7 @@ def __init__(self, kernel_manager, lifecycle_config):
3337
self.kernel_namespace = None
3438
self.delete_kernel_namespace = False
3539

36-
def launch_process(self, kernel_cmd, **kwargs):
40+
async def launch_process(self, kernel_cmd, **kwargs):
3741
"""Launches the specified process within a Kubernetes environment."""
3842
# Set env before superclass call so we see these in the debug output
3943

@@ -43,7 +47,7 @@ def launch_process(self, kernel_cmd, **kwargs):
4347
self.kernel_pod_name = self._determine_kernel_pod_name(**kwargs)
4448
self.kernel_namespace = self._determine_kernel_namespace(**kwargs) # will create namespace if not provided
4549

46-
return super(KubernetesKernelLifecycleManager, self).launch_process(kernel_cmd, **kwargs)
50+
return await super(KubernetesKernelLifecycleManager, self).launch_process(kernel_cmd, **kwargs)
4751

4852
def get_initial_states(self):
4953
"""Return list of states indicating container is starting (includes running)."""
@@ -146,7 +150,7 @@ def _determine_kernel_namespace(self, **kwargs):
146150
namespace = kwargs['env'].get('KERNEL_NAMESPACE')
147151
if namespace is None:
148152
# check if share gateway namespace is configured...
149-
if share_gateway_namespace: # if so, set to EG namespace
153+
if shared_namespace: # if so, set to EG namespace
150154
namespace = enterprise_gateway_namespace
151155
self.log.warning("Shared namespace has been configured. All kernels will reside in EG namespace: {}".
152156
format(namespace))

kubernetes_kernel_provider/provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Provides support for launching and managing kernels within a Kubernetes cluster."""
2+
import asyncio
23
import os
34
from datetime import datetime
45
from kubernetes import config
@@ -17,6 +18,7 @@ class KubernetesKernelProvider(RemoteKernelProviderBase):
1718
kernel_file = 'k8skp_kernel.json'
1819
lifecycle_manager_classes = ['kubernetes_kernel_provider.k8s.KubernetesKernelLifecycleManager']
1920

21+
@asyncio.coroutine
2022
def find_kernels(self):
2123
""" Ensures the provider is running within a Kubernetes cluster. If not, it will
2224
log a warning message and no kernelspecs will be returned. Since find_kernels()

kubernetes_kernel_provider/tests/test_kernelspec_app.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from tempfile import mkdtemp
1111

1212

13-
@pytest.fixture(scope="module")
13+
@pytest.fixture()
1414
def mock_kernels_dir():
1515
kernels_dir = mkdtemp(prefix="kernels_")
1616
orig_data_dir = os.environ.get("JUPYTER_DATA_DIR")
@@ -23,55 +23,48 @@ def mock_kernels_dir():
2323
os.environ.pop("JUPYTER_DATA_DIR")
2424

2525

26-
@pytest.mark.script_launch_mode('subprocess')
2726
def test_no_opts(script_runner):
2827
ret = script_runner.run('jupyter-k8s-kernelspec')
2928
assert ret.success is False
3029
assert ret.stdout.startswith("No subcommand specified.")
3130
assert ret.stderr == ''
3231

3332

34-
@pytest.mark.script_launch_mode('subprocess')
3533
def test_bad_subcommand(script_runner):
3634
ret = script_runner.run('jupyter', 'k8s-kernelspec', 'bogus-subcommand')
3735
assert ret.success is False
3836
assert ret.stdout.startswith("No subcommand specified.")
3937
assert ret.stderr == ''
4038

4139

42-
@pytest.mark.script_launch_mode('subprocess')
4340
def test_help_all(script_runner):
4441
ret = script_runner.run('jupyter-k8s-kernelspec', 'install', '--help-all')
4542
assert ret.success
4643
assert ret.stdout.startswith("A Jupyter kernel for use within a Kubernetes cluster")
4744
assert ret.stderr == ''
4845

4946

50-
@pytest.mark.script_launch_mode('subprocess')
5147
def test_bad_argument(script_runner):
5248
ret = script_runner.run('jupyter-k8s-kernelspec', 'install', '--bogus-argument')
5349
assert ret.success is False
5450
assert ret.stdout.startswith("A Jupyter kernel for use within a Kubernetes cluster")
5551
assert "[K8SKP_SpecInstaller] CRITICAL | Unrecognized flag: \'--bogus-argument\'" in ret.stderr
5652

5753

58-
@pytest.mark.script_launch_mode('subprocess')
5954
def test_mutually_exclusive(script_runner):
6055
ret = script_runner.run('jupyter-k8s-kernelspec', 'install', '--spark', '--tensorflow')
6156
assert ret.success is False
6257
assert ret.stdout == ''
6358
assert "[K8SKP_SpecInstaller] ERROR | Tensorflow support is mutually exclusive with Spark support." in ret.stderr
6459

6560

66-
@pytest.mark.script_launch_mode('subprocess')
6761
def test_bad_language(script_runner):
6862
ret = script_runner.run('jupyter-k8s-kernelspec', 'install', '--language=R', '--tensorflow')
6963
assert ret.success is False
7064
assert ret.stdout == ''
7165
assert "[K8SKP_SpecInstaller] ERROR | Tensorflow support is only available for use by Python kernels." in ret.stderr
7266

7367

74-
@pytest.mark.script_launch_mode('subprocess')
7568
def test_create_kernelspec(script_runner, mock_kernels_dir):
7669
my_env = os.environ.copy()
7770
my_env.update({"JUPYTER_DATA_DIR": mock_kernels_dir})
@@ -93,7 +86,6 @@ def test_create_kernelspec(script_runner, mock_kernels_dir):
9386
'elyra/kernel-spark-py:dev'
9487

9588

96-
@pytest.mark.script_launch_mode('subprocess')
9789
def test_create_python_kernelspec(script_runner, mock_kernels_dir):
9890
my_env = os.environ.copy()
9991
my_env.update({"JUPYTER_DATA_DIR": mock_kernels_dir})
@@ -120,7 +112,6 @@ def test_create_python_kernelspec(script_runner, mock_kernels_dir):
120112
assert kernel_json["metadata"]["lifecycle_manager"]["config"]["image_name"] == 'foo/bar:zed'
121113

122114

123-
@pytest.mark.script_launch_mode('subprocess')
124115
def test_create_r_kernelspec(script_runner, mock_kernels_dir):
125116
my_env = os.environ.copy()
126117
my_env.update({"JUPYTER_DATA_DIR": mock_kernels_dir})
@@ -142,7 +133,6 @@ def test_create_r_kernelspec(script_runner, mock_kernels_dir):
142133
assert argv[len(argv) - 1] == 'lazy'
143134

144135

145-
@pytest.mark.script_launch_mode('subprocess')
146136
def test_create_scala_kernelspec(script_runner, mock_kernels_dir):
147137
my_env = os.environ.copy()
148138
my_env.update({"JUPYTER_DATA_DIR": mock_kernels_dir})
@@ -163,7 +153,6 @@ def test_create_scala_kernelspec(script_runner, mock_kernels_dir):
163153
assert '--MyExtraSparkOpts' in kernel_json["env"]["__TOREE_SPARK_OPTS__"]
164154

165155

166-
@pytest.mark.script_launch_mode('subprocess')
167156
def test_create_tensorflow_kernelspec(script_runner, mock_kernels_dir):
168157
my_env = os.environ.copy()
169158
my_env.update({"JUPYTER_DATA_DIR": mock_kernels_dir})

pytest.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[pytest]
2+
markers =
3+
script_launch_mode
4+
5+
script_launch_mode = subprocess

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ def run(self):
6363
install_requires = [
6464
'entrypoints',
6565
'kubernetes>=4.0.0',
66-
'jupyter_kernel_mgmt>=0.4.0',
67-
'remote_kernel_provider>=0.2.0',
66+
'jupyter_kernel_mgmt>=0.5.0',
67+
'remote_kernel_provider>=0.3.0',
6868
],
6969
extras_require = {
70-
'test': ['mock', 'pytest'],
70+
'test': ['mock', 'pytest', 'pytest-console-scripts'],
7171
},
7272
entry_points={
7373
'console_scripts': [
@@ -77,7 +77,7 @@ def run(self):
7777
'k8skp = kubernetes_kernel_provider.provider:KubernetesKernelProvider',
7878
]
7979
},
80-
python_requires = ">=3.4",
80+
python_requires = ">=3.5",
8181
cmdclass = {
8282
'bdist_egg': bdist_egg if 'bdist_egg' in sys.argv else bdist_egg_disabled,
8383
},

0 commit comments

Comments
 (0)