Skip to content

Commit fd23d8e

Browse files
committed
azblob
1 parent ef44e88 commit fd23d8e

File tree

6 files changed

+94
-1
lines changed

6 files changed

+94
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/iceberg/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@ repository = { workspace = true }
3030

3131
[features]
3232
default = ["storage-memory", "storage-fs", "storage-s3", "tokio"]
33-
storage-all = ["storage-memory", "storage-fs", "storage-s3", "storage-gcs"]
33+
storage-all = ["storage-memory", "storage-fs", "storage-s3", "storage-gcs", "storage-azblob"]
3434

3535
storage-azdls = ["opendal/services-azdls"]
3636
storage-fs = ["opendal/services-fs"]
3737
storage-gcs = ["opendal/services-gcs"]
3838
storage-memory = ["opendal/services-memory"]
3939
storage-oss = ["opendal/services-oss"]
4040
storage-s3 = ["opendal/services-s3", "reqsign"]
41+
storage-azblob = ["opendal/services-azblob"]
4142

4243
smol = ["dep:smol"]
4344
tokio = ["tokio/rt-multi-thread"]

crates/iceberg/src/io/file_io.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use crate::{Error, ErrorKind, Result};
4444
/// | GCS | `storage-gcs` | `gs`, `gcs` | `gs://<bucket>/path/to/file` |
4545
/// | OSS | `storage-oss` | `oss` | `oss://<bucket>/path/to/file` |
4646
/// | Azure Datalake | `storage-azdls` | `abfs`, `abfss`, `wasb`, `wasbs` | `abfs://<filesystem>@<account>.dfs.core.windows.net/path/to/file` or `wasb://<container>@<account>.blob.core.windows.net/path/to/file` |
47+
/// | AZBLOB | `storage-azblob` | `azblob` | `azblob://<container>/path/to/file` |
4748
#[derive(Clone, Debug)]
4849
pub struct FileIO {
4950
builder: FileIOBuilder,

crates/iceberg/src/io/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ pub use storage_oss::*;
9898
#[cfg(feature = "storage-s3")]
9999
pub use storage_s3::*;
100100

101+
#[cfg(feature = "storage-azblob")]
102+
mod storage_azblob;
103+
#[cfg(feature = "storage-azblob")]
104+
pub use storage_azblob::*;
105+
101106
pub(crate) fn is_truthy(value: &str) -> bool {
102107
["true", "t", "1", "on"].contains(&value.to_lowercase().as_str())
103108
}

crates/iceberg/src/io/storage.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use std::sync::Arc;
1919

2020
use opendal::layers::RetryLayer;
21+
#[cfg(feature = "storage-azblob")]
22+
use opendal::services::AzblobConfig;
2123
#[cfg(feature = "storage-azdls")]
2224
use opendal::services::AzdlsConfig;
2325
#[cfg(feature = "storage-gcs")]
@@ -53,6 +55,8 @@ pub(crate) enum Storage {
5355
},
5456
#[cfg(feature = "storage-gcs")]
5557
Gcs { config: Arc<GcsConfig> },
58+
#[cfg(feature = "storage-azblob")]
59+
Azblob { config: Arc<AzblobConfig> },
5660
#[cfg(feature = "storage-oss")]
5761
Oss { config: Arc<OssConfig> },
5862
/// Expects paths of the form
@@ -90,6 +94,10 @@ impl Storage {
9094
Scheme::Gcs => Ok(Self::Gcs {
9195
config: super::gcs_config_parse(props)?.into(),
9296
}),
97+
#[cfg(feature = "storage-azblob")]
98+
Scheme::Azblob => Ok(Self::Azblob {
99+
config: super::azblob_config_parse(props)?.into(),
100+
}),
93101
#[cfg(feature = "storage-oss")]
94102
Scheme::Oss => Ok(Self::Oss {
95103
config: super::oss_config_parse(props)?.into(),
@@ -179,6 +187,19 @@ impl Storage {
179187
))
180188
}
181189
}
190+
#[cfg(feature = "storage-azblob")]
191+
Storage::Azblob { config } => {
192+
let operator = super::azblob_config_build(config, path)?;
193+
let prefix = format!("azblob://{}/", operator.info().name());
194+
if path.starts_with(&prefix) {
195+
Ok((operator, &path[prefix.len()..]))
196+
} else {
197+
Err(Error::new(
198+
ErrorKind::DataInvalid,
199+
format!("Invalid azblob url: {}, should start with {}", path, prefix),
200+
))
201+
}
202+
}
182203
#[cfg(feature = "storage-oss")]
183204
Storage::Oss { config } => {
184205
let op = super::oss_config_build(config, path)?;
@@ -203,6 +224,7 @@ impl Storage {
203224
not(feature = "storage-s3"),
204225
not(feature = "storage-fs"),
205226
not(feature = "storage-gcs"),
227+
not(feature = "storage-azblob"),
206228
not(feature = "storage-oss"),
207229
not(feature = "storage-azdls"),
208230
))]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
//! Azure blob storage properties
18+
use std::collections::HashMap;
19+
20+
use opendal::Operator;
21+
use opendal::services::AzblobConfig;
22+
use url::Url;
23+
24+
use crate::{Error, ErrorKind, Result};
25+
26+
/// Azure blob account name.
27+
pub const AZBLOB_ACCOUNT_NAME: &str = "azblob.account-name";
28+
/// Azure blob account key.
29+
pub const AZBLOB_ACCOUNT_KEY: &str = "azblob.account-key";
30+
/// Azure blob account endpoint.
31+
pub const AZBLOB_ENDPOINT: &str = "azblob.endpoint";
32+
33+
/// Parse iceberg properties to [`AzblobConfig`].
34+
pub(crate) fn azblob_config_parse(mut m: HashMap<String, String>) -> Result<AzblobConfig> {
35+
let mut cfg = AzblobConfig::default();
36+
37+
if let Some(account_name) = m.remove(AZBLOB_ACCOUNT_NAME) {
38+
cfg.account_name = Some(account_name);
39+
};
40+
if let Some(account_key) = m.remove(AZBLOB_ACCOUNT_KEY) {
41+
cfg.account_key = Some(account_key);
42+
};
43+
if let Some(endpoint) = m.remove(AZBLOB_ENDPOINT) {
44+
cfg.endpoint = Some(endpoint);
45+
};
46+
47+
Ok(cfg)
48+
}
49+
50+
/// Build a new OpenDAL [`Operator`] based on a provided [`AzblobConfig`].
51+
pub(crate) fn azblob_config_build(cfg: &AzblobConfig, path: &str) -> Result<Operator> {
52+
let url = Url::parse(path)?;
53+
let container = url.host_str().ok_or_else(|| {
54+
Error::new(
55+
ErrorKind::DataInvalid,
56+
format!("Invalid azblob url: {}, container is required", path),
57+
)
58+
})?;
59+
60+
let mut cfg = cfg.clone();
61+
cfg.container = container.to_string();
62+
Ok(Operator::from_config(cfg)?.finish())
63+
}

0 commit comments

Comments
 (0)