Skip to content

Commit a7eb6e2

Browse files
feat(system): represent the host OS as a SupportedOs enum with per-executor support gates
Only supported Oses can be represented, the runner will bail if it detects a non supported os. Then, each executor can define its support level for the current system depending on the Os and, for example, the linux distribution/version.
1 parent 61422a1 commit a7eb6e2

15 files changed

Lines changed: 374 additions & 146 deletions

File tree

src/cli/setup.rs

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::executor::{ToolInstallStatus, get_all_executors};
1+
use crate::executor::{ExecutorSupport, ToolInstallStatus, get_all_executors};
22
use crate::prelude::*;
33
use crate::system::SystemInfo;
44
use clap::{Args, Subcommand};
@@ -22,10 +22,7 @@ enum SetupCommands {
2222
pub async fn run(args: SetupArgs, setup_cache_dir: Option<&Path>) -> Result<()> {
2323
match args.command {
2424
None => setup(setup_cache_dir).await,
25-
Some(SetupCommands::Status) => {
26-
status();
27-
Ok(())
28-
}
25+
Some(SetupCommands::Status) => status(),
2926
}
3027
}
3128

@@ -34,41 +31,81 @@ async fn setup(setup_cache_dir: Option<&Path>) -> Result<()> {
3431
let executors = get_all_executors();
3532
start_group!("Setting up the environment for all executors");
3633
for executor in executors {
37-
info!(
38-
"Setting up the environment for the executor: {}",
39-
executor.name()
40-
);
41-
executor.setup(&system_info, setup_cache_dir).await?;
34+
match executor.support_level(&system_info) {
35+
ExecutorSupport::Unsupported => {
36+
info!(
37+
"Skipping setup for the {} executor: not supported on {}",
38+
executor.name(),
39+
system_info.os
40+
);
41+
}
42+
ExecutorSupport::RequiresManualInstallation => {
43+
info!(
44+
"Skipping automatic setup for the {} executor on {}; install required tooling manually.",
45+
executor.name(),
46+
system_info.os
47+
);
48+
}
49+
ExecutorSupport::FullySupported => {
50+
info!(
51+
"Setting up the environment for the executor: {}",
52+
executor.name()
53+
);
54+
executor.setup(&system_info, setup_cache_dir).await?;
55+
}
56+
}
4257
}
4358
info!("Environment setup completed");
4459
end_group!();
4560
Ok(())
4661
}
4762

48-
pub fn status() {
63+
pub fn status() -> Result<()> {
64+
let system_info = SystemInfo::new()?;
4965
info!("{}", style("Tools").bold());
5066
for executor in get_all_executors() {
51-
let tool_status = executor.tool_status();
52-
match &tool_status.status {
53-
ToolInstallStatus::Installed { version } => {
54-
info!(" {} {} ({})", check_mark(), tool_status.tool_name, version);
55-
}
56-
ToolInstallStatus::IncorrectVersion { version, message } => {
57-
info!(
58-
" {} {} ({}, {})",
59-
warn_mark(),
60-
tool_status.tool_name,
61-
version,
62-
message
63-
);
64-
}
65-
ToolInstallStatus::NotInstalled => {
67+
// Don't probe for tooling that can't be used on this OS anyway.
68+
if executor.support_level(&system_info) == ExecutorSupport::Unsupported {
69+
continue;
70+
}
71+
match executor.tool_status() {
72+
Some(tool_status) => match &tool_status.status {
73+
ToolInstallStatus::Installed { version } => {
74+
info!(
75+
" {} {} executor: {} ({})",
76+
check_mark(),
77+
executor.name(),
78+
tool_status.tool_name,
79+
version
80+
);
81+
}
82+
ToolInstallStatus::IncorrectVersion { version, message } => {
83+
info!(
84+
" {} {} executor: {} ({}, {})",
85+
warn_mark(),
86+
executor.name(),
87+
tool_status.tool_name,
88+
version,
89+
message
90+
);
91+
}
92+
ToolInstallStatus::NotInstalled => {
93+
info!(
94+
" {} {} executor: {} (not installed)",
95+
cross_mark(),
96+
executor.name(),
97+
tool_status.tool_name
98+
);
99+
}
100+
},
101+
None => {
66102
info!(
67-
" {} {} (not installed)",
68-
cross_mark(),
69-
tool_status.tool_name
103+
" {} {} executor: No tool to install",
104+
check_mark(),
105+
executor.name()
70106
);
71107
}
72108
}
73109
}
110+
Ok(())
74111
}

src/cli/status.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,14 @@ pub async fn run(api_client: &CodSpeedAPIClient) -> Result<()> {
2222
info!("");
2323

2424
// Setup/tools status
25-
super::setup::status();
25+
super::setup::status()?;
2626
info!("");
2727

2828
// System info
2929
info!("{}", style("System").bold());
3030
info!(" codspeed {VERSION}");
3131
let system_info = SystemInfo::new()?;
32-
info!(
33-
" {} {} ({})",
34-
system_info.os, system_info.os_version, system_info.arch
35-
);
32+
info!(" {} ({})", system_info.os, system_info.arch);
3633
info!(
3734
" {} ({}C / {}GB)",
3835
system_info.cpu_brand, system_info.cpu_cores, system_info.total_memory_gb

src/executor/helpers/apt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use super::run_with_sudo::run_with_sudo;
22
use crate::prelude::*;
3-
use crate::system::SystemInfo;
3+
use crate::system::{SupportedOs, SystemInfo};
44
use std::path::Path;
55
use std::process::Command;
66

77
const METADATA_FILENAME: &str = "./tmp/codspeed-cache-metadata.txt";
88

99
fn is_system_compatible(system_info: &SystemInfo) -> bool {
10-
system_info.os == "ubuntu" || system_info.os == "debian"
10+
matches!(system_info.os, SupportedOs::Linux(ref distro) if distro.is_supported())
1111
}
1212

1313
/// Installs packages with caching support.

src/executor/memory/executor.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::executor::ExecutorName;
2+
use crate::executor::ExecutorSupport;
23
use crate::executor::ToolStatus;
34
use crate::executor::helpers::command::CommandBuilder;
45
use crate::executor::helpers::env::get_base_injected_env;
@@ -11,7 +12,7 @@ use crate::executor::{ExecutionContext, Executor};
1112
use crate::instruments::mongo_tracer::MongoTracer;
1213
use crate::prelude::*;
1314
use crate::runner_mode::RunnerMode;
14-
use crate::system::SystemInfo;
15+
use crate::system::{SupportedOs, SystemInfo};
1516
use async_trait::async_trait;
1617
use ipc_channel::ipc;
1718
use memtrack::MemtrackIpcClient;
@@ -72,8 +73,15 @@ impl Executor for MemoryExecutor {
7273
ExecutorName::Memory
7374
}
7475

75-
fn tool_status(&self) -> ToolStatus {
76-
get_memtrack_status()
76+
fn tool_status(&self) -> Option<ToolStatus> {
77+
Some(get_memtrack_status())
78+
}
79+
80+
fn support_level(&self, system_info: &SystemInfo) -> ExecutorSupport {
81+
match &system_info.os {
82+
SupportedOs::Linux(_) => ExecutorSupport::FullySupported,
83+
SupportedOs::Macos { .. } => ExecutorSupport::Unsupported,
84+
}
7785
}
7886

7987
async fn setup(

src/executor/mod.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,28 @@ pub enum ToolInstallStatus {
7373
NotInstalled,
7474
}
7575

76+
/// How well a given executor runs on a given [`SupportedOs`].
77+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
78+
pub enum ExecutorSupport {
79+
/// The executor cannot run on this OS at all — `run_executor` hard-bails.
80+
Unsupported,
81+
/// The executor runs on this OS, but the user is responsible for installing the required tooling themselves.
82+
RequiresManualInstallation,
83+
/// The executor runs on this OS and `setup()` knows how to auto-install tooling.
84+
FullySupported,
85+
}
86+
7687
#[async_trait(?Send)]
7788
pub trait Executor {
7889
fn name(&self) -> ExecutorName;
7990

8091
/// Report the installation status of the tool(s) this executor depends on.
81-
fn tool_status(&self) -> ToolStatus;
92+
fn tool_status(&self) -> Option<ToolStatus>;
93+
94+
/// Declare how well this executor runs on the host system. Drives whether `setup()` is invoked
95+
/// (only when [`ExecutorSupport::FullySupported`]) and whether we bail out of running the
96+
/// executor at all (on [`ExecutorSupport::Unsupported`]).
97+
fn support_level(&self, system_info: &SystemInfo) -> ExecutorSupport;
8298

8399
async fn setup(
84100
&self,
@@ -107,11 +123,24 @@ pub async fn run_executor(
107123
execution_context: &ExecutionContext,
108124
setup_cache_dir: Option<&Path>,
109125
) -> Result<()> {
110-
if !execution_context.config.skip_setup {
111-
executor
112-
.setup(&orchestrator.system_info, setup_cache_dir)
113-
.await?;
126+
match executor.support_level(&orchestrator.system_info) {
127+
ExecutorSupport::Unsupported => {
128+
bail!(
129+
"The {} executor is not supported on {}",
130+
executor.name(),
131+
orchestrator.system_info.os
132+
);
133+
}
134+
ExecutorSupport::RequiresManualInstallation | ExecutorSupport::FullySupported => {
135+
if !execution_context.config.skip_setup {
136+
executor
137+
.setup(&orchestrator.system_info, setup_cache_dir)
138+
.await?;
139+
}
140+
}
141+
}
114142

143+
if !execution_context.config.skip_setup {
115144
// TODO: refactor and move directly in the Instruments struct as a `setup` method
116145
if execution_context.config.instruments.is_mongodb_enabled() {
117146
install_mongodb_tracer().await?;

src/executor/orchestrator.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::local_logger::rolling_buffer::{activate_rolling_buffer, deactivate_ro
1111
use crate::prelude::*;
1212
use crate::run_environment::{self, RunEnvironment, RunEnvironmentProvider};
1313
use crate::runner_mode::RunnerMode;
14-
use crate::system::{self, SystemInfo};
14+
use crate::system::SystemInfo;
1515
use crate::upload::poll_results::poll_results;
1616
use crate::upload::{UploadResult, upload};
1717
use serde_json::Value;
@@ -43,7 +43,6 @@ impl Orchestrator {
4343
) -> Result<Self> {
4444
let provider = run_environment::get_provider(&config, api_client).await?;
4545
let system_info = SystemInfo::new()?;
46-
system::check_system(&system_info)?;
4746
let logger = Logger::new(provider.as_ref())?;
4847

4948
if provider.get_run_environment() == RunEnvironment::Local {

src/executor/valgrind/executor.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use std::path::Path;
33

44
use crate::executor::Executor;
55
use crate::executor::ToolStatus;
6-
use crate::executor::{ExecutionContext, ExecutorName};
6+
use crate::executor::{ExecutionContext, ExecutorName, ExecutorSupport};
77
use crate::instruments::mongo_tracer::MongoTracer;
88
use crate::prelude::*;
9-
use crate::system::SystemInfo;
9+
use crate::system::{SupportedOs, SystemInfo};
1010

11-
use super::setup::{get_valgrind_status, install_valgrind};
11+
use super::setup::get_valgrind_status;
12+
use super::setup::install_valgrind;
13+
use super::setup::is_codspeed_valgrind_installation_supported;
1214
use super::{helpers::perf_maps::harvest_perf_maps, helpers::venv_compat, measure};
1315

1416
pub struct ValgrindExecutor;
@@ -19,8 +21,21 @@ impl Executor for ValgrindExecutor {
1921
ExecutorName::Valgrind
2022
}
2123

22-
fn tool_status(&self) -> ToolStatus {
23-
get_valgrind_status()
24+
fn tool_status(&self) -> Option<ToolStatus> {
25+
Some(get_valgrind_status())
26+
}
27+
28+
fn support_level(&self, system_info: &SystemInfo) -> ExecutorSupport {
29+
match &system_info.os {
30+
SupportedOs::Linux(_) => {
31+
if is_codspeed_valgrind_installation_supported(system_info) {
32+
ExecutorSupport::FullySupported
33+
} else {
34+
ExecutorSupport::RequiresManualInstallation
35+
}
36+
}
37+
SupportedOs::Macos { .. } => ExecutorSupport::Unsupported,
38+
}
2439
}
2540

2641
async fn setup(&self, system_info: &SystemInfo, setup_cache_dir: Option<&Path>) -> Result<()> {

0 commit comments

Comments
 (0)