Skip to content

Commit 27e862a

Browse files
committed
file storage
1 parent 9e84d37 commit 27e862a

File tree

4 files changed

+94
-5
lines changed

4 files changed

+94
-5
lines changed

docker-compose.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
version: '3'
22

33
volumes:
4+
simple:
45
packages:
56
vault:
67

@@ -85,6 +86,7 @@ services:
8586
- ./htmlcov:/opt/warehouse/src/htmlcov:z
8687
- .coveragerc:/opt/warehouse/src/.coveragerc:z
8788
- packages:/var/opt/warehouse/packages
89+
- simple:/var/opt/warehouse/simple
8890
ports:
8991
- "80:8000"
9092

@@ -95,6 +97,7 @@ services:
9597
command: python -m http.server 9001
9698
volumes:
9799
- packages:/var/opt/warehouse/packages
100+
- simple:/var/opt/warehouse/simple
98101
ports:
99102
- "9001:9001"
100103

@@ -110,6 +113,7 @@ services:
110113
environment:
111114
C_FORCE_ROOT: "1"
112115
FILES_BACKEND: "warehouse.packaging.services.LocalFileStorage path=/var/opt/warehouse/packages/ url=http://files:9001/packages/{path}"
116+
SIMPLE_BACKEND: "warehouse.packaging.services.LocalFileStorage path=/var/opt/warehouse/simple/ url=http://files:9001/simple/{path}"
113117

114118
static:
115119
build:

warehouse/packaging/interfaces.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ def store(path, file_path, *, meta=None):
3434
"""
3535

3636

37+
class ISimpleStorage(Interface):
38+
def create_service(context, request):
39+
"""
40+
Create the service, given the context and request for which it is being
41+
created for, passing a name for settings.
42+
"""
43+
44+
def get(path):
45+
"""
46+
Return a file like object that can be read to access the file located
47+
at the given path.
48+
"""
49+
50+
def store(path, file_path, *, meta=None):
51+
"""
52+
Save the file located at file_path to the file storage at the location
53+
specified by path. An additional meta keyword argument may contain
54+
extra information that an implementation may or may not store.
55+
"""
56+
57+
3758
class IDocsStorage(Interface):
3859
def create_service(context, request):
3960
"""

warehouse/packaging/services.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from zope.interface import implementer
2222

23-
from warehouse.packaging.interfaces import IDocsStorage, IFileStorage
23+
from warehouse.packaging.interfaces import IDocsStorage, IFileStorage, ISimpleStorage
2424

2525

2626
class InsecureStorageWarning(UserWarning):
@@ -137,6 +137,37 @@ def store(self, path, file_path, *, meta=None):
137137
self.bucket.upload_file(file_path, path, ExtraArgs=extra_args)
138138

139139

140+
@implementer(ISimpleStorage)
141+
class S3SimpleStorage(GenericFileStorage):
142+
@classmethod
143+
def create_service(cls, context, request):
144+
session = request.find_service(name="aws.session")
145+
s3 = session.resource("s3")
146+
bucket = s3.Bucket(request.registry.settings["files.bucket"])
147+
prefix = request.registry.settings.get("files.prefix")
148+
return cls(bucket, prefix=prefix)
149+
150+
def get(self, path):
151+
# Note: this is not actually used in production, instead our CDN is
152+
# configured to connect directly to our storage bucket. See:
153+
# https://github.com/python/pypi-infra/blob/master/terraform/file-hosting/vcl/main.vcl
154+
try:
155+
return self.bucket.Object(self._get_path(path)).get()["Body"]
156+
except botocore.exceptions.ClientError as exc:
157+
if exc.response["Error"]["Code"] != "NoSuchKey":
158+
raise
159+
raise FileNotFoundError("No such key: {!r}".format(path)) from None
160+
161+
def store(self, path, file_path, *, meta=None):
162+
extra_args = {}
163+
if meta is not None:
164+
extra_args["Metadata"] = meta
165+
166+
path = self._get_path(path)
167+
168+
self.bucket.upload_file(file_path, path, ExtraArgs=extra_args)
169+
170+
140171
@implementer(IDocsStorage)
141172
class S3DocsStorage:
142173
def __init__(self, s3_client, bucket_name, *, prefix=None):
@@ -203,3 +234,38 @@ def store(self, path, file_path, *, meta=None):
203234
if meta is not None:
204235
blob.metadata = meta
205236
blob.upload_from_filename(file_path)
237+
238+
239+
@implementer(ISimpleStorage)
240+
class GCSSimpleStorage(GenericFileStorage):
241+
@classmethod
242+
@google.api_core.retry.Retry(
243+
predicate=google.api_core.retry.if_exception_type(
244+
google.api_core.exceptions.ServiceUnavailable
245+
)
246+
)
247+
def create_service(cls, context, request):
248+
storage_client = request.find_service(name="gcloud.gcs")
249+
bucket_name = request.registry.settings["files.bucket"]
250+
bucket = storage_client.get_bucket(bucket_name)
251+
prefix = request.registry.settings.get("files.prefix")
252+
253+
return cls(bucket, prefix=prefix)
254+
255+
def get(self, path):
256+
# Note: this is not actually used in production, instead our CDN is
257+
# configured to connect directly to our storage bucket. See:
258+
# https://github.com/python/pypi-infra/blob/master/terraform/file-hosting/vcl/main.vcl
259+
raise NotImplementedError
260+
261+
@google.api_core.retry.Retry(
262+
predicate=google.api_core.retry.if_exception_type(
263+
google.api_core.exceptions.ServiceUnavailable
264+
)
265+
)
266+
def store(self, path, file_path, *, meta=None):
267+
path = self._get_path(path)
268+
blob = self.bucket.blob(path)
269+
if meta is not None:
270+
blob.metadata = meta
271+
blob.upload_from_filename(file_path)

warehouse/packaging/utils.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import hashlib
2-
import os.path
32

43
from pyramid_jinja2 import IJinja2Environment
54

6-
import warehouse
7-
85
from warehouse.legacy.api.simple import _simple_detail
96

107

@@ -22,7 +19,8 @@ def render_simple_detail(project, request, store=False):
2219

2320
if store:
2421
# TODO: Store generated file in FileStorage
25-
# We should probably configure a new FileStorage for a new simple-files bucket in GCS
22+
# We should probably configure a new FileStorage
23+
# for a new simple-files bucket in GCS
2624
pass
2725

2826
return (content_hash, simple_detail_path)

0 commit comments

Comments
 (0)