| 1 | |
|
| 2 | |
|
| 3 | |
|
| 4 | |
|
| 5 | |
|
| 6 | |
|
| 7 | |
|
| 8 | |
|
| 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 | 0× | fn env(name: &str) -> Option<String> { |
| 17 | 0× | std::env::var(name).ok().filter(|v| !v.is_empty()) |
| 18 | 0× | } |
| 19 | |
|
| 20 | |
|
| 21 | |
|
| 22 | |
|
| 23 | 0× | pub fn open( |
| 24 | 0× | url: &StoreUrl, |
| 25 | 0× | principal: Option<(&str, &str, &str)>, |
| 26 | 0× | ) -> Result<(MicrosoftAzure, String)> { |
| 27 | 0× | let (container, prefix) = match url.path.split_once('/') { |
| 28 | 0× | Some((c, p)) => (c.to_string(), p.to_string()), |
| 29 | 0× | None if !url.path.is_empty() => (url.path.clone(), String::new()), |
| 30 | | None => { |
| 31 | 0× | return Err(Error::Backend(format!( |
| 32 | 0× | "{}: an Azure store URL names the account and the container: az://ACCOUNT/CONTAINER/PREFIX", |
| 33 | 0× | url.plain() |
| 34 | 0× | ))) |
| 35 | | } |
| 36 | | }; |
| 37 | 0× | if let Some((tenant, id, secret)) = principal { |
| 38 | 0× | let mut b = MicrosoftAzureBuilder::new() |
| 39 | 0× | .with_account(&url.host) |
| 40 | 0× | .with_container_name(&container) |
| 41 | 0× | .with_http_connector(crate::http::UreqConnector) |
| 42 | 0× | .with_client_secret_authorization(id, secret, tenant); |
| 43 | 0× | if let Some(endpoint) = url.param("endpoint") { |
| 44 | 0× | b = b |
| 45 | 0× | .with_endpoint(endpoint.to_string()) |
| 46 | 0× | .with_allow_http(endpoint.starts_with("http://")); |
| 47 | 0× | } |
| 48 | 0× | let store = b |
| 49 | 0× | .build() |
| 50 | 0× | .map_err(|e| Error::Backend(format!("{}: {e}", url.plain())))?; |
| 51 | 0× | return Ok((store, prefix)); |
| 52 | 0× | } |
| 53 | 0× | let mut b = MicrosoftAzureBuilder::from_env() |
| 54 | 0× | .with_account(&url.host) |
| 55 | 0× | .with_container_name(&container) |
| 56 | 0× | .with_http_connector(crate::http::UreqConnector); |
| 57 | 0× | let principal = ( |
| 58 | 0× | env("AZURE_CLIENT_ID").or_else(|| env("AZURE_STORAGE_CLIENT_ID")), |
| 59 | 0× | env("AZURE_CLIENT_SECRET").or_else(|| env("AZURE_STORAGE_CLIENT_SECRET")), |
| 60 | 0× | env("AZURE_TENANT_ID").or_else(|| env("AZURE_STORAGE_TENANT_ID")), |
| 61 | | ); |
| 62 | 0× | let mut explicit = false; |
| 63 | 0× | if let (Some(id), Some(secret), Some(tenant)) = principal { |
| 64 | 0× | b = b.with_client_secret_authorization(id, secret, tenant); |
| 65 | 0× | explicit = true; |
| 66 | 0× | } |
| 67 | 0× | if env("AZURE_STORAGE_ACCOUNT_KEY").is_some() |
| 68 | 0× | || env("AZURE_STORAGE_ACCESS_KEY").is_some() |
| 69 | 0× | || env("AZURE_STORAGE_SAS_TOKEN").is_some() |
| 70 | 0× | || env("AZURE_STORAGE_SAS_KEY").is_some() |
| 71 | 0× | { |
| 72 | 0× | explicit = true; |
| 73 | 0× | } |
| 74 | 0× | if let Some(endpoint) = url.param("endpoint") { |
| 75 | 0× | b = b |
| 76 | 0× | .with_endpoint(endpoint.to_string()) |
| 77 | 0× | .with_allow_http(endpoint.starts_with("http://")); |
| 78 | 0× | } |
| 79 | 0× | if !explicit { |
| 80 | 0× | |
| 81 | 0× | b = b.with_use_azure_cli(true); |
| 82 | 0× | } |
| 83 | 0× | let store = b |
| 84 | 0× | .build() |
| 85 | 0× | .map_err(|e| Error::Backend(format!("{}: {e}: {HINT}", url.plain())))?; |
| 86 | 0× | Ok((store, prefix)) |
| 87 | 0× | } |