Skip to content

Commit 166d0b4

Browse files
Merge pull request #314 from NCAS-CMS/anon_bucket
Anon bucket
2 parents 685cf6f + dd24476 commit 166d0b4

4 files changed

Lines changed: 109 additions & 24 deletions

File tree

activestorage/active.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -519,21 +519,22 @@ def _from_storage(self, ds, indexer, chunks, out_shape, out_dtype,
519519
# Create a shared session object.
520520
if self.interface_type == "s3" and self._version == 2:
521521
if self.storage_options is not None:
522-
key, secret = None, None
523522
if self.storage_options.get("anon", None) is True:
524523
print("Reductionist session for Anon S3 bucket.")
525524
session = reductionist.get_session(
526525
None, None, S3_ACTIVE_STORAGE_CACERT)
527-
if "key" in self.storage_options:
528-
key = self.storage_options["key"]
529-
if "secret" in self.storage_options:
530-
secret = self.storage_options["secret"]
531-
if key and secret:
532-
session = reductionist.get_session(
533-
key, secret, S3_ACTIVE_STORAGE_CACERT)
534526
else:
535-
session = reductionist.get_session(
536-
S3_ACCESS_KEY, S3_SECRET_KEY, S3_ACTIVE_STORAGE_CACERT)
527+
key, secret = None, None
528+
if "key" in self.storage_options:
529+
key = self.storage_options["key"]
530+
if "secret" in self.storage_options:
531+
secret = self.storage_options["secret"]
532+
if key and secret:
533+
session = reductionist.get_session(
534+
key, secret, S3_ACTIVE_STORAGE_CACERT)
535+
else:
536+
session = reductionist.get_session(
537+
S3_ACCESS_KEY, S3_SECRET_KEY, S3_ACTIVE_STORAGE_CACERT)
537538
else:
538539
session = reductionist.get_session(S3_ACCESS_KEY,
539540
S3_SECRET_KEY,

activestorage/reductionist.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,10 @@ def get_session(username: str, password: str,
2424
:returns: a client session object.
2525
"""
2626
session = requests.Session()
27-
# TODO Stack-HPC
28-
# we need to allow Anon buckets. though this
29-
# will break connection to data server
30-
# if username is None and password is None:
31-
# return session
32-
session.auth = (username, password)
3327
session.verify = cacert or False
28+
if username is None and password is None:
29+
return session
30+
session.auth = (username, password)
3431
return session
3532

3633

@@ -261,8 +258,13 @@ def __init__(self, status_code, error):
261258

262259
def decode_and_raise_error(response):
263260
"""Decode an error response and raise ReductionistError."""
264-
try:
265-
error = json.dumps(response.json())
261+
if response.status_code == http.client.INTERNAL_SERVER_ERROR:
262+
try:
263+
error = json.dumps(response.json())
264+
except requests.exceptions.JSONDecodeError as exc:
265+
error = http.client.responses.get(response.status_code, "-")
266+
raise ReductionistError(response.status_code, error) from exc
266267
raise ReductionistError(response.status_code, error)
267-
except requests.exceptions.JSONDecodeError as exc:
268-
raise ReductionistError(response.status_code, "-") from exc
268+
269+
error = http.client.responses.get(response.status_code, "-")
270+
raise ReductionistError(response.status_code, error)

tests/test_real_s3.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55

66
from activestorage.active import Active, load_from_s3
7-
from activestorage.reductionist import ReductionistError
87

98

109
S3_BUCKET = "bnl"
@@ -32,9 +31,54 @@ def test_anon_s3():
3231
active_storage_url=active_storage_url,
3332
option_disable_chunk_cache=True)
3433
active._version = 2
35-
with pytest.raises(ReductionistError):
34+
result = active.min()[:]
35+
assert result[0][0][0] == 197.69595
36+
37+
38+
def test_anon_s3_invalid_file():
39+
"""Test a very basic but real S3 ANON access."""
40+
active_storage_url = "https://reductionist.jasmin.ac.uk/" # Wacasoft
41+
bigger_file = "CMIP6-test3.nc" # not a file
42+
43+
test_file_uri = os.path.join(
44+
"esmvaltool-zarr",
45+
bigger_file
46+
)
47+
48+
active = Active(test_file_uri, 'tas',
49+
storage_options={
50+
"anon": True,
51+
'client_kwargs': {
52+
'endpoint_url': "https://uor-aces-o.s3-ext.jc.rl.ac.uk"
53+
}
54+
},
55+
active_storage_url=active_storage_url)
56+
active._version = 2
57+
with pytest.raises(FileNotFoundError) as red_err:
58+
result = active.min()[:]
59+
60+
61+
def test_anon_s3_bucket_not_anon():
62+
"""Test a very basic but real S3 ANON access."""
63+
active_storage_url = "https://reductionist.jasmin.ac.uk/" # Wacasoft
64+
bigger_file = "CMIP6-test.nc" # not a file
65+
66+
test_file_uri = os.path.join(
67+
"repacking",
68+
bigger_file
69+
)
70+
71+
active = Active(test_file_uri, 'tas',
72+
storage_options={
73+
"anon": True,
74+
'client_kwargs': {
75+
'endpoint_url': "https://uor-aces-o.s3-ext.jc.rl.ac.uk"
76+
}
77+
},
78+
active_storage_url=active_storage_url)
79+
active._version = 2
80+
with pytest.raises(PermissionError) as red_err:
3681
result = active.min()[:]
37-
assert result == 197.69595
3882

3983

4084
def test_s3_small_file():

tests/unit/test_reductionist.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,42 @@ def test_reduce_chunk_not_found(mock_request):
299299
operation)
300300

301301
print("Not found exc from reductionist", str(exc.value))
302-
assert str(exc.value) == 'Reductionist error: HTTP 404: -'
302+
assert str(exc.value) == 'Reductionist error: HTTP 404: Not Found'
303+
304+
305+
@mock.patch.object(reductionist, 'request')
306+
def test_reductionist_internal_server_error_json(mock_request):
307+
"""Unit test for Reductionist 500 JSON response."""
308+
response = requests.Response()
309+
response.status_code = 500
310+
response._content = b'{"detail": "backend exploded"}'
311+
response.headers["Content-Type"] = "application/json"
312+
mock_request.return_value = response
313+
314+
active_url = "https://s3.example.com"
315+
access_key = "fake-access"
316+
secret_key = "fake-secret"
317+
cacert = None
318+
s3_url = "https://active.example.com"
319+
offset = 2
320+
size = 128
321+
compression = None
322+
filters = None
323+
missing = []
324+
dtype = np.dtype("int32")
325+
shape = (32, )
326+
axis = (0, )
327+
order = "C"
328+
chunk_selection = [slice(0, 2, 1)]
329+
operation = "min"
330+
331+
session = reductionist.get_session(access_key, secret_key, cacert)
332+
with pytest.raises(reductionist.ReductionistError) as exc:
333+
reductionist.reduce_chunk(session, active_url, s3_url,
334+
offset, size, compression, filters, missing,
335+
dtype, shape, order, chunk_selection, axis,
336+
operation)
337+
338+
assert (
339+
str(exc.value) == 'Reductionist error: HTTP 500: {"detail": "backend exploded"}'
340+
)

0 commit comments

Comments
 (0)