Minio/S3 blob storage support.

This commit is contained in:
Mauro D
2023-06-06 16:53:29 +00:00
parent c6e45a21e3
commit ab895b2fae
10 changed files with 628 additions and 58 deletions

View File

@@ -127,7 +127,12 @@ future-release = [ { if = "authenticated-as", ne = "", then = "99999999d"},
[store]
db.path = "{TMP}/sqlite.db"
blob.path = "{TMP}"
[store.blob]
type = "local"
[store.blob.local]
path = "{TMP}"
[certificate.default]
cert = "file://{CERT}"

212
tests/src/store/blob.rs Normal file
View File

@@ -0,0 +1,212 @@
use store::{BlobKind, Store};
use utils::config::Config;
use crate::store::TempDir;
const CONFIG_S3: &str = r#"
[store.db]
path = "{TMP}/_blob_s3_test_delete.db?mode=rwc"
[store.blob]
type = "s3"
[store.blob.s3]
access-key = "minioadmin"
secret-key = "minioadmin"
region = "eu-central-1"
endpoint = "http://localhost:9000"
bucket = "tmp"
"#;
const CONFIG_LOCAL: &str = r#"
[store.db]
path = "{TMP}/_blob_s3_test_delete.db?mode=rwc"
[store.blob]
type = "local"
[store.blob.local]
path = "{TMP}"
"#;
const DATA: &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce erat nisl, dignissim a porttitor id, varius nec arcu. Sed mauris.";
#[tokio::test]
pub async fn blob_s3_test() {
let temp_dir = TempDir::new("blob_tests", true);
test_blob(
Store::open(
&Config::parse(
&CONFIG_LOCAL.replace("{TMP}", temp_dir.path.as_path().to_str().unwrap()),
)
.unwrap(),
)
.await
.unwrap(),
)
.await;
test_blob(
Store::open(
&Config::parse(&CONFIG_S3.replace("{TMP}", temp_dir.path.as_path().to_str().unwrap()))
.unwrap(),
)
.await
.unwrap(),
)
.await;
temp_dir.delete();
}
async fn test_blob(store: Store) {
// Store and fetch
let kind = BlobKind::LinkedMaildir {
account_id: 0,
document_id: 0,
};
store.put_blob(&kind, DATA).await.unwrap();
assert_eq!(
String::from_utf8(store.get_blob(&kind, 0..u32::MAX).await.unwrap().unwrap()).unwrap(),
std::str::from_utf8(DATA).unwrap()
);
assert_eq!(
String::from_utf8(store.get_blob(&kind, 11..57).await.unwrap().unwrap()).unwrap(),
std::str::from_utf8(&DATA[11..57]).unwrap()
);
assert!(store.delete_blob(&kind).await.unwrap());
assert!(store.get_blob(&kind, 0..u32::MAX).await.unwrap().is_none());
// Copy
let src_kind = BlobKind::LinkedMaildir {
account_id: 0,
document_id: 1,
};
store.put_blob(&src_kind, DATA).await.unwrap();
for id in 0..4 {
let dest_kind = BlobKind::LinkedMaildir {
account_id: 1,
document_id: id,
};
assert!(store.copy_blob(&src_kind, &dest_kind, None).await.unwrap());
assert_eq!(
String::from_utf8(
store
.get_blob(&dest_kind, 0..u32::MAX)
.await
.unwrap()
.unwrap()
)
.unwrap(),
std::str::from_utf8(DATA).unwrap()
);
}
// Copy partial
let tmp_kind = BlobKind::Temporary {
account_id: 1,
creation_year: 2020,
creation_month: 12,
creation_day: 31,
seq: 0,
};
let tmp_kind2 = BlobKind::Temporary {
account_id: 1,
creation_year: 2021,
creation_month: 1,
creation_day: 1,
seq: 0,
};
assert!(store
.copy_blob(&src_kind, &tmp_kind, (0..11).into())
.await
.unwrap());
assert!(store
.copy_blob(&src_kind, &tmp_kind2, (0..11).into())
.await
.unwrap());
assert_eq!(
String::from_utf8(
store
.get_blob(&tmp_kind, 0..u32::MAX)
.await
.unwrap()
.unwrap()
)
.unwrap(),
std::str::from_utf8(&DATA[0..11]).unwrap()
);
// Delete range
store
.bulk_delete_blob(&BlobKind::LinkedMaildir {
account_id: 1,
document_id: 0,
})
.await
.unwrap();
store.bulk_delete_blob(&tmp_kind).await.unwrap();
// Make sure the blobs are deleted
for id in 0..4 {
assert!(store
.get_blob(
&BlobKind::LinkedMaildir {
account_id: 1,
document_id: id,
},
0..u32::MAX
)
.await
.unwrap()
.is_none());
}
assert!(store
.get_blob(&tmp_kind, 0..u32::MAX)
.await
.unwrap()
.is_none());
// Make sure other blobs were not deleted
assert!(store
.get_blob(&src_kind, 0..u32::MAX)
.await
.unwrap()
.is_some());
assert!(store
.get_blob(&tmp_kind2, 0..u32::MAX)
.await
.unwrap()
.is_some());
// Copying a non-existing blob should fail
assert!(!store.copy_blob(&tmp_kind, &src_kind, None).await.unwrap());
// Copy blob between buckets
assert!(store
.copy_blob(&src_kind, &tmp_kind, (10..20).into())
.await
.unwrap());
assert_eq!(
String::from_utf8(
store
.get_blob(&tmp_kind, 0..u32::MAX)
.await
.unwrap()
.unwrap()
)
.unwrap(),
std::str::from_utf8(&DATA[10..20]).unwrap()
);
// Delete blobs
for blob_kind in [src_kind, tmp_kind, tmp_kind2] {
assert!(store.delete_blob(&blob_kind).await.unwrap());
assert!(store
.get_blob(&blob_kind, 0..u32::MAX)
.await
.unwrap()
.is_none());
}
}

View File

@@ -22,6 +22,7 @@
*/
pub mod assign_id;
pub mod blob;
pub mod query;
use std::{io::Read, sync::Arc};
@@ -39,7 +40,8 @@ pub async fn store_tests() {
let temp_dir = TempDir::new("store_tests", insert);
let config_file = format!(
concat!(
"store.blob.path = \"{}\"\n",
"store.blob.type = \"local\"\n",
"store.blob.local.path = \"{}\"\n",
"store.db.path = \"{}/sqlite.db\"\n"
),
temp_dir.path.display(),
@@ -53,7 +55,7 @@ pub async fn store_tests() {
if insert {
db.destroy().await;
}
//assign_id::test(db).await;
assign_id::test(db.clone()).await;
query::test(db, insert).await;
temp_dir.delete();
}