Skip to main content

ahri_tre_lake/
preflight.rs

1//! Read-only configured Lake prerequisites. These checks never open DuckDB,
2//! attach a catalog, resolve credentials, or create scratch/TLS files.
3
4use rustix::fs::{Mode, OFlags, openat};
5use std::{
6    fs::File,
7    net::{TcpStream, ToSocketAddrs},
8    path::{Component, Path},
9    time::Duration,
10};
11
12/// Checks readable directory access through a container-visible absolute path.
13/// Every component is opened without following symlinks. Success establishes
14/// directory access only, not write permission or analytical adapter readiness.
15pub fn filesystem_storage_accessible(root: &Path) -> bool {
16    if !root.is_absolute()
17        || root
18            .components()
19            .any(|c| matches!(c, Component::ParentDir | Component::CurDir))
20    {
21        return false;
22    }
23    let Ok(mut directory) = File::open("/") else {
24        return false;
25    };
26    for component in root.components() {
27        if let Component::Normal(name) = component {
28            let Ok(descriptor) = openat(
29                &directory,
30                Path::new(name),
31                OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC | OFlags::NOFOLLOW,
32                Mode::empty(),
33            ) else {
34                return false;
35            };
36            directory = File::from(descriptor);
37        }
38    }
39    true
40}
41
42/// The endpoint selected by an Effective storage policy; no storage credential
43/// or workload-identity discovery is involved in a reachability check.
44pub enum StorageEndpoint<'a> {
45    AwsS3 { region: &'a str, bucket: &'a str },
46    Https(&'a str),
47}
48
49/// Checks TCP reachability only. Call inside a bounded diagnostic worker: DNS
50/// may block beyond the socket timeout. No HTTP request or TLS handshake occurs.
51pub fn storage_endpoint_reachable(endpoint: StorageEndpoint<'_>) -> bool {
52    let origin = match endpoint {
53        StorageEndpoint::AwsS3 { region, bucket } => {
54            format!("https://{bucket}.s3.{region}.amazonaws.com")
55        }
56        StorageEndpoint::Https(origin) => origin.to_string(),
57    };
58    let Ok(url) = reqwest::Url::parse(&origin) else {
59        return false;
60    };
61    if url.scheme() != "https" || !url.username().is_empty() || url.password().is_some() {
62        return false;
63    }
64    let Some(host) = url.host_str() else {
65        return false;
66    };
67    (
68        host.trim_matches(['[', ']']),
69        url.port_or_known_default().unwrap_or(443),
70    )
71        .to_socket_addrs()
72        .ok()
73        .into_iter()
74        .flatten()
75        .take(4)
76        .any(|address| TcpStream::connect_timeout(&address, Duration::from_millis(750)).is_ok())
77}