diff --git a/.github/workflows/test_fuzz.yml b/.github/workflows/test_fuzz.yml index 25dc47f709ee..0735c3819955 100644 --- a/.github/workflows/test_fuzz.yml +++ b/.github/workflows/test_fuzz.yml @@ -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 }} diff --git a/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs b/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs index 7a148af8dd7e..b397d23702ea 100644 --- a/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs +++ b/bindings/dotnet/OpenDAL/ServiceConfig/AzblobServiceConfig.cs @@ -46,6 +46,10 @@ public sealed class AzblobServiceConfig : IServiceConfig /// public string? Container { get; init; } /// + /// Enable soft deletes for this storage account. + /// + public bool? EnableSoftDeletes { get; init; } + /// /// The encryption algorithm of Azblob service backend. /// public string? EncryptionAlgorithm { get; init; } @@ -97,6 +101,10 @@ public IReadOnlyDictionary 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); diff --git a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java index e915e1112625..35498b78229a 100644 --- a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java +++ b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java @@ -182,6 +182,10 @@ class Azblob implements ServiceConfig { *

The container name of Azblob service backend.

*/ public final @NonNull String container; + /** + *

Enable soft deletes for this storage account.

+ */ + public final Boolean enableSoftDeletes; /** *

The encryption algorithm of Azblob service backend.

*/ @@ -235,6 +239,9 @@ public Map 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); } diff --git a/bindings/python/python/opendal/config.py b/bindings/python/python/opendal/config.py index bfe1587c87e1..c0add8aac4a1 100644 --- a/bindings/python/python/opendal/config.py +++ b/bindings/python/python/opendal/config.py @@ -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] diff --git a/core/services/azblob/src/backend.rs b/core/services/azblob/src/backend.rs index 62a41c877e85..ce8b79f82edb 100644 --- a/core/services/azblob/src/backend.rs +++ b/core/services/azblob/src/backend.rs @@ -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: @@ -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(), @@ -588,6 +598,7 @@ impl Service for AzblobBackend { path.to_string(), args.recursive(), args.limit(), + args.deleted(), ); Ok(oio::PageLister::new(l)) diff --git a/core/services/azblob/src/config.rs b/core/services/azblob/src/config.rs index 0e8933ba2193..6f9d10e03a67 100644 --- a/core/services/azblob/src/config.rs +++ b/core/services/azblob/src/config.rs @@ -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 { @@ -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() } } diff --git a/core/services/azblob/src/core.rs b/core/services/azblob/src/core.rs index c0a5b1453638..2c3cde23b5d9 100644 --- a/core/services/azblob/src/core.rs +++ b/core/services/azblob/src/core.rs @@ -824,6 +824,7 @@ impl AzblobCore { next_marker: &str, delimiter: &str, limit: Option, + include_deleted: bool, ) -> Result> { let p = build_abs_path(&self.root, path); let mut url = QueryPairsWriter::new(&format!("{}/{}", self.endpoint, self.container)) @@ -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")) @@ -916,6 +921,8 @@ pub struct BlobPrefix { pub struct Blob { pub properties: Properties, pub name: String, + #[serde(rename = "Deleted")] + pub deleted: Option, } #[derive(Default, Debug, Deserialize)] diff --git a/core/services/azblob/src/lister.rs b/core/services/azblob/src/lister.rs index 90235d6990a5..0cb59637c028 100644 --- a/core/services/azblob/src/lister.rs +++ b/core/services/azblob/src/lister.rs @@ -34,6 +34,7 @@ pub struct AzblobLister { path: String, delimiter: &'static str, limit: Option, + deleted: bool, } impl AzblobLister { @@ -43,6 +44,7 @@ impl AzblobLister { path: String, recursive: bool, limit: Option, + deleted: bool, ) -> Self { let delimiter = if recursive { "" } else { "/" }; @@ -52,6 +54,7 @@ impl AzblobLister { path, delimiter, limit, + deleted, } } } @@ -66,6 +69,7 @@ impl oio::PageList for AzblobLister { &ctx.token, self.delimiter, self.limit, + self.deleted, ) .await?; @@ -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); } diff --git a/website/data/services.json b/website/data/services.json index 1156882f31f3..a7dc425f61b0 100644 --- a/website/data/services.json +++ b/website/data/services.json @@ -267,6 +267,13 @@ "required": false, "group": "General", "comments": "Skip signature will skip loading credentials and signing requests." + }, + { + "name": "enable_soft_deletes", + "type": "bool", + "required": false, + "group": "General", + "comments": "Enable soft deletes for this storage account." } ], "examples": [ @@ -274,37 +281,37 @@ "binding": "rust", "language": "rust", "minimal": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n (\"container\".to_string(), \"...\".to_string()),\n])?;", - "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // (\"root\".to_string(), \"...\".to_string()),\n\n // The container name of Azblob service backend.\n (\"container\".to_string(), \"...\".to_string()),\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // The account name of Azblob service backend.\n // (\"account_name\".to_string(), \"...\".to_string()),\n\n // The account key of Azblob service backend.\n // (\"account_key\".to_string(), \"...\".to_string()),\n\n // The encryption key of Azblob service backend.\n // (\"encryption_key\".to_string(), \"...\".to_string()),\n\n // The encryption key sha256 of Azblob service backend.\n // (\"encryption_key_sha256\".to_string(), \"...\".to_string()),\n\n // The encryption algorithm of Azblob service backend.\n // (\"encryption_algorithm\".to_string(), \"...\".to_string()),\n\n // The sas token of Azblob service backend.\n // (\"sas_token\".to_string(), \"...\".to_string()),\n\n // Skip signature will skip loading credentials and signing requests.\n // (\"skip_signature\".to_string(), \"true\".to_string()),\n])?;" + "full": "use opendal::Operator;\n\nlet operator = Operator::via_iter(\"azblob\", [\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // (\"root\".to_string(), \"...\".to_string()),\n\n // The container name of Azblob service backend.\n (\"container\".to_string(), \"...\".to_string()),\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // The account name of Azblob service backend.\n // (\"account_name\".to_string(), \"...\".to_string()),\n\n // The account key of Azblob service backend.\n // (\"account_key\".to_string(), \"...\".to_string()),\n\n // The encryption key of Azblob service backend.\n // (\"encryption_key\".to_string(), \"...\".to_string()),\n\n // The encryption key sha256 of Azblob service backend.\n // (\"encryption_key_sha256\".to_string(), \"...\".to_string()),\n\n // The encryption algorithm of Azblob service backend.\n // (\"encryption_algorithm\".to_string(), \"...\".to_string()),\n\n // The sas token of Azblob service backend.\n // (\"sas_token\".to_string(), \"...\".to_string()),\n\n // Skip signature will skip loading credentials and signing requests.\n // (\"skip_signature\".to_string(), \"true\".to_string()),\n\n // Enable soft deletes for this storage account.\n // (\"enable_soft_deletes\".to_string(), \"true\".to_string()),\n])?;" }, { "binding": "java", "language": "java", "minimal": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\nconfig.put(\"container\", \"...\");\nOperator operator = Operator.of(\"azblob\", config);", - "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\n// The root of Azblob service backend.\n//\n// All operations will happen under this root.\n// config.put(\"root\", \"...\");\n\n// The container name of Azblob service backend.\nconfig.put(\"container\", \"...\");\n\n// The endpoint of Azblob service backend.\n//\n// Endpoint must be full uri, e.g.\n//\n// - Azblob: `https://accountname.blob.core.windows.net`\n// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n// config.put(\"endpoint\", \"...\");\n\n// The account name of Azblob service backend.\n// config.put(\"account_name\", \"...\");\n\n// The account key of Azblob service backend.\n// config.put(\"account_key\", \"...\");\n\n// The encryption key of Azblob service backend.\n// config.put(\"encryption_key\", \"...\");\n\n// The encryption key sha256 of Azblob service backend.\n// config.put(\"encryption_key_sha256\", \"...\");\n\n// The encryption algorithm of Azblob service backend.\n// config.put(\"encryption_algorithm\", \"...\");\n\n// The sas token of Azblob service backend.\n// config.put(\"sas_token\", \"...\");\n\n// Skip signature will skip loading credentials and signing requests.\n// config.put(\"skip_signature\", \"true\");\nOperator operator = Operator.of(\"azblob\", config);" + "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap config = new HashMap<>();\n// The root of Azblob service backend.\n//\n// All operations will happen under this root.\n// config.put(\"root\", \"...\");\n\n// The container name of Azblob service backend.\nconfig.put(\"container\", \"...\");\n\n// The endpoint of Azblob service backend.\n//\n// Endpoint must be full uri, e.g.\n//\n// - Azblob: `https://accountname.blob.core.windows.net`\n// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n// config.put(\"endpoint\", \"...\");\n\n// The account name of Azblob service backend.\n// config.put(\"account_name\", \"...\");\n\n// The account key of Azblob service backend.\n// config.put(\"account_key\", \"...\");\n\n// The encryption key of Azblob service backend.\n// config.put(\"encryption_key\", \"...\");\n\n// The encryption key sha256 of Azblob service backend.\n// config.put(\"encryption_key_sha256\", \"...\");\n\n// The encryption algorithm of Azblob service backend.\n// config.put(\"encryption_algorithm\", \"...\");\n\n// The sas token of Azblob service backend.\n// config.put(\"sas_token\", \"...\");\n\n// Skip signature will skip loading credentials and signing requests.\n// config.put(\"skip_signature\", \"true\");\n\n// Enable soft deletes for this storage account.\n// config.put(\"enable_soft_deletes\", \"true\");\nOperator operator = Operator.of(\"azblob\", config);" }, { "binding": "python", "language": "python", "minimal": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n container=\"...\",\n)", - "full": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # root=\"...\",\n\n # The container name of Azblob service backend.\n container=\"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # endpoint=\"...\",\n\n # The account name of Azblob service backend.\n # account_name=\"...\",\n\n # The account key of Azblob service backend.\n # account_key=\"...\",\n\n # The encryption key of Azblob service backend.\n # encryption_key=\"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # encryption_key_sha256=\"...\",\n\n # The encryption algorithm of Azblob service backend.\n # encryption_algorithm=\"...\",\n\n # The sas token of Azblob service backend.\n # sas_token=\"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # skip_signature=\"true\",\n)" + "full": "import opendal\n\noperator = opendal.Operator(\n \"azblob\",\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # root=\"...\",\n\n # The container name of Azblob service backend.\n container=\"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # endpoint=\"...\",\n\n # The account name of Azblob service backend.\n # account_name=\"...\",\n\n # The account key of Azblob service backend.\n # account_key=\"...\",\n\n # The encryption key of Azblob service backend.\n # encryption_key=\"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # encryption_key_sha256=\"...\",\n\n # The encryption algorithm of Azblob service backend.\n # encryption_algorithm=\"...\",\n\n # The sas token of Azblob service backend.\n # sas_token=\"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # skip_signature=\"true\",\n\n # Enable soft deletes for this storage account.\n # enable_soft_deletes=\"true\",\n)" }, { "binding": "nodejs", "language": "javascript", "minimal": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n container: \"...\",\n});", - "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // root: \"...\",\n\n // The container name of Azblob service backend.\n container: \"...\",\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // endpoint: \"...\",\n\n // The account name of Azblob service backend.\n // account_name: \"...\",\n\n // The account key of Azblob service backend.\n // account_key: \"...\",\n\n // The encryption key of Azblob service backend.\n // encryption_key: \"...\",\n\n // The encryption key sha256 of Azblob service backend.\n // encryption_key_sha256: \"...\",\n\n // The encryption algorithm of Azblob service backend.\n // encryption_algorithm: \"...\",\n\n // The sas token of Azblob service backend.\n // sas_token: \"...\",\n\n // Skip signature will skip loading credentials and signing requests.\n // skip_signature: \"true\",\n});" + "full": "import { Operator } from \"opendal\";\n\nconst operator = new Operator(\"azblob\", {\n // The root of Azblob service backend.\n //\n // All operations will happen under this root.\n // root: \"...\",\n\n // The container name of Azblob service backend.\n container: \"...\",\n\n // The endpoint of Azblob service backend.\n //\n // Endpoint must be full uri, e.g.\n //\n // - Azblob: `https://accountname.blob.core.windows.net`\n // - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n // endpoint: \"...\",\n\n // The account name of Azblob service backend.\n // account_name: \"...\",\n\n // The account key of Azblob service backend.\n // account_key: \"...\",\n\n // The encryption key of Azblob service backend.\n // encryption_key: \"...\",\n\n // The encryption key sha256 of Azblob service backend.\n // encryption_key_sha256: \"...\",\n\n // The encryption algorithm of Azblob service backend.\n // encryption_algorithm: \"...\",\n\n // The sas token of Azblob service backend.\n // sas_token: \"...\",\n\n // Skip signature will skip loading credentials and signing requests.\n // skip_signature: \"true\",\n\n // Enable soft deletes for this storage account.\n // enable_soft_deletes: \"true\",\n});" }, { "binding": "ruby", "language": "ruby", "minimal": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n \"container\" => \"...\",\n})", - "full": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # \"root\" => \"...\",\n\n # The container name of Azblob service backend.\n \"container\" => \"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # \"endpoint\" => \"...\",\n\n # The account name of Azblob service backend.\n # \"account_name\" => \"...\",\n\n # The account key of Azblob service backend.\n # \"account_key\" => \"...\",\n\n # The encryption key of Azblob service backend.\n # \"encryption_key\" => \"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # \"encryption_key_sha256\" => \"...\",\n\n # The encryption algorithm of Azblob service backend.\n # \"encryption_algorithm\" => \"...\",\n\n # The sas token of Azblob service backend.\n # \"sas_token\" => \"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # \"skip_signature\" => \"true\",\n})" + "full": "require \"opendal\"\n\noperator = OpenDal::Operator.new(\"azblob\", {\n # The root of Azblob service backend.\n #\n # All operations will happen under this root.\n # \"root\" => \"...\",\n\n # The container name of Azblob service backend.\n \"container\" => \"...\",\n\n # The endpoint of Azblob service backend.\n #\n # Endpoint must be full uri, e.g.\n #\n # - Azblob: `https://accountname.blob.core.windows.net`\n # - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n # \"endpoint\" => \"...\",\n\n # The account name of Azblob service backend.\n # \"account_name\" => \"...\",\n\n # The account key of Azblob service backend.\n # \"account_key\" => \"...\",\n\n # The encryption key of Azblob service backend.\n # \"encryption_key\" => \"...\",\n\n # The encryption key sha256 of Azblob service backend.\n # \"encryption_key_sha256\" => \"...\",\n\n # The encryption algorithm of Azblob service backend.\n # \"encryption_algorithm\" => \"...\",\n\n # The sas token of Azblob service backend.\n # \"sas_token\" => \"...\",\n\n # Skip signature will skip loading credentials and signing requests.\n # \"skip_signature\" => \"true\",\n\n # Enable soft deletes for this storage account.\n # \"enable_soft_deletes\" => \"true\",\n})" }, { "binding": "go", "language": "go", "minimal": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t\"container\": \"...\",\n})", - "full": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t// The root of Azblob service backend.\n\t//\n\t// All operations will happen under this root.\n\t// \"root\": \"...\",\n\n\t// The container name of Azblob service backend.\n\t\"container\": \"...\",\n\n\t// The endpoint of Azblob service backend.\n\t//\n\t// Endpoint must be full uri, e.g.\n\t//\n\t// - Azblob: `https://accountname.blob.core.windows.net`\n\t// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n\t// \"endpoint\": \"...\",\n\n\t// The account name of Azblob service backend.\n\t// \"account_name\": \"...\",\n\n\t// The account key of Azblob service backend.\n\t// \"account_key\": \"...\",\n\n\t// The encryption key of Azblob service backend.\n\t// \"encryption_key\": \"...\",\n\n\t// The encryption key sha256 of Azblob service backend.\n\t// \"encryption_key_sha256\": \"...\",\n\n\t// The encryption algorithm of Azblob service backend.\n\t// \"encryption_algorithm\": \"...\",\n\n\t// The sas token of Azblob service backend.\n\t// \"sas_token\": \"...\",\n\n\t// Skip signature will skip loading credentials and signing requests.\n\t// \"skip_signature\": \"true\",\n})" + "full": "import (\n\t\"github.com/apache/opendal-go-services/azblob\"\n\topendal \"github.com/apache/opendal/bindings/go\"\n)\n\noperator, err := opendal.NewOperator(azblob.Scheme, opendal.OperatorOptions{\n\t// The root of Azblob service backend.\n\t//\n\t// All operations will happen under this root.\n\t// \"root\": \"...\",\n\n\t// The container name of Azblob service backend.\n\t\"container\": \"...\",\n\n\t// The endpoint of Azblob service backend.\n\t//\n\t// Endpoint must be full uri, e.g.\n\t//\n\t// - Azblob: `https://accountname.blob.core.windows.net`\n\t// - Azurite: `http://127.0.0.1:10000/devstoreaccount1`\n\t// \"endpoint\": \"...\",\n\n\t// The account name of Azblob service backend.\n\t// \"account_name\": \"...\",\n\n\t// The account key of Azblob service backend.\n\t// \"account_key\": \"...\",\n\n\t// The encryption key of Azblob service backend.\n\t// \"encryption_key\": \"...\",\n\n\t// The encryption key sha256 of Azblob service backend.\n\t// \"encryption_key_sha256\": \"...\",\n\n\t// The encryption algorithm of Azblob service backend.\n\t// \"encryption_algorithm\": \"...\",\n\n\t// The sas token of Azblob service backend.\n\t// \"sas_token\": \"...\",\n\n\t// Skip signature will skip loading credentials and signing requests.\n\t// \"skip_signature\": \"true\",\n\n\t// Enable soft deletes for this storage account.\n\t// \"enable_soft_deletes\": \"true\",\n})" } ] },