Skip to content

Commit 364ba2d

Browse files
authored
chore: add test for ZarrParser on bucket without list permissions (#919)
1 parent 2e8887b commit 364ba2d

3 files changed

Lines changed: 95 additions & 3 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ check-docs = { cmd = "mkdocs build --strict" }
228228
readthedocs = { cmd = "rm -rf $READTHEDOCS_OUTPUT/html && cp -r site $READTHEDOCS_OUTPUT/html" }
229229
# Define commands to run within the docs environment
230230
[tool.pixi.feature.minio.tasks]
231-
run-tests = { cmd = "pytest virtualizarr/tests/test_manifests/test_store.py virtualizarr/tests/test_parsers/test_hdf/test_hdf_manifest_store.py --run-minio-tests --run-network-tests --verbose" }
232-
run-tests-xml-cov = { cmd = "pytest virtualizarr/tests/test_manifests/test_store.py virtualizarr/tests/test_parsers/test_hdf/test_hdf_manifest_store.py --run-minio-tests --run-network-tests --verbose --cov-report=xml" }
231+
run-tests = { cmd = "pytest virtualizarr/tests/test_manifests/test_store.py virtualizarr/tests/test_parsers/test_hdf/test_hdf_manifest_store.py virtualizarr/tests/test_parsers/test_zarr.py --run-minio-tests --run-network-tests --verbose" }
232+
run-tests-xml-cov = { cmd = "pytest virtualizarr/tests/test_manifests/test_store.py virtualizarr/tests/test_parsers/test_hdf/test_hdf_manifest_store.py virtualizarr/tests/test_parsers/test_zarr.py --run-minio-tests --run-network-tests --verbose --cov-report=xml" }
233233

234234
[tool.setuptools_scm]
235235
fallback_version = "9999"

virtualizarr/tests/conftest.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,46 @@ def minio_bucket(container):
7979
"file": filename,
8080
"client": client,
8181
}
82+
83+
84+
@pytest.fixture(scope="session")
85+
def minio_nolist_bucket(container):
86+
"""Create a MinIO bucket whose anonymous policy allows Get but NOT List."""
87+
from minio import Minio
88+
89+
bucket = "nolist-bucket"
90+
client = Minio(
91+
"localhost:9000",
92+
access_key=container["username"],
93+
secret_key=container["password"],
94+
secure=False,
95+
)
96+
client.make_bucket(bucket)
97+
policy = {
98+
"Version": "2012-10-17",
99+
"Statement": [
100+
{
101+
"Effect": "Allow",
102+
"Principal": {"AWS": "*"},
103+
"Action": ["s3:GetBucketLocation"],
104+
"Resource": f"arn:aws:s3:::{bucket}",
105+
},
106+
{
107+
"Effect": "Allow",
108+
"Principal": {"AWS": "*"},
109+
"Action": [
110+
"s3:GetObject",
111+
],
112+
"Resource": f"arn:aws:s3:::{bucket}/*",
113+
},
114+
],
115+
}
116+
client.set_bucket_policy(bucket, json.dumps(policy))
117+
yield {
118+
"port": container["port"],
119+
"endpoint": container["endpoint"],
120+
"username": container["username"],
121+
"password": container["password"],
122+
"bucket": bucket,
123+
"client": client,
124+
}

virtualizarr/tests/test_parsers/test_zarr.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
get_strategy,
2121
join_url,
2222
)
23-
from virtualizarr.tests import requires_pyarrow
23+
from virtualizarr.tests import requires_minio, requires_pyarrow
2424

2525
pytestmark = requires_pyarrow
2626

@@ -545,3 +545,52 @@ def test_sharded_array_raises_error(tmpdir):
545545
match="Zarr V3 arrays with sharding are not yet supported",
546546
):
547547
parser(url=filepath, registry=registry)
548+
549+
550+
@requires_minio
551+
@pytest.mark.xfail(
552+
reason="ZarrParser does not yet support buckets without list permissions"
553+
)
554+
def test_zarr_parser_nolist_bucket(minio_nolist_bucket):
555+
"""Test that ZarrParser works with a bucket that does not allow list operations."""
556+
import obstore as obs
557+
558+
bucket = minio_nolist_bucket["bucket"]
559+
endpoint = minio_nolist_bucket["endpoint"]
560+
username = minio_nolist_bucket["username"]
561+
password = minio_nolist_bucket["password"]
562+
563+
# Write a Zarr V3 store directly to the bucket using admin credentials
564+
admin_store = obs.store.S3Store(
565+
bucket,
566+
endpoint_url=endpoint,
567+
access_key_id=username,
568+
secret_access_key=password,
569+
virtual_hosted_style_request=False,
570+
client_options={"allow_http": True},
571+
)
572+
zarr_store = zarr.storage.ObjectStore(store=admin_store)
573+
ds = xr.Dataset(
574+
{"data": (("x", "y"), np.arange(12, dtype="float32").reshape(3, 4))},
575+
coords={"x": np.arange(3), "y": np.arange(4)},
576+
)
577+
ds.to_zarr(zarr_store, consolidated=False, zarr_format=3)
578+
579+
# Create an anonymous S3 store (subject to bucket policy which denies list)
580+
anon_store = obs.store.S3Store(
581+
bucket,
582+
endpoint_url=endpoint,
583+
skip_signature=True,
584+
virtual_hosted_style_request=False,
585+
client_options={"allow_http": True},
586+
)
587+
588+
url = f"s3://{bucket}"
589+
registry = ObjectStoreRegistry({url: anon_store})
590+
parser = ZarrParser()
591+
manifeststore = parser(url=url, registry=registry)
592+
593+
with xr.open_dataset(
594+
manifeststore, engine="zarr", consolidated=False, zarr_format=3
595+
) as actual:
596+
xr.testing.assert_identical(actual, ds)

0 commit comments

Comments
 (0)