Skip to content

Commit eb05e61

Browse files
Merge pull request #13 from qinguoyi/feat-minio
feat:add minio
2 parents 81c4978 + a770acc commit eb05e61

6 files changed

Lines changed: 173 additions & 2 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ OSS_ACCESS_KEY_ID=
6666
OSS_ACCESS_KEY_SECRET=
6767
```
6868

69+
### [MINIO](https://min.io/)
70+
71+
Usage:
72+
73+
```python
74+
client = StoreFactory.new_client(
75+
provider="MINIO", endpoint=<endpoint>, bucket=<bucket>
76+
)
77+
```
78+
79+
Required environment variables:
80+
81+
```yaml
82+
MINIO_ACCESS_KEY=
83+
MINIO_SECRET_KEY=
84+
```
85+
6986
## Development
7087

7188
Once you want to run the integration tests, you should have a `.env` file locally, similar to the `.env.example`.

omnistore/objstore/constant.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
OBJECT_STORE_OSS = "OSS"
2+
OBJECT_STORE_MINIO = "MINIO"

omnistore/objstore/minio.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import io
2+
import os
3+
from abc import ABC
4+
from pathlib import Path
5+
6+
import minio
7+
from minio import credentials, S3Error
8+
9+
from omnistore.objstore.objstore import ObjStore
10+
11+
12+
class MinIO(ObjStore):
13+
def __init__(self, endpoint: str, bucket: str):
14+
"""
15+
Construct a new client to communicate with the provider.
16+
"""
17+
auth = credentials.EnvMinioProvider()
18+
self.client = minio.Minio(endpoint, credentials=auth,secure=False)
19+
self.bucket_name = bucket
20+
21+
# Make sure the bucket exists
22+
if not self.client.bucket_exists(bucket):
23+
self.client.make_bucket(bucket)
24+
25+
def create_dir(self, dirname: str):
26+
if not dirname.endswith("/"):
27+
dirname += "/"
28+
empty_stream = io.BytesIO(b"")
29+
self.client.put_object(self.bucket_name, dirname, empty_stream, 0)
30+
31+
def delete_dir(self, dirname: str):
32+
if not dirname.endswith("/"):
33+
dirname += "/"
34+
objects = self.client.list_objects(
35+
self.bucket_name, prefix=dirname, recursive=True
36+
)
37+
for obj in objects:
38+
self.client.remove_object(self.bucket_name, obj.object_name)
39+
40+
def upload(self, src: str, dest: str):
41+
self.client.fput_object(self.bucket_name, dest, src)
42+
43+
def upload_dir(self, src_dir: str, dest_dir: str):
44+
for file in Path(src_dir).rglob("*"):
45+
if file.is_file():
46+
dest_path = f"{dest_dir}/{file.relative_to(src_dir)}"
47+
self.upload(str(file), dest_path)
48+
elif file.is_dir():
49+
self.create_dir(f"{dest_dir}/{file.relative_to(src_dir)}/")
50+
51+
def download(self, src: str, dest: str):
52+
self.client.fget_object(self.bucket_name, src, dest)
53+
54+
def download_dir(self, src_dir: str, dest_dir: str):
55+
if not src_dir.endswith("/"):
56+
src_dir += "/"
57+
path = Path(dest_dir)
58+
if not path.exists():
59+
path.mkdir(parents=True)
60+
objects = self.client.list_objects(
61+
self.bucket_name, prefix=src_dir, recursive=True
62+
)
63+
for obj in objects:
64+
file_path = Path(dest_dir, Path(obj.object_name).relative_to(src_dir))
65+
if not file_path.parent.exists():
66+
file_path.parent.mkdir(parents=True, exist_ok=True)
67+
if obj.object_name.endswith("/"):
68+
continue
69+
self.download(obj.object_name, str(file_path))
70+
71+
def delete(self, filename: str):
72+
self.client.remove_object(self.bucket_name, filename)
73+
74+
def exists(self, filename: str):
75+
try:
76+
self.client.stat_object(self.bucket_name, filename)
77+
return True
78+
except S3Error as e:
79+
if e.code == "NoSuchKey":
80+
return False
81+
else:
82+
raise e

omnistore/objstore/objstore_factory.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from omnistore.objstore.aliyun_oss import OSS
2+
from omnistore.objstore.constant import OBJECT_STORE_OSS, OBJECT_STORE_MINIO
3+
from omnistore.objstore.minio import MinIO
24
from omnistore.store import Store
35

4-
OBJECT_STORE_OSS = "OSS"
5-
66

77
class StoreFactory:
88
ObjStores = {
99
OBJECT_STORE_OSS: OSS,
10+
OBJECT_STORE_MINIO: MinIO,
1011
}
1112

1213
@classmethod

tests/integration_tests/objstore/test_aliyun_oss.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import shutil
33

44
import pytest
5+
from dotenv import load_dotenv
56

67
from omnistore.objstore.objstore_factory import OBJECT_STORE_OSS, StoreFactory
78

9+
load_dotenv()
810

911
class TestOSS:
1012
@pytest.fixture(scope="module", autouse=True)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import os
2+
import shutil
3+
4+
import pytest
5+
from dotenv import load_dotenv
6+
7+
from omnistore.objstore import StoreFactory
8+
from omnistore.objstore.constant import OBJECT_STORE_MINIO
9+
10+
load_dotenv()
11+
12+
class TestMinio:
13+
@pytest.fixture(scope="module", autouse=True)
14+
def setup_and_teardown(self):
15+
print("Setting up the test environment.")
16+
try:
17+
os.makedirs("./test-tmp", exist_ok=True)
18+
except Exception as e:
19+
print(f"An error occurred: {e}")
20+
21+
yield
22+
23+
print("Tearing down the test environment.")
24+
shutil.rmtree("./test-tmp")
25+
26+
def test_upload_and_download_files(self):
27+
endpoint = os.getenv("ENDPOINT")
28+
bucket = os.getenv("BUCKET")
29+
30+
client = StoreFactory.new_client(
31+
provider=OBJECT_STORE_MINIO, endpoint=endpoint, bucket=bucket
32+
)
33+
assert False == client.exists("foo.txt")
34+
35+
with open("./test-tmp/foo.txt", "w") as file:
36+
file.write("test")
37+
38+
client.upload("./test-tmp/foo.txt", "foo.txt")
39+
assert True == client.exists("foo.txt")
40+
41+
client.download("foo.txt", "./test-tmp/bar.txt")
42+
assert True == os.path.exists("./test-tmp/bar.txt")
43+
44+
client.delete("foo.txt")
45+
assert False == client.exists("foo.txt")
46+
47+
def test_upload_and_download_dir(self):
48+
endpoint = os.getenv("ENDPOINT")
49+
bucket = os.getenv("BUCKET")
50+
51+
client = StoreFactory.new_client(
52+
provider=OBJECT_STORE_MINIO, endpoint=endpoint, bucket=bucket
53+
)
54+
assert False == client.exists("/test/foo.txt")
55+
56+
os.makedirs("./test-tmp/test/111", exist_ok=True)
57+
with open("./test-tmp/test/111/foo.txt", "w") as file:
58+
file.write("test")
59+
60+
client.upload_dir("./test-tmp/test", "test")
61+
assert True == client.exists("test/111/foo.txt")
62+
63+
client.download_dir("test", "./test-tmp/test1")
64+
assert True == os.path.exists("./test-tmp/test1/111/foo.txt")
65+
66+
client.delete_dir("test")
67+
assert False == client.exists("test/foo.txt")

0 commit comments

Comments
 (0)