diff --git a/Cargo.lock b/Cargo.lock index 51a8a74..70b6fa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -274,6 +274,7 @@ dependencies = [ "serde_yaml", "sha256", "simplelog", + "sysinfo", "temp-env", "tempfile", "tokio", @@ -333,6 +334,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -726,7 +752,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -999,6 +1025,15 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1284,6 +1319,26 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1653,6 +1708,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.30.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "serde", + "windows", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -2127,6 +2198,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.0", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -2136,6 +2217,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index eb292d4..60d3b97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ git2 = "0.18.3" nestify = "0.3.3" gql_client = { git = "https://github.com/adriencaccia/gql-client-rs" } serde_yaml = "0.9.34" +sysinfo = { version = "0.30.12", features = ["serde"] } [dev-dependencies] temp-env = { version = "0.3.6", features = ["async_closure"] } diff --git a/src/run/check_system.rs b/src/run/check_system.rs new file mode 100644 index 0000000..0b45a31 --- /dev/null +++ b/src/run/check_system.rs @@ -0,0 +1,77 @@ +use std::process::Command; + +use serde::{Deserialize, Serialize}; +use sysinfo::System; + +use crate::prelude::*; + +fn get_user() -> Result { + let user_output = Command::new("whoami") + .output() + .map_err(|_| anyhow!("Failed to get user info"))?; + if !user_output.status.success() { + bail!("Failed to get user info"); + } + let output_str = + String::from_utf8(user_output.stdout).map_err(|_| anyhow!("Failed to parse user info"))?; + Ok(output_str.trim().to_string()) +} + +#[derive(Eq, PartialEq, Hash, Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SystemInfo { + pub os: String, + pub os_version: String, + pub arch: String, + pub host: String, + pub user: String, +} + +#[cfg(test)] +impl SystemInfo { + pub fn test() -> Self { + SystemInfo { + os: "Ubuntu".to_string(), + os_version: "20.04".to_string(), + arch: "x86_64".to_string(), + host: "host".to_string(), + user: "user".to_string(), + } + } +} + +/// Checks if the system is supported and returns the system info +/// +/// Supported systems: +/// - Ubuntu 20.04 on x86_64 +/// - Ubuntu 22.04 on x86_64 +/// - Debian 11 on x86_64 +/// - Debian 12 on x86_64 +pub fn check_system() -> Result { + let os = System::name().ok_or(anyhow!("Failed to get OS name"))?; + let os_version = System::os_version().ok_or(anyhow!("Failed to get OS version"))?; + debug!("OS: {}, Version: {}", os, os_version); + match (os.as_str(), os_version.as_str()) { + ("Ubuntu", "20.04") | ("Ubuntu", "22.04") | ("Debian", "11") | ("Debian", "12") => (), + ("Ubuntu", _) => bail!("Only Ubuntu 20.04 and 22.04 are supported at the moment"), + ("Debian", _) => bail!("Only Debian 11 and 12 are supported at the moment"), + _ => bail!("Only Ubuntu and Debian are supported at the moment"), + } + let arch = System::cpu_arch().ok_or(anyhow!("Failed to get CPU architecture"))?; + debug!("Arch: {}", arch); + if arch != "x86_64" && arch != "arm64" { + bail!("Only x86_64 and arm64 are supported at the moment"); + } + let user = get_user()?; + debug!("User: {}", user); + let host = System::host_name().ok_or(anyhow!("Failed to get host name"))?; + debug!("Host: {}", host); + + Ok(SystemInfo { + os, + os_version, + arch, + host, + user, + }) +} diff --git a/src/run/ci_provider/provider.rs b/src/run/ci_provider/provider.rs index 4e5c769..fb581b4 100644 --- a/src/run/ci_provider/provider.rs +++ b/src/run/ci_provider/provider.rs @@ -2,6 +2,7 @@ use git2::Repository; use simplelog::SharedLogger; use crate::prelude::*; +use crate::run::check_system::SystemInfo; use crate::run::config::Config; use crate::run::uploader::{Runner, UploadMetadata}; @@ -71,13 +72,18 @@ pub trait CIProvider { /// let instruments = Instruments::new(); /// let metadata = provider.get_upload_metadata(&config, "abc123").unwrap(); /// ``` - fn get_upload_metadata(&self, config: &Config, archive_hash: &str) -> Result { + fn get_upload_metadata( + &self, + config: &Config, + system_info: &SystemInfo, + archive_hash: &str, + ) -> Result { let provider_metadata = self.get_provider_metadata()?; let commit_hash = get_commit_hash(&provider_metadata.repository_root_path)?; Ok(UploadMetadata { - version: Some(2), + version: Some(3), tokenless: config.token.is_none(), provider_metadata, profile_md5: archive_hash.into(), @@ -86,6 +92,7 @@ pub trait CIProvider { name: "codspeed-runner".into(), version: crate::VERSION.into(), instruments: config.instruments.get_active_instrument_names(), + system_info: system_info.clone(), }, platform: self.get_provider_slug().into(), }) diff --git a/src/run/mod.rs b/src/run/mod.rs index 7506f45..bf6dcd3 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -5,6 +5,7 @@ use crate::run::{config::Config, logger::Logger}; use crate::VERSION; use clap::Args; +mod check_system; pub mod ci_provider; mod helpers; mod instruments; @@ -118,12 +119,13 @@ pub async fn run(args: RunArgs, api_client: &CodSpeedAPIClient) -> Result<()> { config.set_token(codspeed_config.auth.token.clone()); } - let run_data = runner::run(&config).await?; + let system_info = check_system::check_system()?; + let run_data = runner::run(&config, &system_info).await?; if !config.skip_upload { start_group!("Upload the results"); logger.persist_log_to_profile_folder(&run_data)?; - let upload_result = uploader::upload(&config, &provider, &run_data).await?; + let upload_result = uploader::upload(&config, &system_info, &provider, &run_data).await?; end_group!(); if provider.get_provider_slug() == "local" { diff --git a/src/run/runner/check_system.rs b/src/run/runner/check_system.rs deleted file mode 100644 index d51469d..0000000 --- a/src/run/runner/check_system.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::process::Command; - -use crate::prelude::*; - -/// Returns the OS and version of the system -/// -/// ## Example output -/// ``` -/// ("Ubuntu", "20.04") -/// ("Ubuntu", "22.04") -/// ("Debian", "11") -/// ("Debian", "12") -/// ``` -fn get_os_details() -> Result<(String, String)> { - let lsb_output = Command::new("lsb_release") - .args(["-i", "-r", "-s"]) - .output() - .map_err(|_| anyhow!("Failed to get system info"))?; - if !lsb_output.status.success() { - bail!("Failed to get system info"); - } - let output_str = - String::from_utf8(lsb_output.stdout).map_err(|_| anyhow!("Failed to parse system info"))?; - let mut lines = output_str.trim().lines(); - let os = lines - .next() - .ok_or_else(|| anyhow!("Failed to get OS info"))?; - let os_version = lines - .next() - .ok_or_else(|| anyhow!("Failed to get OS version"))?; - Ok((os.to_string(), os_version.to_string())) -} - -/// NOTE: Since this relies on `dpkg` this will only work on Debian based systems -fn get_arch() -> Result { - let arch_output = Command::new("dpkg") - .args(["--print-architecture"]) - .output() - .map_err(|_| anyhow!("Failed to get architecture info"))?; - if !arch_output.status.success() { - bail!("Failed to get architecture info"); - } - let output_str = String::from_utf8(arch_output.stdout) - .map_err(|_| anyhow!("Failed to parse architecture info"))?; - Ok(output_str.trim().to_string()) -} - -#[derive(Eq, PartialEq, Hash)] -pub struct SystemInfo { - pub os: String, - pub os_version: String, - pub arch: String, -} - -/// Checks if the system is supported -/// -/// Supported systems: -/// - Ubuntu 20.04 on amd64 -/// - Ubuntu 22.04 on amd64 -/// - Debian 11 on amd64 -/// - Debian 12 on amd64 -pub fn check_system() -> Result { - let (os, os_version) = get_os_details()?; - debug!("OS: {}, Version: {}", os, os_version); - match (os.as_str(), os_version.as_str()) { - ("Ubuntu", "20.04") | ("Ubuntu", "22.04") | ("Debian", "11") | ("Debian", "12") => (), - ("Ubuntu", _) => bail!("Only Ubuntu 20.04 and 22.04 are supported at the moment"), - ("Debian", _) => bail!("Only Debian 11 and 12 are supported at the moment"), - _ => bail!("Only Ubuntu and Debian are supported at the moment"), - } - let arch = get_arch()?; - debug!("Arch: {}", arch); - if arch != "amd64" && arch != "arm64" { - bail!("Only amd64 and arm64 are supported at the moment"); - } - Ok(SystemInfo { - os, - os_version, - arch, - }) -} diff --git a/src/run/runner/mod.rs b/src/run/runner/mod.rs index ecd9468..5ae8c8c 100644 --- a/src/run/runner/mod.rs +++ b/src/run/runner/mod.rs @@ -1,4 +1,3 @@ -mod check_system; mod helpers; mod run; mod setup; diff --git a/src/run/runner/run.rs b/src/run/runner/run.rs index 1f2a8d5..8afa8cd 100644 --- a/src/run/runner/run.rs +++ b/src/run/runner/run.rs @@ -1,10 +1,11 @@ use crate::prelude::*; -use crate::run::{config::Config, instruments::mongo_tracer::MongoTracer}; +use crate::run::{ + check_system::SystemInfo, config::Config, instruments::mongo_tracer::MongoTracer, +}; use std::path::PathBuf; use super::{ - check_system::check_system, helpers::{perf_maps::harvest_perf_maps, profile_folder::create_profile_folder}, setup::setup, valgrind, @@ -14,11 +15,10 @@ pub struct RunData { pub profile_folder: PathBuf, } -pub async fn run(config: &Config) -> Result { +pub async fn run(config: &Config, system_info: &SystemInfo) -> Result { if !config.skip_setup { start_group!("Prepare the environment"); - let system_info = check_system()?; - setup(&system_info, config).await?; + setup(system_info, config).await?; end_group!(); } //TODO: add valgrind version check diff --git a/src/run/runner/setup.rs b/src/run/runner/setup.rs index e484db2..6787092 100644 --- a/src/run/runner/setup.rs +++ b/src/run/runner/setup.rs @@ -1,14 +1,12 @@ use std::{ - collections::HashMap, env, process::{Command, Stdio}, }; -use lazy_static::lazy_static; use url::Url; -use super::{check_system::SystemInfo, helpers::download_file::download_file}; -use crate::run::config::Config; +use super::helpers::download_file::download_file; +use crate::run::{check_system::SystemInfo, config::Config}; use crate::{prelude::*, MONGODB_TRACER_VERSION, VALGRIND_CODSPEED_VERSION}; /// Run a command with sudo if available @@ -40,55 +38,23 @@ fn run_with_sudo(command_args: &[&str]) -> Result<()> { Ok(()) } -lazy_static! { - static ref SYSTEM_INFO_TO_CODSPEED_VALGRIND_FILENAME: HashMap = { - let mut m = HashMap::new(); - m.insert( - SystemInfo { - os: "Ubuntu".to_string(), - os_version: "20.04".to_string(), - arch: "amd64".to_string(), - }, - format!( - "valgrind_{}_ubuntu-{}_amd64.deb", - VALGRIND_CODSPEED_VERSION, "20.04" - ), - ); - m.insert( - SystemInfo { - os: "Ubuntu".to_string(), - os_version: "22.04".to_string(), - arch: "amd64".to_string(), - }, - format!( - "valgrind_{}_ubuntu-{}_amd64.deb", - VALGRIND_CODSPEED_VERSION, "22.04" - ), - ); - m.insert( - SystemInfo { - os: "Debian".to_string(), - os_version: "11".to_string(), - arch: "amd64".to_string(), - }, - format!( - "valgrind_{}_ubuntu-{}_amd64.deb", - VALGRIND_CODSPEED_VERSION, "20.04" - ), - ); - m.insert( - SystemInfo { - os: "Debian".to_string(), - os_version: "12".to_string(), - arch: "amd64".to_string(), - }, - format!( - "valgrind_{}_ubuntu-{}_amd64.deb", - VALGRIND_CODSPEED_VERSION, "20.04" - ), - ); - m +fn get_codspeed_valgrind_filename(system_info: &SystemInfo) -> Result { + let version = match ( + system_info.os.as_str(), + system_info.os_version.as_str(), + system_info.arch.as_str(), + ) { + ("Ubuntu", "20.04", "x86_64") => "20.04", + ("Ubuntu", "22.04", "x86_64") => "22.04", + ("Debian", "11", "x86_64") => "20.04", + ("Debian", "12", "x86_64") => "20.04", + _ => bail!("Unsupported system"), }; + + Ok(format!( + "valgrind_{}_ubuntu_{}_amd64.deb", + VALGRIND_CODSPEED_VERSION, version + )) } fn check_installed_valgrind() -> Result { @@ -119,9 +85,7 @@ async fn install_valgrind(system_info: &SystemInfo) -> Result<()> { let valgrind_deb_url = format!( "https://github.com/CodSpeedHQ/valgrind-codspeed/releases/download/{}/{}", VALGRIND_CODSPEED_VERSION, - SYSTEM_INFO_TO_CODSPEED_VALGRIND_FILENAME - .get(system_info) - .context("Unsupported system")? + get_codspeed_valgrind_filename(system_info)? ); let deb_path = env::temp_dir().join("valgrind-codspeed.deb"); download_file(&Url::parse(valgrind_deb_url.as_str()).unwrap(), &deb_path).await?; @@ -174,18 +138,32 @@ mod tests { use super::*; #[test] - fn test_system_info_to_codspeed_valgrind_version() { + fn test_system_info_to_codspeed_valgrind_version_ubuntu() { + let system_info = SystemInfo { + os: "Ubuntu".to_string(), + os_version: "22.04".to_string(), + arch: "x86_64".to_string(), + host: "host".to_string(), + user: "user".to_string(), + }; + assert_eq!( + get_codspeed_valgrind_filename(&system_info).unwrap(), + "valgrind_3.21.0-0codspeed1_ubuntu_22.04_amd64.deb" + ); + } + + #[test] + fn test_system_info_to_codspeed_valgrind_version_debian() { let system_info = SystemInfo { os: "Debian".to_string(), os_version: "11".to_string(), - arch: "amd64".to_string(), + arch: "x86_64".to_string(), + host: "host".to_string(), + user: "user".to_string(), }; assert_eq!( - SYSTEM_INFO_TO_CODSPEED_VALGRIND_FILENAME[&system_info], - format!( - "valgrind_{}_ubuntu-{}_amd64.deb", - VALGRIND_CODSPEED_VERSION, "20.04" - ) + get_codspeed_valgrind_filename(&system_info).unwrap(), + "valgrind_3.21.0-0codspeed1_ubuntu_20.04_amd64.deb" ); } } diff --git a/src/run/uploader/interfaces.rs b/src/run/uploader/interfaces.rs index 9297a47..72423d8 100644 --- a/src/run/uploader/interfaces.rs +++ b/src/run/uploader/interfaces.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::run::{ci_provider::interfaces::ProviderMetadata, instruments::InstrumentNames}; +use crate::run::{ + check_system::SystemInfo, ci_provider::interfaces::ProviderMetadata, + instruments::InstrumentNames, +}; #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] @@ -21,6 +24,8 @@ pub struct Runner { pub name: String, pub version: String, pub instruments: Vec, + #[serde(flatten)] + pub system_info: SystemInfo, } #[derive(Deserialize, Serialize, Debug)] diff --git a/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash.snap b/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash.snap index d41c738..118e888 100644 --- a/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash.snap +++ b/src/run/uploader/snapshots/codspeed_runner__run__uploader__upload_metadata__tests__get_metadata_hash.snap @@ -3,7 +3,7 @@ source: src/run/uploader/upload_metadata.rs expression: upload_metadata --- { - "version": 2, + "version": 3, "tokenless": true, "profileMd5": "jp/k05RKuqP3ERQuIIvx4Q==", "runner": { @@ -11,7 +11,12 @@ expression: upload_metadata "version": "2.1.0", "instruments": [ "MongoDB" - ] + ], + "os": "Ubuntu", + "osVersion": "20.04", + "arch": "x86_64", + "host": "host", + "user": "user" }, "platform": "github-actions", "commitHash": "5bd77cb0da72bef094893ed45fb793ff16ecfbe3", diff --git a/src/run/uploader/upload.rs b/src/run/uploader/upload.rs index c51446f..530a970 100644 --- a/src/run/uploader/upload.rs +++ b/src/run/uploader/upload.rs @@ -1,4 +1,7 @@ -use crate::run::{ci_provider::CIProvider, config::Config, runner::RunData, uploader::UploadError}; +use crate::run::{ + check_system::SystemInfo, ci_provider::CIProvider, config::Config, runner::RunData, + uploader::UploadError, +}; use crate::{prelude::*, request_client::REQUEST_CLIENT}; use async_compression::tokio::write::GzipEncoder; use base64::{engine::general_purpose, Engine as _}; @@ -81,6 +84,7 @@ pub struct UploadResult { #[allow(clippy::borrowed_box)] pub async fn upload( config: &Config, + system_info: &SystemInfo, provider: &Box, run_data: &RunData, ) -> Result { @@ -88,7 +92,7 @@ pub async fn upload( debug!("CI provider detected: {:#?}", provider.get_provider_name()); - let upload_metadata = provider.get_upload_metadata(config, &archive_hash)?; + let upload_metadata = provider.get_upload_metadata(config, system_info, &archive_hash)?; debug!("Upload metadata: {:#?}", upload_metadata); if upload_metadata.tokenless { let hash = upload_metadata.get_hash(); @@ -115,7 +119,6 @@ mod tests { use url::Url; use super::*; - use crate::run::runner::RunData; use std::path::PathBuf; // TODO: remove the ignore when implementing network mocking @@ -134,6 +137,7 @@ mod tests { env!("CARGO_MANIFEST_DIR") )), }; + let system_info = SystemInfo::test(); async_with_vars( [ ("GITHUB_ACTIONS", Some("true")), @@ -164,7 +168,9 @@ mod tests { ], async { let provider = crate::run::ci_provider::get_provider(&config).unwrap(); - upload(&config, &provider, &run_data).await.unwrap(); + upload(&config, &system_info, &provider, &run_data) + .await + .unwrap(); }, ) .await; diff --git a/src/run/uploader/upload_metadata.rs b/src/run/uploader/upload_metadata.rs index 94d36d5..081bfa2 100644 --- a/src/run/uploader/upload_metadata.rs +++ b/src/run/uploader/upload_metadata.rs @@ -14,6 +14,7 @@ mod tests { use insta::assert_json_snapshot; use crate::run::{ + check_system::SystemInfo, ci_provider::interfaces::{GhData, ProviderMetadata, RunEvent, Sender}, instruments::InstrumentNames, uploader::{Runner, UploadMetadata}, @@ -22,13 +23,14 @@ mod tests { #[test] fn test_get_metadata_hash() { let upload_metadata = UploadMetadata { - version: Some(2), + version: Some(3), tokenless: true, profile_md5: "jp/k05RKuqP3ERQuIIvx4Q==".into(), runner: Runner { name: "codspeed-runner".into(), version: "2.1.0".into(), instruments: vec![InstrumentNames::MongoDB], + system_info: SystemInfo::test(), }, platform: "github-actions".into(), commit_hash: "5bd77cb0da72bef094893ed45fb793ff16ecfbe3".into(), @@ -54,7 +56,7 @@ mod tests { let hash = upload_metadata.get_hash(); assert_eq!( hash, - "8beb149c4645c666156e24fe0f68d24a63cec1d7756f35dd17cab1d84528ed7b" + "ada5057b0c440844a1558eed80a1993a41756984cc6147fdef459ce8a289f1d7" ); assert_json_snapshot!(upload_metadata); }