feat(storage-api): add bucket DTO contract (#3314)

* feat(storage-api): add bucket DTO contract

* ci(build): increase workflow timeout to 90 minutes

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: houseme <housemecn@gmail.com>
This commit is contained in:
安正超
2026-06-10 15:16:14 +08:00
committed by GitHub
parent b9c924a6ed
commit bb5d9565a6
12 changed files with 224 additions and 74 deletions
+1 -1
View File
@@ -40,6 +40,7 @@ rustfs-utils = { workspace = true, features = ["full"] }
rustfs-rio.workspace = true
rustfs-rio-v2 = { workspace = true, optional = true }
rustfs-signer.workspace = true
rustfs-storage-api.workspace = true
rustfs-tls-runtime.workspace = true
rustfs-checksums.workspace = true
rustfs-config = { workspace = true, features = ["constants", "notify", "audit"] }
@@ -51,7 +52,6 @@ rustfs-kms.workspace = true
rustfs-s3-types = { workspace = true }
rustfs-data-usage.workspace = true
rustfs-object-capacity.workspace = true
rustfs-storage-api.workspace = true
async-trait.workspace = true
bytes.workspace = true
byteorder = { workspace = true }
-1
View File
@@ -39,7 +39,6 @@ use rustfs_utils::CompressionAlgorithm;
use rustfs_utils::http::headers::AMZ_OBJECT_TAGGING;
use rustfs_utils::http::{AMZ_BUCKET_REPLICATION_STATUS, AMZ_RESTORE, AMZ_STORAGE_CLASS};
use rustfs_utils::path::decode_dir_object;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::Cursor;
+2
View File
@@ -11,6 +11,8 @@ use rustfs_kms::{service_manager::get_global_encryption_service, types::ObjectEn
use rustfs_utils::http::{SSEC_ALGORITHM_HEADER, SSEC_KEY_HEADER, SSEC_KEY_MD5_HEADER};
use rustfs_utils::path::path_join_buf;
#[cfg(feature = "rio-v2")]
use serde::Deserialize;
#[cfg(feature = "rio-v2")]
use sha2::Sha256;
use std::collections::HashMap;
use std::env;
+2 -40
View File
@@ -1,29 +1,7 @@
use super::*;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MakeBucketOptions {
pub lock_enabled: bool,
pub versioning_enabled: bool,
pub force_create: bool, // Create buckets even if they are already created.
pub created_at: Option<OffsetDateTime>, // only for site replication
pub no_lock: bool,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum SRBucketDeleteOp {
#[default]
NoOp,
MarkDelete,
Purge,
}
#[derive(Debug, Default, Clone)]
pub struct DeleteBucketOptions {
pub no_lock: bool,
pub no_recreate: bool,
pub force: bool, // Force deletion
pub srdelete_op: SRBucketDeleteOp,
}
// RUSTFS_COMPAT_TODO(API-003): keep old ecstore::store_api bucket DTO import paths while storage API consumers migrate. Remove after all consumers import these DTOs from rustfs_storage_api.
pub use rustfs_storage_api::{BucketInfo, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp};
#[derive(Debug, Default, Clone)]
pub struct HTTPPreconditions {
@@ -225,22 +203,6 @@ fn is_modified_since(mod_time: &OffsetDateTime, given_time: &OffsetDateTime) ->
mod_secs > given_secs
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct BucketOptions {
pub deleted: bool, // true only when site replication is enabled
pub cached: bool, // true only when we are requesting a cached response instead of hitting the disk for example ListBuckets() call.
pub no_metadata: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct BucketInfo {
pub name: String,
pub created: Option<OffsetDateTime>,
pub deleted: Option<OffsetDateTime>,
pub versioning: bool,
pub object_locking: bool,
}
#[derive(Debug, Default, Clone)]
pub struct MultipartUploadResult {
pub upload_id: String,
@@ -0,0 +1,38 @@
// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use rustfs_ecstore::store_api::{
BucketInfo as EcstoreBucketInfo, BucketOptions as EcstoreBucketOptions, MakeBucketOptions as EcstoreMakeBucketOptions,
};
use rustfs_storage_api::{
BucketInfo as ApiBucketInfo, BucketOptions as ApiBucketOptions, MakeBucketOptions as ApiMakeBucketOptions,
};
#[test]
fn old_store_api_bucket_dto_path_reexports_storage_api_types() {
let ecstore_bucket: EcstoreBucketInfo = ApiBucketInfo {
name: "photos".to_owned(),
versioning: true,
..Default::default()
};
let api_bucket: ApiBucketInfo = ecstore_bucket;
let ecstore_make: EcstoreMakeBucketOptions = ApiMakeBucketOptions::default();
let api_options: ApiBucketOptions = EcstoreBucketOptions::default();
assert_eq!(api_bucket.name, "photos");
assert!(api_bucket.versioning);
assert!(!ecstore_make.lock_enabled);
assert!(!api_options.no_metadata);
}
+7
View File
@@ -27,5 +27,12 @@ categories = ["web-programming", "development-tools", "filesystem"]
[lib]
doctest = false
[dependencies]
serde.workspace = true
time.workspace = true
[dev-dependencies]
serde_json.workspace = true
[lints]
workspace = true
+100
View File
@@ -0,0 +1,100 @@
// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct MakeBucketOptions {
pub lock_enabled: bool,
pub versioning_enabled: bool,
pub force_create: bool,
pub created_at: Option<OffsetDateTime>,
pub no_lock: bool,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum SRBucketDeleteOp {
#[default]
NoOp,
MarkDelete,
Purge,
}
#[derive(Debug, Default, Clone)]
pub struct DeleteBucketOptions {
pub no_lock: bool,
pub no_recreate: bool,
pub force: bool,
pub srdelete_op: SRBucketDeleteOp,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct BucketOptions {
pub deleted: bool,
pub cached: bool,
pub no_metadata: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct BucketInfo {
pub name: String,
pub created: Option<OffsetDateTime>,
pub deleted: Option<OffsetDateTime>,
pub versioning: bool,
pub object_locking: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bucket_info_serializes_with_existing_fields() {
let bucket = BucketInfo {
name: "photos".to_owned(),
created: Some(OffsetDateTime::UNIX_EPOCH),
deleted: None,
versioning: true,
object_locking: false,
};
let encoded = serde_json::to_string(&bucket).expect("serialize BucketInfo to JSON");
let decoded: BucketInfo = serde_json::from_str(&encoded).expect("deserialize BucketInfo from JSON");
assert_eq!(decoded.name, "photos");
assert_eq!(decoded.created, Some(OffsetDateTime::UNIX_EPOCH));
assert!(decoded.versioning);
assert!(!decoded.object_locking);
}
#[test]
fn default_bucket_options_preserve_false_flags() {
let opts = BucketOptions::default();
assert!(!opts.deleted);
assert!(!opts.cached);
assert!(!opts.no_metadata);
}
#[test]
fn default_delete_bucket_options_use_noop_site_replication() {
let opts = DeleteBucketOptions::default();
assert_eq!(opts.srdelete_op, SRBucketDeleteOp::NoOp);
assert!(!opts.no_lock);
assert!(!opts.no_recreate);
assert!(!opts.force);
}
}
+2
View File
@@ -14,6 +14,8 @@
//! Storage API contracts for RustFS.
pub mod bucket;
pub mod error;
pub use bucket::{BucketInfo, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp};
pub use error::{StorageErrorCode, StorageResult};