Skip to content

Commit 843ee36

Browse files
mount the dedicated storage for each function (#1408)
* add mount /function_data space
1 parent b9b4f85 commit 843ee36

File tree

7 files changed

+182
-21
lines changed

7 files changed

+182
-21
lines changed

charts/qiskit-serverless/charts/gateway/templates/rayclustertemplate.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ data:
119119
- mountPath: /data
120120
name: user-storage
121121
subPath: {{`{{ user_id }}`}}
122+
- mountPath: /function_data
123+
name: user-storage
124+
subPath: {{`{{ function_data }}`}}
122125
env:
123126
# Environment variables for Ray TLS authentication.
124127
# See https://docs.ray.io/en/latest/ray-core/configure.html#tls-authentication for more details.
@@ -352,6 +355,9 @@ data:
352355
- mountPath: /data
353356
name: user-storage
354357
subPath: {{`{{ user_id }}`}}
358+
- mountPath: /function_data
359+
name: user-storage
360+
subPath: {{`{{ function_data }}`}}
355361
{{- if .Values.useCertManager }}
356362
- mountPath: /tmp/tls
357363
name: cert-tls

client/qiskit_serverless/core/client.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -475,22 +475,25 @@ def upload(self, program: QiskitFunction):
475475
def get_jobs(self, **kwargs) -> List[Job]:
476476
return self._job_client.list(**kwargs)
477477

478-
def files(self) -> List[str]:
479-
return self._files_client.list()
478+
def files(self, provider: Optional[str] = None) -> List[str]:
479+
return self._files_client.list(provider)
480480

481481
def file_download(
482482
self,
483483
file: str,
484484
target_name: Optional[str] = None,
485485
download_location: str = "./",
486+
provider: Optional[str] = None,
486487
):
487-
return self._files_client.download(file, download_location, target_name)
488+
return self._files_client.download(
489+
file, download_location, target_name, provider
490+
)
488491

489-
def file_delete(self, file: str):
490-
return self._files_client.delete(file)
492+
def file_delete(self, file: str, provider: Optional[str] = None):
493+
return self._files_client.delete(file, provider)
491494

492-
def file_upload(self, file: str):
493-
return self._files_client.upload(file)
495+
def file_upload(self, file: str, provider: Optional[str] = None):
496+
return self._files_client.upload(file, provider)
494497

495498
def list(self, **kwargs) -> List[QiskitFunction]:
496499
"""Returns list of available programs."""

client/qiskit_serverless/core/files.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,18 @@ def __init__(self, host: str, token: str, version: str):
5353
self._token = token
5454

5555
def download(
56-
self, file: str, download_location: str, target_name: Optional[str] = None
56+
self,
57+
file: str,
58+
download_location: str,
59+
target_name: Optional[str] = None,
60+
provider: Optional[str] = None,
5761
) -> Optional[str]:
5862
"""Downloads file."""
5963
tracer = trace.get_tracer("client.tracer")
6064
with tracer.start_as_current_span("files.download"):
6165
with requests.get(
6266
f"{self.host}/api/{self.version}/files/download/",
63-
params={"file": file},
67+
params={"file": file, "provider": provider},
6468
stream=True,
6569
headers={"Authorization": f"Bearer {self._token}"},
6670
timeout=REQUESTS_TIMEOUT,
@@ -80,14 +84,15 @@ def download(
8084
progress_bar.close()
8185
return file_name
8286

83-
def upload(self, file: str) -> Optional[str]:
87+
def upload(self, file: str, provider: Optional[str] = None) -> Optional[str]:
8488
"""Uploads file."""
8589
tracer = trace.get_tracer("client.tracer")
8690
with tracer.start_as_current_span("files.upload"):
8791
with open(file, "rb") as f:
8892
with requests.post(
8993
f"{self.host}/api/{self.version}/files/upload/",
9094
files={"file": f},
95+
data={"provider": provider},
9196
stream=True,
9297
headers={"Authorization": f"Bearer {self._token}"},
9398
timeout=REQUESTS_TIMEOUT,
@@ -97,27 +102,28 @@ def upload(self, file: str) -> Optional[str]:
97102
return "Upload failed"
98103
return "Can not open file"
99104

100-
def list(self) -> List[str]:
105+
def list(self, provider: Optional[str] = None) -> List[str]:
101106
"""Returns list of available files to download produced by programs,"""
102107
tracer = trace.get_tracer("client.tracer")
103108
with tracer.start_as_current_span("files.list"):
104109
response_data = safe_json_request(
105110
request=lambda: requests.get(
106111
f"{self.host}/api/{self.version}/files/",
112+
params={"provider": provider},
107113
headers={"Authorization": f"Bearer {self._token}"},
108114
timeout=REQUESTS_TIMEOUT,
109115
)
110116
)
111117
return response_data.get("results", [])
112118

113-
def delete(self, file: str) -> Optional[str]:
119+
def delete(self, file: str, provider: Optional[str] = None) -> Optional[str]:
114120
"""Deletes file uploaded or produced by the programs,"""
115121
tracer = trace.get_tracer("client.tracer")
116122
with tracer.start_as_current_span("files.delete"):
117123
response_data = safe_json_request(
118124
request=lambda: requests.delete(
119125
f"{self.host}/api/{self.version}/files/delete/",
120-
data={"file": file},
126+
data={"file": file, "provider": provider},
121127
headers={
122128
"Authorization": f"Bearer {self._token}",
123129
"format": "json",

gateway/api/ray.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def submit_job(job: Job) -> Job:
201201
return job
202202

203203

204-
def create_ray_cluster(
204+
def create_ray_cluster( # pylint: disable=too-many-branches
205205
job: Job,
206206
cluster_name: Optional[str] = None,
207207
cluster_data: Optional[str] = None,
@@ -250,14 +250,18 @@ def create_ray_cluster(
250250
node_image = settings.RAY_NODE_IMAGE
251251

252252
# if user specified image use specified image
253+
function_data = user.username
253254
if job.program.image is not None:
254255
node_image = job.program.image
256+
if job.program.provider.name:
257+
function_data = job.program.provider.name
255258

256259
cluster = get_template("rayclustertemplate.yaml")
257260
manifest = cluster.render(
258261
{
259262
"cluster_name": cluster_name,
260263
"user_id": user.username,
264+
"function_data": function_data,
261265
"node_image": node_image,
262266
"workers": job_config.workers,
263267
"min_workers": job_config.min_workers,

gateway/api/views.py

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
Program,
4040
Job,
4141
RuntimeJob,
42+
Provider,
4243
)
4344
from .ray import get_job_handler
4445
from .serializers import (
@@ -580,15 +581,39 @@ class FilesViewSet(viewsets.ViewSet):
580581

581582
BASE_NAME = "files"
582583

584+
def list_user_providers(self, user):
585+
"""list provider names that the user in"""
586+
provider_list = []
587+
providers = Provider.objects.all()
588+
for instance in providers:
589+
if instance.admin_group in user.groups.all():
590+
provider_list.append(instance.name)
591+
return provider_list
592+
593+
def check_user_has_provider(self, user, provider_name):
594+
"""check if user has the provider"""
595+
return provider_name in self.list_user_providers(user)
596+
583597
def list(self, request):
584598
"""List of available for user files."""
599+
response = Response(
600+
{"message": "Requested file was not found."},
601+
status=status.HTTP_404_NOT_FOUND,
602+
)
585603
files = []
586604
tracer = trace.get_tracer("gateway.tracer")
587605
ctx = TraceContextTextMapPropagator().extract(carrier=request.headers)
588606
with tracer.start_as_current_span("gateway.files.list", context=ctx):
607+
user_dir = request.user.username
608+
provider_name = request.query_params.get("provider")
609+
if provider_name is not None:
610+
if self.check_user_has_provider(request.user, provider_name):
611+
user_dir = provider_name
612+
else:
613+
return response
589614
user_dir = os.path.join(
590615
sanitize_file_path(settings.MEDIA_ROOT),
591-
sanitize_file_path(request.user.username),
616+
sanitize_file_path(user_dir),
592617
)
593618
if os.path.exists(user_dir):
594619
files = [
@@ -615,17 +640,23 @@ def download(self, request): # pylint: disable=invalid-name
615640
ctx = TraceContextTextMapPropagator().extract(carrier=request.headers)
616641
with tracer.start_as_current_span("gateway.files.download", context=ctx):
617642
requested_file_name = request.query_params.get("file")
643+
provider_name = request.query_params.get("provider")
618644
if requested_file_name is not None:
645+
user_dir = request.user.username
646+
if provider_name is not None:
647+
if self.check_user_has_provider(request.user, provider_name):
648+
user_dir = provider_name
649+
else:
650+
return response
619651
# look for file in user's folder
620652
filename = os.path.basename(requested_file_name)
621653
user_dir = os.path.join(
622654
sanitize_file_path(settings.MEDIA_ROOT),
623-
sanitize_file_path(request.user.username),
655+
sanitize_file_path(user_dir),
624656
)
625657
file_path = os.path.join(
626658
sanitize_file_path(user_dir), sanitize_file_path(filename)
627659
)
628-
629660
if os.path.exists(user_dir) and os.path.exists(file_path) and filename:
630661
chunk_size = 8192
631662
# note: we do not use with statements as Streaming response closing file itself.
@@ -656,14 +687,20 @@ def delete(self, request): # pylint: disable=invalid-name
656687
if request.data and "file" in request.data:
657688
# look for file in user's folder
658689
filename = os.path.basename(request.data["file"])
690+
provider_name = request.data.get("provider")
691+
user_dir = request.user.username
692+
if provider_name is not None:
693+
if self.check_user_has_provider(request.user, provider_name):
694+
user_dir = provider_name
695+
else:
696+
return response
659697
user_dir = os.path.join(
660698
sanitize_file_path(settings.MEDIA_ROOT),
661-
sanitize_file_path(request.user.username),
699+
sanitize_file_path(user_dir),
662700
)
663701
file_path = os.path.join(
664702
sanitize_file_path(user_dir), sanitize_file_path(filename)
665703
)
666-
667704
if os.path.exists(user_dir) and os.path.exists(file_path) and filename:
668705
os.remove(file_path)
669706
response = Response(
@@ -675,20 +712,32 @@ def delete(self, request): # pylint: disable=invalid-name
675712
@action(methods=["POST"], detail=False)
676713
def upload(self, request): # pylint: disable=invalid-name
677714
"""Upload selected file."""
715+
response = Response(
716+
{"message": "Requested file was not found."},
717+
status=status.HTTP_404_NOT_FOUND,
718+
)
678719
tracer = trace.get_tracer("gateway.tracer")
679720
ctx = TraceContextTextMapPropagator().extract(carrier=request.headers)
680721
with tracer.start_as_current_span("gateway.files.download", context=ctx):
681722
upload_file = request.FILES["file"]
682723
filename = os.path.basename(upload_file.name)
724+
user_dir = request.user.username
725+
if request.data and "provider" in request.data:
726+
provider_name = request.data["provider"]
727+
if provider_name is not None:
728+
if self.check_user_has_provider(request.user, provider_name):
729+
user_dir = provider_name
730+
else:
731+
return response
683732
user_dir = os.path.join(
684733
sanitize_file_path(settings.MEDIA_ROOT),
685-
sanitize_file_path(request.user.username),
734+
sanitize_file_path(user_dir),
686735
)
687736
file_path = os.path.join(
688737
sanitize_file_path(user_dir), sanitize_file_path(filename)
689738
)
690739
with open(file_path, "wb+") as destination:
691740
for chunk in upload_file.chunks():
692741
destination.write(chunk)
693-
return Response({"message": file_path})
742+
return Response({"message": file_path})
694743
return Response("server error", status=status.HTTP_500_INTERNAL_SERVER_ERROR)

0 commit comments

Comments
 (0)