Skip to content

Commit ad89841

Browse files
authored
Merge pull request #78 from gogakoreli/main
refresh drives config credentials using a timer
2 parents 0fe34ee + b6ba60c commit ad89841

File tree

5 files changed

+110
-69
lines changed

5 files changed

+110
-69
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
pip uninstall -y "jupyter_drives" jupyterlab
5454
5555
- name: Upload extension packages
56-
uses: actions/upload-artifact@v3
56+
uses: actions/upload-artifact@v4
5757
with:
5858
name: extension-artifacts
5959
path: dist/jupyter_drives*
@@ -69,7 +69,7 @@ jobs:
6969
with:
7070
python-version: '3.9'
7171
architecture: 'x64'
72-
- uses: actions/download-artifact@v3
72+
- uses: actions/download-artifact@v4
7373
with:
7474
name: extension-artifacts
7575
- name: Install and Test
@@ -105,7 +105,7 @@ jobs:
105105
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
106106

107107
- name: Download extension package
108-
uses: actions/download-artifact@v3
108+
uses: actions/download-artifact@v4
109109
with:
110110
name: extension-artifacts
111111

@@ -139,7 +139,7 @@ jobs:
139139
140140
- name: Upload Playwright Test report
141141
if: always()
142-
uses: actions/upload-artifact@v3
142+
uses: actions/upload-artifact@v4
143143
with:
144144
name: jupyter_drives-playwright-tests
145145
path: |

.github/workflows/check-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
token: ${{ secrets.GITHUB_TOKEN }}
2121

2222
- name: Upload Distributions
23-
uses: actions/upload-artifact@v3
23+
uses: actions/upload-artifact@v4
2424
with:
2525
name: jupyter_drives-releaser-dist-${{ github.run_number }}
2626
path: .jupyter_releaser_checkout/dist

jupyter_drives/base.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,12 @@ def set_default_api_base_url(self):
7373

7474
def __init__(self, **kwargs):
7575
super().__init__(**kwargs)
76-
self._load_credentials()
77-
78-
def _load_credentials(self):
7976
# check if credentials were already set in jupyter_notebook_config.py
80-
if self.access_key_id is not None and self.secret_access_key is not None:
77+
self.credentials_already_set = self.access_key_id is not None and self.secret_access_key is not None
78+
self.load_credentials()
79+
80+
def load_credentials(self):
81+
if self.credentials_already_set:
8182
return
8283

8384
# automatically extract credentials for S3 drives

jupyter_drives/manager.py

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@
2525

2626
import re
2727

28+
from tornado.ioloop import PeriodicCallback
29+
2830
# constant used as suffix to deal with directory objects
2931
EMPTY_DIR_SUFFIX = '/.jupyter_drives_fix_dir'
3032

33+
# 15 minutes
34+
CREDENTIALS_REFRESH = 15 * 60 * 1000
35+
3136
class JupyterDrivesManager():
3237
"""
3338
Jupyter-drives manager class.
@@ -46,21 +51,12 @@ def __init__(self, config: traitlets.config.Config) -> None:
4651
self._client = httpx.AsyncClient()
4752
self._content_managers = {}
4853
self._max_files_listed = 1025
54+
self._drives = None
4955

5056
# instate fsspec file system
5157
self._file_system = fsspec.filesystem(self._config.provider, asynchronous=True)
5258

53-
# initiate aiobotocore session if we are dealing with S3 drives
54-
if self._config.provider == 's3':
55-
if self._config.access_key_id and self._config.secret_access_key:
56-
self._s3_clients = {}
57-
self._s3_session = get_session()
58-
self._file_system = s3fs.S3FileSystem(anon=False, asynchronous=True, key=self._config.access_key_id, secret=self._config.secret_access_key, token=self._config.session_token)
59-
else:
60-
raise tornado.web.HTTPError(
61-
status_code= httpx.codes.BAD_REQUEST,
62-
reason="No credentials specified. Please set them in your user jupyter_server_config file.",
63-
)
59+
self._initialize_credentials_refresh()
6460

6561
@property
6662
def base_api_url(self) -> str:
@@ -81,6 +77,83 @@ def per_page_argument(self) -> Optional[Tuple[str, int]]:
8177
"""
8278
return ("per_page", 100)
8379

80+
def _initialize_credentials_refresh(self):
81+
self._drives_refresh_callback()
82+
if not self._config.credentials_already_set:
83+
self._drives_refresh_timer = PeriodicCallback(
84+
self._drives_refresh_callback, CREDENTIALS_REFRESH
85+
)
86+
self._drives_refresh_timer.start()
87+
88+
def _drives_refresh_callback(self):
89+
self._config.load_credentials()
90+
self._initialize_s3_file_system()
91+
self._initialize_drives()
92+
self._initialize_content_managers()
93+
94+
def _initialize_s3_file_system(self):
95+
# initiate aiobotocore session if we are dealing with S3 drives
96+
if self._config.provider == 's3':
97+
if self._config.access_key_id and self._config.secret_access_key:
98+
self._s3_session = get_session()
99+
self._file_system = s3fs.S3FileSystem(
100+
anon=False,
101+
asynchronous=True,
102+
key=self._config.access_key_id,
103+
secret=self._config.secret_access_key,
104+
token=self._config.session_token,
105+
)
106+
else:
107+
raise tornado.web.HTTPError(
108+
status_code=httpx.codes.BAD_REQUEST,
109+
reason="No credentials specified. Please set them in your user jupyter_server_config file.",
110+
)
111+
112+
def _initialize_drives(self):
113+
if self._config.provider == "s3":
114+
S3Drive = get_driver(Provider.S3)
115+
self._drives = [S3Drive(self._config.access_key_id, self._config.secret_access_key, True, None, None, None, self._config.session_token)]
116+
elif self._config.provider == 'gcs':
117+
GCSDrive = get_driver(Provider.GOOGLE_STORAGE)
118+
self._drives = [GCSDrive(self._config.access_key_id, self._config.secret_access_key)] # verfiy credentials needed
119+
120+
def _initialize_content_managers(self):
121+
for drive_name, content_manager in self._content_managers.items():
122+
self._initialize_content_manager(drive_name, content_manager["provider"], content_manager["location"])
123+
124+
def _initialize_content_manager(self, drive_name, provider, region=None):
125+
try:
126+
if provider == 's3':
127+
if self._config.session_token is None:
128+
configuration = {
129+
"aws_access_key_id": self._config.access_key_id,
130+
"aws_secret_access_key": self._config.secret_access_key,
131+
"aws_region": region,
132+
}
133+
else:
134+
configuration = {
135+
"aws_access_key_id": self._config.access_key_id,
136+
"aws_secret_access_key": self._config.secret_access_key,
137+
"aws_session_token": self._config.session_token,
138+
"aws_region": region,
139+
}
140+
store = obs.store.S3Store.from_url("s3://" + drive_name + "/", config = configuration)
141+
elif provider == 'gcs':
142+
store = obs.store.GCSStore.from_url("gs://" + drive_name + "/", config = {}) # add gcs config
143+
elif provider == 'http':
144+
store = obs.store.HTTPStore.from_url(drive_name, client_options = {}) # add http client config
145+
146+
self._content_managers[drive_name] = {
147+
"store": store,
148+
"location": region,
149+
"provider": provider,
150+
}
151+
except Exception as e:
152+
raise tornado.web.HTTPError(
153+
status_code=httpx.codes.BAD_REQUEST,
154+
reason=f"The following error occured when initializing the content manager: {e}",
155+
)
156+
84157
def set_listing_limit(self, new_limit):
85158
"""Set new limit for listing.
86159
@@ -105,23 +178,21 @@ async def list_drives(self):
105178
"""
106179
data = []
107180
if self._config.access_key_id and self._config.secret_access_key:
108-
if self._config.provider == "s3":
109-
S3Drive = get_driver(Provider.S3)
110-
drives = [S3Drive(self._config.access_key_id, self._config.secret_access_key, True, None, None, None, self._config.session_token)]
111-
112-
elif self._config.provider == 'gcs':
113-
GCSDrive = get_driver(Provider.GOOGLE_STORAGE)
114-
drives = [GCSDrive(self._config.access_key_id, self._config.secret_access_key)] # verfiy credentials needed
115-
116-
else:
181+
if self._drives is None:
117182
raise tornado.web.HTTPError(
118183
status_code= httpx.codes.NOT_IMPLEMENTED,
119184
reason="Listing drives not supported for given provider.",
120185
)
121186

122187
results = []
123-
for drive in drives:
124-
results += drive.list_containers()
188+
for drive in self._drives:
189+
try:
190+
results += drive.list_containers()
191+
except Exception as e:
192+
raise tornado.web.HTTPError(
193+
status_code=httpx.codes.BAD_REQUEST,
194+
reason=f"The following error occured when listing drives: {e}",
195+
)
125196

126197
for result in results:
127198
data.append(
@@ -150,42 +221,10 @@ async def mount_drive(self, drive_name, provider):
150221
Args:
151222
drive_name: name of drive to mount
152223
"""
153-
try:
154-
# check if content manager doesn't already exist
155-
if drive_name not in self._content_managers or self._content_managers[drive_name] is None:
156-
if provider == 's3':
157-
# get region of drive
158-
region = await self._get_drive_location(drive_name)
159-
if self._config.session_token is None:
160-
configuration = {
161-
"aws_access_key_id": self._config.access_key_id,
162-
"aws_secret_access_key": self._config.secret_access_key,
163-
"aws_region": region
164-
}
165-
else:
166-
configuration = {
167-
"aws_access_key_id": self._config.access_key_id,
168-
"aws_secret_access_key": self._config.secret_access_key,
169-
"aws_session_token": self._config.session_token,
170-
"aws_region": region
171-
}
172-
store = obs.store.S3Store.from_url("s3://" + drive_name + "/", config = configuration)
173-
elif provider == 'gcs':
174-
store = obs.store.GCSStore.from_url("gs://" + drive_name + "/", config = {}) # add gcs config
175-
elif provider == 'http':
176-
store = obs.store.HTTPStore.from_url(drive_name, client_options = {}) # add http client config
177-
178-
self._content_managers[drive_name] = {
179-
"store": store,
180-
"location": region
181-
}
182-
183-
else:
184-
raise tornado.web.HTTPError(
185-
status_code= httpx.codes.CONFLICT,
186-
reason= "Drive already mounted."
187-
)
188-
224+
try:
225+
if provider == 's3':
226+
region = await self._get_drive_location(drive_name)
227+
self._initialize_content_manager(drive_name, provider, region)
189228
except Exception as e:
190229
raise tornado.web.HTTPError(
191230
status_code= httpx.codes.BAD_REQUEST,

src/contents.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ export class Drive implements Contents.IDrive {
230230
// when accessed the first time, mount drive
231231
if (currentDrive.mounted === false) {
232232
try {
233-
await mountDrive(localPath, {
233+
const driveName = currentDrive.name;
234+
await mountDrive(driveName, {
234235
provider: currentDrive.provider
235236
});
236-
this._drivesList.filter(x => x.name === localPath)[0].mounted = true;
237+
this._drivesList.filter(x => x.name === driveName)[0].mounted = true;
237238
} catch (e) {
238239
// it will give an error if drive is already mounted
239240
}

0 commit comments

Comments
 (0)