use super::config::{CloudConfig, CloudProvider, credentials_present};
use color_eyre::{Result, eyre::eyre};
use std::fmt::Write as _;
use std::net::IpAddr;
use std::path::Path;
use std::path::PathBuf;
pub async fn run(
provider: Option<CloudProvider>,
tee_required: bool,
bootstrap_env: bool,
write_env_file: Option<PathBuf>,
) -> Result<()> {
let config = CloudConfig::load()?;
let providers = selected_providers(&config, provider);
if providers.is_empty() {
println!("No cloud providers configured in CLI config.");
println!("Run `cargo tangle cloud configure <provider>` first.");
return Err(eyre!("No cloud providers configured"));
}
println!("Cloud preflight checks\n");
let attestation_policy = std::env::var("BLUEPRINT_REMOTE_TEE_ATTESTATION_POLICY")
.unwrap_or_else(|_| "cryptographic".to_string())
.trim()
.to_ascii_lowercase();
let policy_valid = matches!(attestation_policy.as_str(), "cryptographic" | "structural");
let cryptographic = attestation_policy == "cryptographic";
let cryptographic_cmd = std::env::var("BLUEPRINT_REMOTE_TEE_ATTESTATION_VERIFY_CMD").ok();
let mut any_fail = false;
for selected in &providers {
let configured = config.providers.contains_key(selected);
let credentials_ok = credentials_present(selected).await;
let supports_tee = selected.supports_tee();
let tee_ok = !tee_required || supports_tee;
let gcp_cidrs_ok = if *selected == CloudProvider::GCP {
gcp_cidr_prerequisites_met()
} else {
true
};
let mut notes = Vec::new();
if !configured {
notes.push("not configured via `cloud configure`".to_string());
}
if !credentials_ok {
notes.push("credentials/env not detected".to_string());
}
if tee_required && !supports_tee {
notes.push("provider does not support confidential compute".to_string());
}
if *selected == CloudProvider::GCP && !gcp_cidrs_ok {
notes.push(
"set BLUEPRINT_ALLOWED_SSH_CIDRS and BLUEPRINT_ALLOWED_QOS_CIDRS".to_string(),
);
}
let pass = configured && credentials_ok && tee_ok && gcp_cidrs_ok;
if !pass {
any_fail = true;
}
println!("- {}: {}", selected, if pass { "PASS" } else { "FAIL" });
println!(" configured: {}", yes_no(configured));
println!(" credentials: {}", yes_no(credentials_ok));
if tee_required {
println!(" tee-capable: {}", yes_no(supports_tee));
}
if !notes.is_empty() {
println!(" notes: {}", notes.join("; "));
}
}
if tee_required {
println!("\nTEE policy");
println!("- BLUEPRINT_REMOTE_TEE_REQUIRED=true");
println!("- BLUEPRINT_REMOTE_TEE_ATTESTATION_POLICY={attestation_policy}");
if !policy_valid {
any_fail = true;
println!(
"- BLUEPRINT_REMOTE_TEE_ATTESTATION_POLICY: INVALID (use 'cryptographic' or 'structural')"
);
} else if cryptographic {
let ok = cryptographic_cmd
.as_deref()
.map(command_spec_is_executable)
.unwrap_or(false);
if ok {
println!(
"- BLUEPRINT_REMOTE_TEE_ATTESTATION_VERIFY_CMD: configured ({})",
cryptographic_cmd.as_deref().unwrap_or_default()
);
} else {
any_fail = true;
println!(
"- BLUEPRINT_REMOTE_TEE_ATTESTATION_VERIFY_CMD: MISSING or NOT EXECUTABLE (required for cryptographic policy)"
);
}
}
}
if bootstrap_env || write_env_file.is_some() {
let env_output = render_bootstrap_env(
&config,
&providers[0],
tee_required,
&attestation_policy,
cryptographic_cmd.as_deref(),
);
println!("\nBootstrap env");
println!("{env_output}");
if let Some(path) = write_env_file {
std::fs::write(&path, &env_output)?;
println!("Wrote bootstrap env file: {}", path.display());
}
}
if any_fail {
println!("\nPreflight result: FAIL");
return Err(eyre!("Cloud preflight checks failed"));
} else {
println!("\nPreflight result: PASS");
}
Ok(())
}
fn selected_providers(config: &CloudConfig, provider: Option<CloudProvider>) -> Vec<CloudProvider> {
if let Some(p) = provider {
return vec![p];
}
if let Some(default) = &config.default_provider {
return vec![default.clone()];
}
let mut providers: Vec<_> = config.providers.keys().cloned().collect();
providers.sort_by_key(|p| p.to_string());
providers
}
fn yes_no(v: bool) -> &'static str {
if v { "yes" } else { "no" }
}
fn gcp_cidr_prerequisites_met() -> bool {
let ssh_cidrs = std::env::var("BLUEPRINT_ALLOWED_SSH_CIDRS").ok();
let qos_cidrs = std::env::var("BLUEPRINT_ALLOWED_QOS_CIDRS").ok();
gcp_cidr_prerequisites_met_with_values(ssh_cidrs.as_deref(), qos_cidrs.as_deref())
}
fn gcp_cidr_prerequisites_met_with_values(
ssh_cidrs: Option<&str>,
qos_cidrs: Option<&str>,
) -> bool {
ssh_cidrs.is_some_and(cidr_list_is_valid) && qos_cidrs.is_some_and(cidr_list_is_valid)
}
fn cidr_list_is_valid(raw: &str) -> bool {
let mut saw_entry = false;
for cidr in raw.split(',').map(str::trim) {
if cidr.is_empty() {
continue;
}
saw_entry = true;
if !cidr_is_valid(cidr) {
return false;
}
}
saw_entry
}
fn cidr_is_valid(raw: &str) -> bool {
let (ip_raw, prefix_raw) = match raw.split_once('/') {
Some(parts) => parts,
None => return false,
};
let ip: IpAddr = match ip_raw.trim().parse() {
Ok(ip) => ip,
Err(_) => return false,
};
let prefix: u8 = match prefix_raw.trim().parse() {
Ok(prefix) => prefix,
Err(_) => return false,
};
match ip {
IpAddr::V4(_) => prefix <= 32,
IpAddr::V6(_) => prefix <= 128,
}
}
fn command_spec_is_executable(spec: &str) -> bool {
let Some(executable) = spec.split_whitespace().next().filter(|v| !v.is_empty()) else {
return false;
};
let executable_path = Path::new(executable);
if executable_path.is_absolute() || executable.contains(std::path::MAIN_SEPARATOR) {
return is_executable_file(executable_path);
}
std::env::var_os("PATH")
.map(|path| {
std::env::split_paths(&path).any(|dir| {
let candidate = dir.join(executable);
is_executable_file(&candidate)
})
})
.unwrap_or(false)
}
fn is_executable_file(path: &Path) -> bool {
if !path.is_file() {
return false;
}
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::metadata(path)
.map(|metadata| metadata.permissions().mode() & 0o111 != 0)
.unwrap_or(false)
}
#[cfg(not(unix))]
{
true
}
}
fn render_bootstrap_env(
config: &CloudConfig,
provider: &CloudProvider,
tee_required: bool,
attestation_policy: &str,
cryptographic_cmd: Option<&str>,
) -> String {
let mut out = String::new();
let _ = writeln!(&mut out, "# Blueprint remote deployment bootstrap");
let _ = writeln!(
&mut out,
"{}={}",
region_env_var(provider),
region_for_provider(config, provider)
);
let _ = writeln!(
&mut out,
"BLUEPRINT_REMOTE_TEE_REQUIRED={}",
if tee_required { "true" } else { "false" }
);
let _ = writeln!(
&mut out,
"BLUEPRINT_REMOTE_TEE_ATTESTATION_POLICY={}",
attestation_policy
);
if tee_required {
if let Some(backend) = default_tee_backend(provider) {
let _ = writeln!(&mut out, "TEE_BACKEND={backend}");
}
}
if *provider == CloudProvider::GCP {
let _ = writeln!(&mut out, "BLUEPRINT_ALLOWED_SSH_CIDRS=10.0.0.0/8");
let _ = writeln!(&mut out, "BLUEPRINT_ALLOWED_QOS_CIDRS=10.0.0.0/8");
}
if attestation_policy.eq_ignore_ascii_case("cryptographic") {
let value = cryptographic_cmd.unwrap_or("/usr/local/bin/verify-tee-attestation");
let _ = writeln!(
&mut out,
"BLUEPRINT_REMOTE_TEE_ATTESTATION_VERIFY_CMD={value}"
);
}
out
}
pub(super) fn region_env_var(provider: &CloudProvider) -> &'static str {
match provider {
CloudProvider::AWS => "AWS_DEFAULT_REGION",
CloudProvider::GCP => "GCP_DEFAULT_REGION",
CloudProvider::Azure => "AZURE_DEFAULT_REGION",
CloudProvider::DigitalOcean => "DO_REGION",
CloudProvider::Vultr => "VULTR_DEFAULT_REGION",
CloudProvider::Hetzner => "HETZNER_REGION",
CloudProvider::Ovh => "OVH_REGION",
CloudProvider::RunPod => "RUNPOD_REGION",
CloudProvider::LambdaLabs => "LAMBDA_LABS_REGION",
CloudProvider::PrimeIntellect => "PRIME_INTELLECT_REGION",
CloudProvider::VastAi => "VAST_AI_REGION",
CloudProvider::Crusoe => "CRUSOE_REGION",
_ => "BLUEPRINT_CLOUD_REGION",
}
}
fn region_for_provider(config: &CloudConfig, provider: &CloudProvider) -> String {
config
.providers
.get(provider)
.map(|s| s.region.clone())
.unwrap_or_else(|| default_region(provider).to_string())
}
pub(super) fn default_region(provider: &CloudProvider) -> &'static str {
match provider {
CloudProvider::AWS => "us-east-1",
CloudProvider::GCP => "us-central1",
CloudProvider::Azure => "eastus",
CloudProvider::DigitalOcean => "nyc3",
CloudProvider::Vultr => "ewr",
CloudProvider::Hetzner => "fsn1",
CloudProvider::Ovh => "GRA11",
CloudProvider::RunPod => "US",
CloudProvider::LambdaLabs => "us-west-1",
CloudProvider::PrimeIntellect => "us-east",
CloudProvider::VastAi => "any",
CloudProvider::Crusoe => "us-east1",
_ => "",
}
}
fn default_tee_backend(provider: &CloudProvider) -> Option<&'static str> {
match provider {
CloudProvider::AWS => Some("aws-nitro"),
CloudProvider::GCP => Some("gcp-confidential"),
CloudProvider::Azure => Some("azure-skr"),
_ => None,
}
}