|
20 | 20 |
|
21 | 21 | from zope.interface import implementer
|
22 | 22 |
|
23 |
| -from warehouse.packaging.interfaces import IDocsStorage, IFileStorage |
| 23 | +from warehouse.packaging.interfaces import IDocsStorage, IFileStorage, ISimpleStorage |
24 | 24 |
|
25 | 25 |
|
26 | 26 | class InsecureStorageWarning(UserWarning):
|
@@ -137,6 +137,37 @@ def store(self, path, file_path, *, meta=None):
|
137 | 137 | self.bucket.upload_file(file_path, path, ExtraArgs=extra_args)
|
138 | 138 |
|
139 | 139 |
|
| 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 | + |
140 | 171 | @implementer(IDocsStorage)
|
141 | 172 | class S3DocsStorage:
|
142 | 173 | def __init__(self, s3_client, bucket_name, *, prefix=None):
|
@@ -203,3 +234,38 @@ def store(self, path, file_path, *, meta=None):
|
203 | 234 | if meta is not None:
|
204 | 235 | blob.metadata = meta
|
205 | 236 | 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) |
0 commit comments