diff --git a/Cargo.lock b/Cargo.lock index bf1dc609..c0dca724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,9 +772,11 @@ dependencies = [ "boavizta_api_sdk", "chrono", "clap", + "csv", "isocountry", "log", "loggerv", + "once_cell", "pkg-version", "prometheus-client", "rocket", @@ -863,6 +865,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "darling" version = "0.13.4" @@ -1802,9 +1825,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl-probe" diff --git a/cloud-scanner-cli/Cargo.toml b/cloud-scanner-cli/Cargo.toml index 71dace52..2f48cca6 100644 --- a/cloud-scanner-cli/Cargo.toml +++ b/cloud-scanner-cli/Cargo.toml @@ -6,9 +6,11 @@ version = "3.0.1" [dependencies] chrono = { version = "^0.4", features = ["serde"] } +csv = "1.3.0" isocountry = "^0.3" log = "0.4" loggerv = "0.7" +once_cell = "1.20.2" pkg-version = "*" prometheus-client = "*" serde = "^1.0" diff --git a/cloud-scanner-cli/csv/cloud_providers_regions.csv b/cloud-scanner-cli/csv/cloud_providers_regions.csv new file mode 100644 index 00000000..03572cfe --- /dev/null +++ b/cloud-scanner-cli/csv/cloud_providers_regions.csv @@ -0,0 +1,32 @@ +cloud provider,region,country code +aws,af-south-1,ZAF +aws,ap-east-1,HKG +aws,ap-northeast-1,JPN +aws,ap-northeast-2,KOR +aws,ap-northeast-3,JPN +aws,ap-south-1,IND +aws,ap-south-2,IND +aws,ap-southeast-1,SGP +aws,ap-southeast-2,AUS +aws,ap-southeast-3,IDN +aws,ap-southeast-4,AUS +aws,ca-central-1,CAN +aws,ca-west-1,CAN +aws,cn-north-1,CHN +aws,cn-northwest-1,CHN +aws,eu-central-1,DEU +aws,eu-central-2,CHE +aws,eu-north-1,SWE +aws,eu-south-1,ITA +aws,eu-south-2,ESP +aws,eu-west-1,IRL +aws,eu-west-2,GBR +aws,eu-west-3,FRA +aws,il-central-1,ISR +aws,me-central-1,ARE +aws,me-south-1,BHR +aws,sa-east-1,BRA +aws,us-east-1,USA +aws,us-east-2,USA +aws,us-west-1,USA +aws,us-west-2,USA diff --git a/cloud-scanner-cli/src/usage_location.rs b/cloud-scanner-cli/src/usage_location.rs index e55f55d8..2d2042ed 100644 --- a/cloud-scanner-cli/src/usage_location.rs +++ b/cloud-scanner-cli/src/usage_location.rs @@ -1,11 +1,20 @@ //! The location where cloud resources are running. - -use isocountry::CountryCode; -use rocket_okapi::okapi::schemars; +use csv::ReaderBuilder; +use log::error; +use once_cell::sync::Lazy; use rocket_okapi::okapi::schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::error::Error; +use std::sync::Mutex; use thiserror::Error; +// Use of static to load the region-country map once +static REGION_COUNTRY_MAP: Lazy>> = Lazy::new(|| { + let map = load_region_country_map().unwrap_or_default(); + Mutex::new(map) +}); + #[derive(Error, Debug)] pub enum RegionError { #[error("Unsupported region ({0})")] @@ -28,57 +37,45 @@ impl TryFrom<&str> for UsageLocation { let cc = get_country_from_aws_region(aws_region)?; Ok(UsageLocation { aws_region: String::from(aws_region), - iso_country_code: cc.alpha3().to_owned(), + iso_country_code: cc, }) } type Error = RegionError; } +/// Load the region-country map from a CSV file +fn load_region_country_map() -> Result, Box> { + let csv_content = include_str!("../csv/cloud_providers_regions.csv"); + + let mut reader = ReaderBuilder::new() + .has_headers(true) + .from_reader(csv_content.as_bytes()); + + let mut region_country_map = HashMap::new(); + + for result in reader.records() { + let record = result?; + let region = &record[1]; // AWS region + let country_code = &record[2]; // country code + region_country_map.insert(region.to_string(), country_code.to_string()); + } + + Ok(region_country_map) +} + /// Converts AWS region as String into an ISO country code, returns FRA if not found -/// -/// TODO! : do not convert to FRA by default, should rather fail explicitly if region is not found. -fn get_country_from_aws_region(aws_region: &str) -> Result { - let cc: CountryCode = match aws_region { - "af-south-1" => CountryCode::ZAF, - "ap-east-1" => CountryCode::HKG, - "ap-northeast-1" => CountryCode::JPN, - "ap-northeast-2" => CountryCode::KOR, - "ap-northeast-3" => CountryCode::JPN, - "ap-south-1" => CountryCode::IND, - "ap-south-2" => CountryCode::IND, - "ap-southeast-1" => CountryCode::SGP, - "ap-southeast-2" => CountryCode::AUS, - "ap-southeast-3" => CountryCode::IDN, - "ap-southeast-4" => CountryCode::AUS, - "ca-central-1" => CountryCode::CAN, - "ca-west-1" => CountryCode::CAN, - "cn-north-1" => CountryCode::CHN, - "cn-northwest-1" => CountryCode::CHN, - "eu-central-1" => CountryCode::DEU, - "eu-central-2" => CountryCode::CHE, - "eu-north-1" => CountryCode::SWE, - "eu-south-1" => CountryCode::ITA, - "eu-south-2" => CountryCode::ESP, - "eu-west-1" => CountryCode::IRL, - "eu-west-2" => CountryCode::GBR, - "eu-west-3" => CountryCode::FRA, - "il-central-1" => CountryCode::ISR, - "me-central-1" => CountryCode::ARE, - "me-south-1" => CountryCode::BHR, - "sa-east-1" => CountryCode::BRA, - "us-east-1" => CountryCode::USA, - "us-east-2" => CountryCode::USA, - "us-west-1" => CountryCode::USA, - "us-west-2" => CountryCode::USA, - _ => { +fn get_country_from_aws_region(aws_region: &str) -> Result { + let map = REGION_COUNTRY_MAP.lock().unwrap(); + match map.get(aws_region) { + Some(country_code) => Ok(country_code.to_string()), + None => { error!( "Unsupported region: unable to match aws region [{}] to country code", aws_region ); return Err(RegionError::UnsupportedRegion(String::from(aws_region))); } - }; - Ok(cc) + } } #[cfg(test)]