-
Notifications
You must be signed in to change notification settings - Fork 421
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
345 additions
and
287 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
[package] | ||
name = "deltalake-azure" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
deltalake-core = { path = "../deltalake-core" } | ||
lazy_static = "1" | ||
|
||
# workspace depenndecies | ||
async-trait = { workspace = true } | ||
bytes = { workspace = true } | ||
futures = { workspace = true } | ||
tracing = { workspace = true } | ||
object_store = { workspace = true, features = ["azure"]} | ||
thiserror = { workspace = true } | ||
tokio = { workspace = true } | ||
regex = { workspace = true } | ||
url = { workspace = true } | ||
|
||
[dev-dependencies] | ||
chrono = { workspace = true } | ||
serial_test = "2" | ||
deltalake-test = { path = "../deltalake-test" } | ||
pretty_env_logger = "*" | ||
rand = "0.8" | ||
serde_json = { workspace = true } | ||
|
||
[features] | ||
integration_test = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
use deltalake_core::errors::DeltaTableError; | ||
|
||
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>; | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
pub(crate) enum Error { | ||
#[error("failed to parse config: {0}")] | ||
Parse(String), | ||
|
||
#[error(transparent)] | ||
ObjectStore(#[from] object_store::Error), | ||
} | ||
|
||
impl From<Error> for DeltaTableError { | ||
fn from(e: Error) -> Self { | ||
match e { | ||
Error::Parse(msg) => DeltaTableError::Generic(msg), | ||
Error::ObjectStore(e) => DeltaTableError::ObjectStore { source: e }, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use std::collections::HashMap; | ||
use std::str::FromStr; | ||
use std::sync::Arc; | ||
|
||
use deltalake_core::logstore::{default_logstore, logstores, LogStore, LogStoreFactory}; | ||
use deltalake_core::storage::{ | ||
factories, url_prefix_handler, ObjectStoreFactory, ObjectStoreRef, StorageOptions, | ||
}; | ||
use deltalake_core::{DeltaResult, Path}; | ||
use object_store::azure::AzureConfigKey; | ||
use object_store::parse_url_opts; | ||
use url::Url; | ||
|
||
mod config; | ||
pub mod error; | ||
|
||
trait AzureOptions { | ||
fn as_azure_options(&self) -> HashMap<AzureConfigKey, String>; | ||
} | ||
|
||
impl AzureOptions for StorageOptions { | ||
fn as_azure_options(&self) -> HashMap<AzureConfigKey, String> { | ||
self.0 | ||
.iter() | ||
.filter_map(|(key, value)| { | ||
Some(( | ||
AzureConfigKey::from_str(&key.to_ascii_lowercase()).ok()?, | ||
value.clone(), | ||
)) | ||
}) | ||
.collect() | ||
} | ||
} | ||
|
||
#[derive(Clone, Default, Debug)] | ||
pub struct AzureFactory {} | ||
|
||
impl ObjectStoreFactory for AzureFactory { | ||
fn parse_url_opts( | ||
&self, | ||
url: &Url, | ||
options: &StorageOptions, | ||
) -> DeltaResult<(ObjectStoreRef, Path)> { | ||
let config = config::AzureConfigHelper::try_new(options.as_azure_options())?.build()?; | ||
let (store, prefix) = parse_url_opts(url, config)?; | ||
Ok((url_prefix_handler(store, prefix.clone())?, prefix)) | ||
} | ||
} | ||
|
||
impl LogStoreFactory for AzureFactory { | ||
fn with_options( | ||
&self, | ||
store: ObjectStoreRef, | ||
location: &Url, | ||
options: &StorageOptions, | ||
) -> DeltaResult<Arc<dyn LogStore>> { | ||
Ok(default_logstore(store, location, options)) | ||
} | ||
} | ||
|
||
/// Register an [ObjectStoreFactory] for common Azure [Url] schemes | ||
pub fn register_handlers(_additional_prefixes: Option<Url>) { | ||
let factory = Arc::new(AzureFactory {}); | ||
for scheme in ["az", "adl", "azure", "abfs", "abfss"].iter() { | ||
let url = Url::parse(&format!("{}://", scheme)).unwrap(); | ||
factories().insert(url.clone(), factory.clone()); | ||
logstores().insert(url.clone(), factory.clone()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
use chrono::Utc; | ||
use deltalake_azure::register_handlers; | ||
use deltalake_test::utils::*; | ||
use std::process::ExitStatus; | ||
|
||
/// Kinds of storage integration | ||
#[derive(Clone, Debug)] | ||
pub enum MsftIntegration { | ||
Azure(String), | ||
Onelake, | ||
OnelakeAbfs, | ||
} | ||
|
||
impl Default for MsftIntegration { | ||
fn default() -> Self { | ||
register_handlers(None); | ||
Self::Azure(format!("test-delta-table-{}", Utc::now().timestamp())) | ||
} | ||
} | ||
|
||
impl StorageIntegration for MsftIntegration { | ||
fn prepare_env(&self) { | ||
match self { | ||
Self::Azure(_) => az_cli::prepare_env(), | ||
Self::Onelake => onelake_cli::prepare_env(), | ||
Self::OnelakeAbfs => onelake_cli::prepare_env(), | ||
} | ||
} | ||
|
||
fn create_bucket(&self) -> std::io::Result<ExitStatus> { | ||
match self { | ||
Self::Azure(_) => az_cli::create_container(self.bucket_name()), | ||
Self::Onelake => Ok(ExitStatus::default()), | ||
Self::OnelakeAbfs => Ok(ExitStatus::default()), | ||
} | ||
} | ||
|
||
fn bucket_name(&self) -> String { | ||
match self { | ||
Self::Azure(name) => name.clone(), | ||
Self::Onelake => { | ||
let account_name = | ||
std::env::var("AZURE_STORAGE_ACCOUNT_NAME").unwrap_or(String::from("onelake")); | ||
let container_name = std::env::var("AZURE_STORAGE_CONTAINER_NAME") | ||
.unwrap_or(String::from("delta-rs")); | ||
format!( | ||
"{0}.dfs.fabric.microsoft.com/{1}", | ||
account_name, container_name | ||
) | ||
} | ||
Self::OnelakeAbfs => { | ||
let account_name = | ||
std::env::var("AZURE_STORAGE_ACCOUNT_NAME").unwrap_or(String::from("onelake")); | ||
let container_name = std::env::var("AZURE_STORAGE_CONTAINER_NAME") | ||
.unwrap_or(String::from("delta-rs")); | ||
format!( | ||
"{0}@{1}.dfs.fabric.microsoft.com", | ||
container_name, account_name | ||
) | ||
} | ||
} | ||
} | ||
|
||
fn root_uri(&self) -> String { | ||
format!("az://{}", self.bucket_name()) | ||
} | ||
|
||
fn copy_directory(&self, source: &str, destination: &str) -> std::io::Result<ExitStatus> { | ||
let destination = format!("{}/{}", self.bucket_name(), destination); | ||
az_cli::copy_directory(source, destination) | ||
} | ||
} | ||
|
||
impl Drop for MsftIntegration { | ||
fn drop(&mut self) { | ||
az_cli::delete_container(self.bucket_name()).expect("Failed to drop bucket"); | ||
} | ||
} | ||
|
||
//cli for onelake | ||
mod onelake_cli { | ||
use super::set_env_if_not_set; | ||
/// prepare_env | ||
pub fn prepare_env() { | ||
let token = "jwt-token"; | ||
set_env_if_not_set("AZURE_STORAGE_USE_EMULATOR", "0"); | ||
set_env_if_not_set("AZURE_STORAGE_ACCOUNT_NAME", "daily-onelake"); | ||
set_env_if_not_set( | ||
"AZURE_STORAGE_CONTAINER_NAME", | ||
"86bc63cf-5086-42e0-b16d-6bc580d1dc87", | ||
); | ||
set_env_if_not_set("AZURE_STORAGE_TOKEN", token); | ||
} | ||
} | ||
|
||
/// small wrapper around az cli | ||
mod az_cli { | ||
use super::set_env_if_not_set; | ||
use std::process::{Command, ExitStatus}; | ||
|
||
/// Create a new bucket | ||
pub fn create_container(container_name: impl AsRef<str>) -> std::io::Result<ExitStatus> { | ||
let mut child = Command::new("az") | ||
.args([ | ||
"storage", | ||
"container", | ||
"create", | ||
"-n", | ||
container_name.as_ref(), | ||
]) | ||
.spawn() | ||
.expect("az command is installed"); | ||
child.wait() | ||
} | ||
|
||
/// delete bucket | ||
pub fn delete_container(container_name: impl AsRef<str>) -> std::io::Result<ExitStatus> { | ||
let mut child = Command::new("az") | ||
.args([ | ||
"storage", | ||
"container", | ||
"delete", | ||
"-n", | ||
container_name.as_ref(), | ||
]) | ||
.spawn() | ||
.expect("az command is installed"); | ||
child.wait() | ||
} | ||
|
||
/// copy directory | ||
pub fn copy_directory( | ||
source: impl AsRef<str>, | ||
destination: impl AsRef<str>, | ||
) -> std::io::Result<ExitStatus> { | ||
let mut child = Command::new("az") | ||
.args([ | ||
"storage", | ||
"blob", | ||
"upload-batch", | ||
"-s", | ||
source.as_ref(), | ||
"-d", | ||
destination.as_ref(), | ||
]) | ||
.spawn() | ||
.expect("az command is installed"); | ||
child.wait() | ||
} | ||
|
||
/// prepare_env | ||
pub fn prepare_env() { | ||
set_env_if_not_set("AZURE_STORAGE_USE_EMULATOR", "1"); | ||
set_env_if_not_set("AZURE_STORAGE_ACCOUNT_NAME", "devstoreaccount1"); | ||
set_env_if_not_set("AZURE_STORAGE_ACCOUNT_KEY", "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="); | ||
set_env_if_not_set( | ||
"AZURE_STORAGE_CONNECTION_STRING", | ||
"DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;" | ||
); | ||
} | ||
} |
Oops, something went wrong.