OXYGENHUB

Coverage / crates/oxy-strata/src/azure.rs 0.0%

0 of 70 lines run, 0 of 16 functions · run #413 on main · run missed

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
160×
fn env(name: &str) -> Option<String> {
170×
    std::env::var(name).ok().filter(|v| !v.is_empty())
180×
}
19
20
/// The store and the prefix under the container.
21
/// With `principal` (tenant, client id, client secret) given, only it
22
/// is used.
230×
pub fn open(
240×
    url: &StoreUrl,
250×
    principal: Option<(&str, &str, &str)>,
260×
) -> Result<(MicrosoftAzure, String)> {
270×
    let (container, prefix) = match url.path.split_once('/') {
280×
        Some((c, p)) => (c.to_string(), p.to_string()),
290×
        None if !url.path.is_empty() => (url.path.clone(), String::new()),
30
        None => {
310×
            return Err(Error::Backend(format!(
320×
                "{}: an Azure store URL names the account and the container: az://ACCOUNT/CONTAINER/PREFIX",
330×
                url.plain()
340×
            )))
35
        }
36
    };
370×
    if let Some((tenant, id, secret)) = principal {
380×
        let mut b = MicrosoftAzureBuilder::new()
390×
            .with_account(&url.host)
400×
            .with_container_name(&container)
410×
            .with_http_connector(crate::http::UreqConnector)
420×
            .with_client_secret_authorization(id, secret, tenant);
430×
        if let Some(endpoint) = url.param("endpoint") {
440×
            b = b
450×
                .with_endpoint(endpoint.to_string())
460×
                .with_allow_http(endpoint.starts_with("http://"));
470×
        }
480×
        let store = b
490×
            .build()
500×
            .map_err(|e| Error::Backend(format!("{}: {e}", url.plain())))?;
510×
        return Ok((store, prefix));
520×
    }
530×
    let mut b = MicrosoftAzureBuilder::from_env()
540×
        .with_account(&url.host)
550×
        .with_container_name(&container)
560×
        .with_http_connector(crate::http::UreqConnector);
570×
    let principal = (
580×
        env("AZURE_CLIENT_ID").or_else(|| env("AZURE_STORAGE_CLIENT_ID")),
590×
        env("AZURE_CLIENT_SECRET").or_else(|| env("AZURE_STORAGE_CLIENT_SECRET")),
600×
        env("AZURE_TENANT_ID").or_else(|| env("AZURE_STORAGE_TENANT_ID")),
61
    );
620×
    let mut explicit = false;
630×
    if let (Some(id), Some(secret), Some(tenant)) = principal {
640×
        b = b.with_client_secret_authorization(id, secret, tenant);
650×
        explicit = true;
660×
    }
670×
    if env("AZURE_STORAGE_ACCOUNT_KEY").is_some()
680×
        || env("AZURE_STORAGE_ACCESS_KEY").is_some()
690×
        || env("AZURE_STORAGE_SAS_TOKEN").is_some()
700×
        || env("AZURE_STORAGE_SAS_KEY").is_some()
710×
    {
720×
        explicit = true;
730×
    }
740×
    if let Some(endpoint) = url.param("endpoint") {
750×
        b = b
760×
            .with_endpoint(endpoint.to_string())
770×
            .with_allow_http(endpoint.starts_with("http://"));
780×
    }
790×
    if !explicit {
800×
        // What `az login` left behind; the managed identity comes after.
810×
        b = b.with_use_azure_cli(true);
820×
    }
830×
    let store = b
840×
        .build()
850×
        .map_err(|e| Error::Backend(format!("{}: {e}: {HINT}", url.plain())))?;
860×
    Ok((store, prefix))
870×
}