Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/test_fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,10 @@ jobs:
- name: Set Rust Fuzz
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libfuzzer-14-dev
rustup install ${{ env.RUST_TOOLCHAIN }}
cargo +${{ env.RUST_TOOLCHAIN }} install cargo-fuzz
- name: Fuzz
uses: ./.github/actions/fuzz_test
env:
CUSTOM_LIBFUZZER_PATH: /usr/lib/llvm-14/lib/libFuzzer.a
with:
setup: ${{ matrix.cases.setup }}
service: ${{ matrix.cases.service }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public sealed class AzblobServiceConfig : IServiceConfig
/// </summary>
public string? Container { get; init; }
/// <summary>
/// Enable soft deletes for this storage account.
/// </summary>
public bool? EnableSoftDeletes { get; init; }
/// <summary>
/// The encryption algorithm of Azblob service backend.
/// </summary>
public string? EncryptionAlgorithm { get; init; }
Expand Down Expand Up @@ -97,6 +101,10 @@ public IReadOnlyDictionary<string, string> ToOptions()
{
map["container"] = Utilities.ToOptionString(Container);
}
if (EnableSoftDeletes is not null)
{
map["enable_soft_deletes"] = Utilities.ToOptionString(EnableSoftDeletes);
}
if (EncryptionAlgorithm is not null)
{
map["encryption_algorithm"] = Utilities.ToOptionString(EncryptionAlgorithm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ class Azblob implements ServiceConfig {
* <p>The container name of Azblob service backend.</p>
*/
public final @NonNull String container;
/**
* <p>Enable soft deletes for this storage account.</p>
*/
public final Boolean enableSoftDeletes;
/**
* <p>The encryption algorithm of Azblob service backend.</p>
*/
Expand Down Expand Up @@ -235,6 +239,9 @@ public Map<String, String> configMap() {
map.put("batch_max_operations", String.valueOf(batchMaxOperations));
}
map.put("container", container);
if (enableSoftDeletes != null) {
map.put("enable_soft_deletes", String.valueOf(enableSoftDeletes));
}
if (encryptionAlgorithm != null) {
map.put("encryption_algorithm", encryptionAlgorithm);
}
Expand Down
2 changes: 2 additions & 0 deletions bindings/python/python/opendal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class AzblobConfig(TypedDict):
"""Deprecated: Azblob delete batch capability is enabled by default with Azure Blob's 256-operation batch limit. [Deprecated since 0.57.0] Azblob delete batch capability is enabled by default with Azure Blob's 256-operation batch limit. Use CapabilityOverrideLayer to override delete_max_size for specific endpoints."""
container: Required[str]
"""The container name of Azblob service backend."""
enable_soft_deletes: NotRequired[bool]
"""Enable soft deletes for this storage account."""
encryption_algorithm: NotRequired[str]
"""The encryption algorithm of Azblob service backend."""
encryption_key: NotRequired[str]
Expand Down
11 changes: 11 additions & 0 deletions core/services/azblob/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,15 @@ impl AzblobBuilder {
self
}

/// Set the soft delete feature for this backend.
///
/// If enabled, deleted blobs will be retained for the configured retention period
/// and can be listed using list_with_deleted.
pub fn enable_soft_deletes(mut self, enabled: bool) -> Self {
self.config.enable_soft_deletes = enabled;
self
}

/// from_connection_string will make a builder from connection string
///
/// connection string looks like:
Expand Down Expand Up @@ -442,6 +451,7 @@ impl Builder for AzblobBuilder {

list: true,
list_with_recursive: true,
list_with_deleted: self.config.enable_soft_deletes,

presign: self.config.sas_token.is_some(),
presign_stat: self.config.sas_token.is_some(),
Expand Down Expand Up @@ -588,6 +598,7 @@ impl Service for AzblobBackend {
path.to_string(),
args.recursive(),
args.limit(),
args.deleted(),
);

Ok(oio::PageLister::new(l))
Expand Down
5 changes: 5 additions & 0 deletions core/services/azblob/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ pub struct AzblobConfig {
/// Skip signature will skip loading credentials and signing requests.
#[serde(default)]
pub skip_signature: bool,

/// Enable soft deletes for this storage account.
#[serde(default)]
pub enable_soft_deletes: bool,
}

impl Debug for AzblobConfig {
Expand All @@ -92,6 +96,7 @@ impl Debug for AzblobConfig {
.field("root", &self.root)
.field("container", &self.container)
.field("endpoint", &self.endpoint)
.field("enable_soft_deletes", &self.enable_soft_deletes)
.finish_non_exhaustive()
}
}
Expand Down
7 changes: 7 additions & 0 deletions core/services/azblob/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ impl AzblobCore {
next_marker: &str,
delimiter: &str,
limit: Option<usize>,
include_deleted: bool,
) -> Result<Response<Buffer>> {
let p = build_abs_path(&self.root, path);
let mut url = QueryPairsWriter::new(&format!("{}/{}", self.endpoint, self.container))
Expand All @@ -843,6 +844,10 @@ impl AzblobCore {
url = url.push("marker", &percent_encode_path(next_marker));
}

if include_deleted {
url = url.push("include", "deleted");
}

let req = Request::get(url.finish())
.extension(Operation::List)
.extension(ServiceOperation("ListBlobs"))
Expand Down Expand Up @@ -916,6 +921,8 @@ pub struct BlobPrefix {
pub struct Blob {
pub properties: Properties,
pub name: String,
#[serde(rename = "Deleted")]
pub deleted: Option<bool>,
}

#[derive(Default, Debug, Deserialize)]
Expand Down
8 changes: 8 additions & 0 deletions core/services/azblob/src/lister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub struct AzblobLister {
path: String,
delimiter: &'static str,
limit: Option<usize>,
deleted: bool,
}

impl AzblobLister {
Expand All @@ -43,6 +44,7 @@ impl AzblobLister {
path: String,
recursive: bool,
limit: Option<usize>,
deleted: bool,
) -> Self {
let delimiter = if recursive { "" } else { "/" };

Expand All @@ -52,6 +54,7 @@ impl AzblobLister {
path,
delimiter,
limit,
deleted,
}
}
}
Expand All @@ -66,6 +69,7 @@ impl oio::PageList for AzblobLister {
&ctx.token,
self.delimiter,
self.limit,
self.deleted,
)
.await?;

Expand Down Expand Up @@ -118,6 +122,10 @@ impl oio::PageList for AzblobLister {
object.properties.last_modified.as_str(),
)?);

if object.deleted.unwrap_or(false) {
meta.is_deleted(true);
}

let de = oio::Entry::with(path, meta.build());
ctx.entries.push_back(de);
}
Expand Down
Loading
Loading