Skip to content

Commit ef2a970

Browse files
committed
switch to provider independent base class
1 parent ed558ba commit ef2a970

File tree

4 files changed

+91
-35
lines changed

4 files changed

+91
-35
lines changed

conftest.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ def drives_base_config():
1717

1818

1919
@pytest.fixture
20-
def drives_s3_config(drives_base_config):
20+
def drives_config(drives_base_config):
2121
return drives_base_config()
2222

2323

2424
@pytest.fixture
25-
def drives_s3_manager(drives_base_config):
26-
from .jupyter_drives.managers.s3 import S3Manager
25+
def drives_manager(drives_base_config):
26+
from .jupyter_drives.managers.manager import JupyterDrivesManager
2727

28-
return S3Manager(drives_base_config)
28+
return JupyterDrivesManager(drives_base_config)
2929

3030

3131
@pytest.fixture
32-
def drives_valid_s3_manager(drives_s3_manager):
33-
drives_s3_manager._config.access_key_id = "valid"
34-
drives_s3_manager._config.secret_access = "valid"
35-
return drives_s3_manager
32+
def drives_valid_manager(drives_manager):
33+
drives_manager._config.access_key_id = "valid"
34+
drives_manager._config.secret_access = "valid"
35+
return drives_manager

jupyter_drives/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ def _load_jupyter_server_extension(server_app):
4040
server_app.log.info(f"Registered {name} server extension")
4141

4242
# Entry points
43-
def get_s3_manager(config: "traitlets.config.Config") -> "jupyter_drives.managers.JupyterDrivesManager":
44-
"""S3 Manager factory"""
45-
from .managers.s3 import S3Manager
43+
def get_manager(config: "traitlets.config.Config") -> "jupyter_drives.managers.JupyterDrivesManager":
44+
"""Drives Manager factory"""
45+
from .managers.manager import JupyterDrivesManager
4646

47-
return S3Manager(config)
47+
return JupyterDrivesManager(config)

jupyter_drives/managers/manager.py

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import abc
21
import http
32
import json
43
import logging
54
from typing import Dict, List, Optional, Tuple, Union, Any
65

7-
import nbformat
86
import tornado
9-
import traitlets
107
import httpx
8+
import traitlets
119
from jupyter_server.utils import url_path_join
1210

11+
import obstore as obs
12+
from libcloud.storage.types import Provider
13+
from libcloud.storage.providers import get_driver
14+
1315
from ..log import get_logger
1416
from ..base import DrivesConfig
1517

1618
import re
1719

18-
class JupyterDrivesManager(abc.ABC):
20+
class JupyterDrivesManager():
1921
"""
20-
Abstract base class for jupyter-drives manager.
22+
Jupyter-drives manager class.
2123
2224
Args:
2325
config: Server extension configuration object
@@ -26,12 +28,12 @@ class JupyterDrivesManager(abc.ABC):
2628
2729
The manager will receive the global server configuration object;
2830
so it can add configuration parameters if needed.
29-
It needs them to extract the ``DrivesConfig`` from it to pass it to this
30-
parent class (see ``S3Manager`` for an example).
31+
It needs them to extract the ``DrivesConfig``.
3132
"""
32-
def __init__(self, config: DrivesConfig) -> None:
33-
self._config = config
33+
def __init__(self, config: traitlets.config.Config) -> None:
34+
self._config = DrivesConfig(config=config)
3435
self._client = httpx.AsyncClient()
36+
self._content_managers = {}
3537

3638
@property
3739
def base_api_url(self) -> str:
@@ -50,18 +52,47 @@ def per_page_argument(self) -> Optional[Tuple[str, int]]:
5052
[str, int]: (query argument name, value)
5153
None: the provider does not support pagination
5254
"""
53-
return None
55+
return ("per_page", 100)
5456

55-
@abc.abstractclassmethod
5657
async def list_drives(self):
5758
"""Get list of available drives.
5859
5960
Returns:
6061
List of available drives and their properties.
6162
"""
62-
raise NotImplementedError()
63+
data = []
64+
if self._config.access_key_id and self._config.secret_access_key:
65+
if self._config.provider == "s3":
66+
S3Drive = get_driver(Provider.S3)
67+
drives = [S3Drive(self._config.access_key_id, self._config.secret_access_key)]
68+
69+
results = []
70+
for drive in drives:
71+
results += drive.list_containers()
72+
73+
for result in results:
74+
data.append(
75+
{
76+
"name": result.name,
77+
"region": result.driver.region,
78+
"creation_date": result.extra["creation_date"],
79+
"status": "inactive",
80+
"provider": "S3"
81+
}
82+
)
83+
response = {
84+
"data": data,
85+
"code": 200
86+
}
87+
else:
88+
response = {"code": 400, "message": "No credentials specified. Please set them in your user jupyter_server_config file."}
89+
raise tornado.web.HTTPError(
90+
status_code= httpx.codes.BAD_REQUEST,
91+
reason="No credentials specified. Please set them in your user jupyter_server_config file.",
92+
)
93+
94+
return response
6395

64-
@abc.abstractclassmethod
6596
async def mount_drive(self, drive_name, **kwargs):
6697
"""Mount a drive.
6798
@@ -71,46 +102,71 @@ async def mount_drive(self, drive_name, **kwargs):
71102
Returns:
72103
The content manager for the drive.
73104
"""
74-
raise NotImplementedError()
105+
try:
106+
# check if content manager didn't already exist
107+
if drive_name not in self._content_managers or self._content_managers[drive_name] is None:
108+
if kwargs.provider == 's3':
109+
store = obs.store.S3Store.from_url("s3://" + drive_name + "/", config = {"aws_access_key_id": self._config.access_key_id, "aws_secret_access_key": self._config.secret_access_key, "aws_region": kwargs.drive_region})
110+
elif kwargs.provider == 'gcs':
111+
store = obs.store.GCSStore.from_url("gs://" + drive_name + "/", config = {}) # add gcs config
112+
elif kwargs.provider == 'http':
113+
store = obs.store.HTTPStore.from_url(drive_name, client_options = {}) # add http client config
114+
else:
115+
raise ValueError(f"Provider not supported: {kwargs.provider}")
116+
117+
self._content_managers[drive_name].store = store
118+
self._content_managers[drive_name].provider = kwargs.provider
119+
120+
return store
121+
# response = {
122+
# "content_manager": store,
123+
# "code": 201,
124+
# "message": "Drive succesfully mounted."
125+
# }
126+
# else:
127+
# response = {
128+
# "code": 409,
129+
# "message": "Drive already mounted."
130+
# }
131+
except Exception as e:
132+
raise ValueError("The following error occured when mouting the drive: {e}")
133+
134+
# return response
75135

76-
@abc.abstractclassmethod
77136
async def unmount_drive(self, drive_name: str, **kwargs):
78137
"""Unmount a drive.
79138
80139
Args:
81140
drive_name: name of drive to unmount
82141
"""
83-
raise NotImplementedError()
142+
print('Drive unmount function called.')
84143

85-
@abc.abstractclassmethod
86144
async def get_contents(self, drive_name, path, **kwargs):
87145
"""Get contents of a file or directory.
88146
89147
Args:
90148
drive_name: name of drive to get the contents of
91149
path: path to file or directory
92150
"""
93-
raise NotImplementedError()
151+
print('Get contents function called.')
94152

95-
@abc.abstractclassmethod
96153
async def new_file(self, drive_name, path, **kwargs):
97154
"""Create a new file or directory at the given path.
98155
99156
Args:
100157
drive_name: name of drive where the new content is created
101158
path: path where new content should be created
102159
"""
103-
raise NotImplementedError()
160+
print('New file function called.')
104161

105-
@abc.abstractclassmethod
106162
async def rename_file(self, drive_name, path, **kwargs):
107163
"""Rename a file.
108164
109165
Args:
110166
drive_name: name of drive where file is located
111167
path: path of file
112168
"""
113-
raise NotImplementedError()
169+
print('Rename file function called.')
114170

115171
async def _call_provider(
116172
self,

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ test = [
4343
]
4444

4545
[project.entry-points."jupyter_drives.manager_v1"]
46-
s3 = "jupyter_drives:get_s3_manager"
46+
s3 = "jupyter_drives:get_manager"
4747

4848
[tool.hatch.version]
4949
source = "nodejs"

0 commit comments

Comments
 (0)