Skip to content

Commit fd79de0

Browse files
smirenthomasf
authored andcommitted
Minio 7+ adaptation implementation
1 parent 1dfc810 commit fd79de0

File tree

10 files changed

+76
-83
lines changed

10 files changed

+76
-83
lines changed

minio_storage/errors.py

+4-16
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,10 @@ def __init__(self, msg, cause):
99

1010
reraise = {}
1111
for v in (
12-
merr.APINotImplemented,
13-
merr.AccessDenied,
14-
merr.AccountProblem,
15-
merr.CredentialNotSupported,
16-
merr.CrossLocationLoggingProhibited,
17-
merr.ExpiredToken,
18-
merr.InvalidAccessKeyId,
19-
merr.InvalidAddressingHeader,
20-
merr.InvalidBucketError,
21-
merr.InvalidBucketName,
22-
merr.InvalidDigest,
23-
merr.InvalidEncryptionAlgorithmError,
24-
merr.InvalidEndpointError,
25-
merr.InvalidSecurity,
26-
merr.InvalidToken,
27-
merr.NoSuchBucket,
12+
merr.MinioException,
13+
merr.InvalidResponseError,
14+
merr.ServerError,
15+
merr.S3Error,
2816
):
2917
reraise[v] = {"err": v}
3018

minio_storage/management/commands/minio.py

+14-12
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def bucket_list(
167167
summary: bool = True,
168168
):
169169
try:
170-
objs = storage.client.list_objects_v2(
170+
objs = storage.client.list_objects(
171171
bucket_name, prefix=prefix, recursive=recursive
172172
)
173173

@@ -200,39 +200,41 @@ def fmt(o):
200200

201201
if summary:
202202
print(f"{n_files} files and {n_dirs} directories", file=sys.stderr)
203-
except minio.error.NoSuchBucket:
203+
except minio.error.S3Error:
204204
raise CommandError(f"bucket {bucket_name} does not exist")
205205

206206
def bucket_create(self, storage, bucket_name):
207207
try:
208208
storage.client.make_bucket(bucket_name)
209209
print(f"created bucket: {bucket_name}", file=sys.stderr)
210-
except minio.error.BucketAlreadyOwnedByYou:
210+
except minio.error.S3Error:
211211
raise CommandError(f"you have already created {bucket_name}")
212212
return
213213

214214
def bucket_delete(self, storage, bucket_name):
215215
try:
216216
storage.client.remove_bucket(bucket_name)
217-
except minio.error.NoSuchBucket:
218-
raise CommandError(f"bucket {bucket_name} does not exist")
219-
except minio.error.BucketNotEmpty:
220-
raise CommandError(f"bucket {bucket_name} is not empty")
217+
except minio.error.S3Error as err:
218+
if err.code == 'BucketNotEmpty':
219+
raise CommandError(f"bucket {bucket_name} is not empty")
220+
elif err.code == 'NoSuchBucket':
221+
raise CommandError(f"bucket {bucket_name} does not exist")
221222

222223
def policy_get(self, storage, bucket_name):
223224
try:
224225
policy = storage.client.get_bucket_policy(bucket_name)
225226
policy = json.loads(policy)
226227
policy = json.dumps(policy, ensure_ascii=False, indent=2)
227228
return policy
228-
except minio.error.NoSuchBucket:
229-
raise CommandError(f"bucket {bucket_name} does not exist")
230-
except minio.error.NoSuchBucketPolicy:
231-
raise CommandError(f"bucket {bucket_name} has no policy")
229+
except minio.error.S3Error as err:
230+
if err.code == 'NoSuchBucket':
231+
raise CommandError(f"bucket {bucket_name} does not exist")
232+
elif err.code == 'NoSuchBucketPolicy':
233+
raise CommandError(f"bucket {bucket_name} has no policy")
232234

233235
def policy_set(self, storage, bucket_name, policy: Policy):
234236
try:
235237
policy = Policy(policy)
236238
storage.client.set_bucket_policy(bucket_name, policy.bucket(bucket_name))
237-
except minio.error.NoSuchBucket as e:
239+
except minio.error.S3Error as e:
238240
raise CommandError(e.message)

minio_storage/storage.py

+37-35
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import typing as T
55
import urllib
66
from logging import getLogger
7-
from time import mktime
87
from urllib.parse import urlsplit, urlunsplit
98

109
import minio
@@ -14,7 +13,6 @@
1413
from django.core.files.storage import Storage
1514
from django.utils import timezone
1615
from django.utils.deconstruct import deconstructible
17-
from minio.helpers import get_target_url
1816

1917
from minio_storage.errors import minio_error
2018
from minio_storage.files import ReadOnlySpooledTemporaryFile
@@ -109,15 +107,16 @@ def _create_base_url_client(client: minio.Minio, bucket_name: str, base_url: str
109107
base_url_parts = urlsplit(base_url)
110108

111109
# Clone from the normal client, but with base_url as the endpoint
110+
credentials = client._provider.retrieve()
112111
base_url_client = minio.Minio(
113112
base_url_parts.netloc,
114-
access_key=client._access_key,
115-
secret_key=client._secret_key,
116-
session_token=client._session_token,
113+
access_key=credentials.access_key,
114+
secret_key=credentials.secret_key,
115+
session_token=credentials.session_token,
117116
secure=base_url_parts.scheme == "https",
118117
# The bucket region may be auto-detected by client (via an HTTP
119118
# request), so don't just use client._region
120-
region=client._get_bucket_region(bucket_name),
119+
region=client._get_region(bucket_name, None),
121120
http_client=client._http,
122121
)
123122
if hasattr(client, "_credentials"):
@@ -151,7 +150,7 @@ def _examine_file(self, name, content):
151150
def _open(self, name, mode="rb"):
152151
try:
153152
f = self.file_class(self._sanitize_path(name), mode, self)
154-
except merr.MinioError as e:
153+
except merr.MinioException as e:
155154
raise minio_error("File {} could not be saved: {}".format(name, str(e)), e)
156155
return f
157156

@@ -169,14 +168,14 @@ def _save(self, name: str, content: bytes) -> str:
169168
metadata=self.object_metadata,
170169
)
171170
return sane_name
172-
except merr.ResponseError as error:
171+
except merr.InvalidResponseError as error:
173172
raise minio_error(f"File {name} could not be saved", error)
174173

175174
def delete(self, name: str) -> None:
176175
if self.backup_format and self.backup_bucket:
177176
try:
178177
obj = self.client.get_object(self.bucket_name, name)
179-
except merr.ResponseError as error:
178+
except merr.InvalidResponseError as error:
180179
raise minio_error(
181180
"Could not obtain file {} " "to make a copy of it".format(name),
182181
error,
@@ -195,7 +194,7 @@ def delete(self, name: str) -> None:
195194
self.client.put_object(
196195
self.backup_bucket, target_name, obj, content_length
197196
)
198-
except merr.ResponseError as error:
197+
except merr.InvalidResponseError as error:
199198
raise minio_error(
200199
"Could not make a copy of file "
201200
"{} before removing it".format(name),
@@ -204,22 +203,22 @@ def delete(self, name: str) -> None:
204203

205204
try:
206205
self.client.remove_object(self.bucket_name, name)
207-
except merr.ResponseError as error:
206+
except merr.InvalidResponseError as error:
208207
raise minio_error(f"Could not remove file {name}", error)
209208

210209
def exists(self, name: str) -> bool:
211210
try:
212211
self.client.stat_object(self.bucket_name, self._sanitize_path(name))
213212
return True
214-
except merr.ResponseError as error:
213+
except merr.InvalidResponseError as error:
215214
# TODO - deprecate
216215
if error.code == "NoSuchKey":
217216
return False
218217
else:
219218
raise minio_error(f"Could not stat file {name}", error)
220-
except merr.NoSuchKey:
219+
except merr.S3Error:
221220
return False
222-
except merr.NoSuchBucket:
221+
except merr.S3Error:
223222
raise
224223
except Exception as error:
225224
logger.error(error)
@@ -242,24 +241,24 @@ def listdir(self, path: str) -> T.Tuple[T.List, T.List]:
242241
dirs: T.List[str] = []
243242
files: T.List[str] = []
244243
try:
245-
objects = self.client.list_objects_v2(self.bucket_name, prefix=path)
244+
objects = self.client.list_objects(self.bucket_name, prefix=path)
246245
for o in objects:
247246
p = posixpath.relpath(o.object_name, path)
248247
if o.is_dir:
249248
dirs.append(p)
250249
else:
251250
files.append(p)
252251
return dirs, files
253-
except merr.NoSuchBucket:
252+
except merr.S3Error:
254253
raise
255-
except merr.ResponseError as error:
254+
except merr.InvalidResponseError as error:
256255
raise minio_error(f"Could not list directory {path}", error)
257256

258257
def size(self, name: str) -> int:
259258
try:
260259
info = self.client.stat_object(self.bucket_name, name)
261260
return info.size
262-
except merr.ResponseError as error:
261+
except merr.InvalidResponseError as error:
263262
raise minio_error(f"Could not access file size for {name}", error)
264263

265264
def _presigned_url(
@@ -279,7 +278,7 @@ def _presigned_url(
279278
# It's assumed that self.base_url will contain bucket information,
280279
# which could be different, so remove the bucket_name component (with 1
281280
# extra character for the leading "/") from the generated URL
282-
url_key_path = url_parts.path[len(self.bucket_name) + 1 :]
281+
url_key_path = url_parts.path[len(self.bucket_name) + 1:]
283282

284283
# Prefix the URL with any path content from base_url
285284
new_url_path = base_url_parts.path + url_key_path
@@ -302,30 +301,33 @@ def url(
302301
if self.presign_urls:
303302
url = self._presigned_url(name, max_age=max_age)
304303
else:
305-
if self.base_url is not None:
304+
def strip_beg(path):
305+
while path.startswith("/"):
306+
path = path[1:]
307+
return path
306308

307-
def strip_beg(path):
308-
while path.startswith("/"):
309-
path = path[1:]
310-
return path
309+
def strip_end(path):
310+
while path.endswith("/"):
311+
path = path[:-1]
312+
return path
311313

312-
def strip_end(path):
313-
while path.endswith("/"):
314-
path = path[:-1]
315-
return path
314+
if self.base_url is not None:
316315

317316
url = "{}/{}".format(
318317
strip_end(self.base_url), urllib.parse.quote(strip_beg(name))
319318
)
320319
else:
321-
url = get_target_url(
322-
self.client._endpoint_url,
323-
bucket_name=self.bucket_name,
324-
object_name=name,
325-
# bucket_region=region,
320+
url = "{}/{}/{}".format(
321+
strip_end(self.endpoint_url),
322+
self.bucket_name,
323+
urllib.parse.quote(strip_beg(name)),
326324
)
327325
return url
328326

327+
@property
328+
def endpoint_url(self):
329+
return self.client._base_url._url.geturl()
330+
329331
def accessed_time(self, name: str) -> datetime.datetime:
330332
"""
331333
Not available via the S3 API
@@ -341,8 +343,8 @@ def created_time(self, name: str) -> datetime.datetime:
341343
def modified_time(self, name: str) -> datetime.datetime:
342344
try:
343345
info = self.client.stat_object(self.bucket_name, name)
344-
return datetime.datetime.fromtimestamp(mktime(info.last_modified))
345-
except merr.ResponseError as error:
346+
return info.last_modified
347+
except merr.InvalidResponseError as error:
346348
raise minio_error(
347349
f"Could not access modification time for file {name}", error
348350
)

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"minio_storage/management/commands/",
2121
],
2222
setup_requires=["setuptools_scm"],
23-
install_requires=["django>=1.11", "minio>=4.0.21,<7"],
23+
install_requires=["django>=1.11", "minio>=7.1.12"],
2424
extras_require={"test": ["coverage", "requests"]},
2525
classifiers=[
2626
"Development Status :: 4 - Beta",

tests/test_app/tests/bucket_tests.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def pretty(v):
7777
)
7878
def test_auto_create_no_policy(self):
7979
ms = MinioMediaStorage()
80-
with self.assertRaises(minio.error.NoSuchBucketPolicy):
80+
with self.assertRaises(minio.error.S3Error):
8181
ms.client.get_bucket_policy(ms.bucket_name)
8282

8383
@override_settings(
@@ -96,7 +96,7 @@ def test_media_policy_auto_true(self):
9696
url = ms.url(fn)
9797
self.assertEqual(requests.get(url).status_code, 200)
9898
self.assertEqual(
99-
requests.get(f"{ms.client._endpoint_url}/{ms.bucket_name}").status_code, 403
99+
requests.get(f"{ms.endpoint_url}/{ms.bucket_name}").status_code, 403
100100
)
101101

102102
@override_settings(
@@ -114,7 +114,7 @@ def test_media_policy_get(self):
114114
self.assertEqual(ms.open(fn).read(), b"test")
115115
self.assertEqual(requests.get(ms.url(fn)).status_code, 200)
116116
self.assertEqual(
117-
requests.get(f"{ms.client._endpoint_url}/{ms.bucket_name}").status_code, 403
117+
requests.get(f"{ms.endpoint_url}/{ms.bucket_name}").status_code, 403
118118
)
119119

120120
@override_settings(
@@ -132,7 +132,7 @@ def test_media_policy_write(self):
132132
self.assertEqual(ms.open(fn).read(), b"test")
133133
self.assertEqual(requests.get(ms.url(fn)).status_code, 403)
134134
self.assertEqual(
135-
requests.get(f"{ms.client._endpoint_url}/{ms.bucket_name}").status_code, 403
135+
requests.get(f"{ms.endpoint_url}/{ms.bucket_name}").status_code, 403
136136
)
137137

138138
@override_settings(

tests/test_app/tests/managementcommand_tests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_management_command(self):
3131

3232
try:
3333
self.obliterate_bucket(self.media_storage.bucket_name)
34-
except minio.error.NoSuchBucket:
34+
except minio.error.S3Error:
3535
pass
3636

3737
with self.assertRaisesRegex(CommandError, f"bucket {bucket} does not exist"):

tests/test_app/tests/retrieve_tests.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django.core.files.base import ContentFile
88
from django.test import TestCase, override_settings
99
from freezegun import freeze_time
10-
from minio.error import NoSuchKey
10+
from minio.error import S3Error
1111

1212
from minio_storage.errors import MinIOError
1313
from minio_storage.storage import MinioMediaStorage
@@ -44,7 +44,7 @@ def test_file_size(self):
4444
def test_size_of_non_existent_throws(self):
4545
test_file = self.media_storage.save("sizetest.txt", ContentFile(b"1234"))
4646
self.media_storage.delete(test_file)
47-
with self.assertRaises(NoSuchKey):
47+
with self.assertRaises(S3Error):
4848
self.media_storage.size(test_file)
4949

5050
def test_modified_time(self):
@@ -63,7 +63,7 @@ def test_created_time(self):
6363
)
6464

6565
def test_modified_time_of_non_existent_throws(self):
66-
with self.assertRaises(NoSuchKey):
66+
with self.assertRaises(S3Error):
6767
self.media_storage.modified_time("nonexistent.jpg")
6868

6969
def _listdir_root(self, root):
@@ -128,7 +128,7 @@ def test_opening_non_existing_file_raises_minioerror(self):
128128
try:
129129
self.media_storage.open("this does not exist")
130130
except MinIOError as e:
131-
assert e.cause.__class__ == NoSuchKey
131+
assert e.cause.__class__ == S3Error
132132

133133
def test_file_names_are_properly_sanitized(self):
134134
self.media_storage.save("./meh22222.txt", io.BytesIO(b"stuff"))

0 commit comments

Comments
 (0)