OXYGENHUB
# 454236d1108d oxygen / crates / oxy-strata / src / azure.rs
2.6 KiB · Rust · last changed Strata: S3, Google Cloud Storage and Azure Blob Storage stores 1 month ago source blame history raw
1
//! Azure Blob Storage: `az://ACCOUNT/CONTAINER/PREFIX`.
2
//!
3
//! Credentials: a service principal (`AZURE_CLIENT_ID`,
4
//! `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`, as the Azure SDKs name them,
5
//! or the `AZURE_STORAGE_*` spellings), an account key
6
//! (`AZURE_STORAGE_ACCOUNT_KEY`), a SAS token (`AZURE_STORAGE_SAS_TOKEN`),
7
//! else what `az login` left behind, else the machine's managed
8
//! identity. `?endpoint=URL` points at an emulator.
9
10
use crate::StoreUrl;
11
use object_store::azure::{MicrosoftAzure, MicrosoftAzureBuilder};
12
use oxy_core::strata::{Error, Result};
13
14
pub const HINT: &str = "run `az login`, or set AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID for a service principal, or AZURE_STORAGE_ACCOUNT_KEY";
15
16
fn env(name: &str) -> Option<String> {
17
    std::env::var(name).ok().filter(|v| !v.is_empty())
18
}
19
20
/// The store and the prefix under the container.
21
pub fn open(url: &StoreUrl) -> Result<(MicrosoftAzure, String)> {
22
    let (container, prefix) = match url.path.split_once('/') {
23
        Some((c, p)) => (c.to_string(), p.to_string()),
24
        None if !url.path.is_empty() => (url.path.clone(), String::new()),
25
        None => {
26
            return Err(Error::Backend(format!(
27
                "{}: an Azure store URL names the account and the container: az://ACCOUNT/CONTAINER/PREFIX",
28
                url.plain()
29
            )))
30
        }
31
    };
32
    let mut b = MicrosoftAzureBuilder::from_env()
33
        .with_account(&url.host)
34
        .with_container_name(&container);
35
    let principal = (
36
        env("AZURE_CLIENT_ID").or_else(|| env("AZURE_STORAGE_CLIENT_ID")),
37
        env("AZURE_CLIENT_SECRET").or_else(|| env("AZURE_STORAGE_CLIENT_SECRET")),
38
        env("AZURE_TENANT_ID").or_else(|| env("AZURE_STORAGE_TENANT_ID")),
39
    );
40
    let mut explicit = false;
41
    if let (Some(id), Some(secret), Some(tenant)) = principal {
42
        b = b.with_client_secret_authorization(id, secret, tenant);
43
        explicit = true;
44
    }
45
    if env("AZURE_STORAGE_ACCOUNT_KEY").is_some()
46
        || env("AZURE_STORAGE_ACCESS_KEY").is_some()
47
        || env("AZURE_STORAGE_SAS_TOKEN").is_some()
48
        || env("AZURE_STORAGE_SAS_KEY").is_some()
49
    {
50
        explicit = true;
51
    }
52
    if let Some(endpoint) = url.param("endpoint") {
53
        b = b
54
            .with_endpoint(endpoint.to_string())
55
            .with_allow_http(endpoint.starts_with("http://"));
56
    }
57
    if !explicit {
58
        // What `az login` left behind; the managed identity comes after.
59
        b = b.with_use_azure_cli(true);
60
    }
61
    let store = b
62
        .build()
63
        .map_err(|e| Error::Backend(format!("{}: {e}: {HINT}", url.plain())))?;
64
    Ok((store, prefix))
65
}