Skip to content

Commit 6a69fc5

Browse files
authored
DEVPROD-18259 Generate tasks for suites that run via bazel (#104)
1 parent d79845b commit 6a69fc5

18 files changed

+415
-56
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# Changelog
2+
## 3.3.0 - 2025-06-10
3+
* Generate tasks for suites that run via bazel
4+
25
## 3.0.1 - 2025-05-22
36
* Add multiversion binary selection dependency to burn-in tasks when needed.
47

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "mongo-task-generator"
33
description = "Dynamically split evergreen tasks into subtasks for testing the 10gen/mongo project."
44
license = "Apache-2.0"
5-
version = "3.2.0"
5+
version = "3.3.0"
66
repository = "https://github.com/mongodb/mongo-task-generator"
77
authors = ["DevProd Correctness Team <devprod-correctness-team@mongodb.com>"]
88
edition = "2018"

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,27 @@ Options:
117117
Disable evergreen task-history queries and use task splitting fallback
118118
--resmoke-command <RESMOKE_COMMAND>
119119
Command to invoke resmoke [default: "python buildscripts/resmoke.py"]
120+
--include-fully-disabled-feature-tests
121+
If the generator should include tests that are tagged with fully disabled features
120122
--generate-sub-tasks-config <GENERATE_SUB_TASKS_CONFIG>
121123
File containing configuration for generating sub-tasks
122124
--burn-in
123125
Generate burn_in related tasks
124126
--burn-in-tests-command <BURN_IN_TESTS_COMMAND>
125127
Command to invoke burn_in_tests [default: "python buildscripts/burn_in_tests.py run"]
126-
--s3-test-stats-bucket <S3_TEST_STATS_BUCKETT>
128+
--s3-test-stats-bucket <S3_TEST_STATS_BUCKET>
127129
S3 bucket to get test stats from [default: mongo-test-stats]
130+
--test-runtime-per-required-subtask <TEST_RUNTIME_PER_REQUIRED_SUBTASK>
131+
[default: 3600]
132+
--large-required-task-runtime-threshold <LARGE_REQUIRED_TASK_RUNTIME_THRESHOLD>
133+
[default: 7200]
134+
--default-subtasks-per-task <DEFAULT_SUBTASKS_PER_TASK>
135+
[default: 5]
136+
--max-subtasks-per-task <MAX_SUBTASKS_PER_TASK>
137+
[default: 10]
138+
--bazel-suite-configs <BAZEL_SUITE_CONFIGS>
139+
YAML file mapping mapping bazel target names of suite configs to their file location location
140+
128141
-h, --help
129142
Print help
130143
```

src/evergreen/evg_config_utils.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub struct MultiversionGenerateTaskConfig {
3030
pub suite_name: String,
3131
/// Old version to run testing against.
3232
pub old_version: String,
33+
/// The bazel test target, if it is a bazel-based resmoke task.
34+
pub bazel_target: Option<String>,
3335
}
3436

3537
pub trait EvgConfigUtils: Sync + Send {
@@ -361,8 +363,12 @@ impl EvgConfigUtils for EvgConfigUtilsImpl {
361363
let generated_task_name = remove_gen_suffix(&task.name);
362364

363365
if let Some(vars) = optional_vars {
364-
if let Some(ParamValue::String(suite_var)) = vars.get("suite") {
365-
suite_var
366+
if let Some(ParamValue::String(suite)) = vars.get("suite") {
367+
if is_bazel_suite(suite) {
368+
get_bazel_suite_name(suite)
369+
} else {
370+
suite
371+
}
366372
} else {
367373
generated_task_name
368374
}
@@ -406,11 +412,18 @@ impl EvgConfigUtils for EvgConfigUtilsImpl {
406412
get_func_vars_by_name(task, INITIALIZE_MULTIVERSION_TASKS)
407413
{
408414
let mut multiversion_generate_tasks = vec![];
409-
for (suite_name, old_version) in multiversion_task_map {
415+
for (suite, old_version) in multiversion_task_map {
416+
let (suite_name, bazel_target) = if is_bazel_suite(suite) {
417+
(get_bazel_suite_name(suite).to_string(), Some(suite.clone()))
418+
} else {
419+
(suite.clone(), None)
420+
};
421+
410422
if let ParamValue::String(value) = old_version {
411423
multiversion_generate_tasks.push(MultiversionGenerateTaskConfig {
412-
suite_name: suite_name.clone(),
424+
suite_name,
413425
old_version: value.clone(),
426+
bazel_target,
414427
});
415428
}
416429
}
@@ -844,6 +857,33 @@ fn get_resmoke_vars(task: &EvgTask) -> Option<&HashMap<String, ParamValue>> {
844857
return get_func_vars_by_name(task, RUN_RESMOKE_TESTS);
845858
}
846859

860+
/// Checks if a Resmoke suite is a bazel target.
861+
///
862+
/// # Arguments
863+
///
864+
/// * `suite` - A suite name from Evergreen YAML.
865+
///
866+
/// # Returns
867+
///
868+
/// True if the suite looks like a bazel target (e.g. starts with `//`).
869+
pub fn is_bazel_suite(suite: &str) -> bool {
870+
suite.starts_with("//")
871+
}
872+
873+
/// Get a suite name from a bazel target.
874+
///
875+
/// # Arguments
876+
///
877+
/// * `target` - A bazel target.
878+
///
879+
/// # Returns
880+
///
881+
/// A useful suite name, just the name of the target without the bazel package prefix.
882+
pub fn get_bazel_suite_name(target: &str) -> &str {
883+
let (_, name) = target.rsplit_once(':').unwrap();
884+
name
885+
}
886+
847887
#[cfg(test)]
848888
mod tests {
849889
use std::collections::BTreeMap;
@@ -1413,10 +1453,12 @@ mod tests {
14131453
MultiversionGenerateTaskConfig {
14141454
suite_name: "mv_suite1_last_continuous".to_string(),
14151455
old_version: "last-continuous".to_string(),
1456+
bazel_target: None,
14161457
},
14171458
MultiversionGenerateTaskConfig {
14181459
suite_name: "mv_suite1_last_lts".to_string(),
14191460
old_version: "last-lts".to_string(),
1461+
bazel_target: None,
14201462
},
14211463
];
14221464
assert!(multiversion_generate_tasks

src/evergreen_names.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub const SETUP_JSTESTFUZZ: &str = "setup jstestfuzz";
1717
pub const RUN_FUZZER: &str = "run jstestfuzz";
1818
/// Function to run generated tasks.
1919
pub const RUN_GENERATED_TESTS: &str = "run generated tests";
20+
/// Function to run generated tasks via 'bazel test'.
21+
pub const RUN_GENERATED_TESTS_VIA_BAZEL: &str = "run generated tests via bazel";
2022

2123
// Function for multi-version tests.
2224
/// Function to do setup for multi-version testing.
@@ -67,6 +69,8 @@ pub const MULTIVERSION_EXCLUDE_TAG: &str = "multiversion_exclude_tags_version";
6769
pub const REQUIRE_MULTIVERSION_SETUP: &str = "require_multiversion_setup";
6870
/// Arguments to pass to resmoke command.
6971
pub const RESMOKE_ARGS: &str = "resmoke_args";
72+
/// Arguments to pass to bazel command.
73+
pub const BAZEL_ARGS: &str = "bazel_args";
7074
/// Name of suite being executed.
7175
pub const SUITE_NAME: &str = "suite";
7276
/// Location where generation task configuration is stored in S3.

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ use tokio::{runtime::Handle, task::JoinHandle, time};
5151
use tracing::{event, Level};
5252
use utils::fs_service::FsServiceImpl;
5353

54+
use crate::resmoke::resmoke_proxy::BazelConfigs;
55+
5456
mod evergreen;
5557
mod evergreen_names;
5658
mod generate_sub_tasks_config;
@@ -150,6 +152,7 @@ pub struct ExecutionConfiguration<'a> {
150152
/// S3 bucket to get test stats from.
151153
pub s3_test_stats_bucket: &'a str,
152154
pub subtask_limits: SubtaskLimits,
155+
pub bazel_suite_configs: Option<PathBuf>,
153156
}
154157

155158
#[derive(Debug, Clone)]
@@ -193,10 +196,15 @@ impl Dependencies {
193196
s3_client: aws_sdk_s3::Client,
194197
) -> Result<Self> {
195198
let fs_service = Arc::new(FsServiceImpl::new());
199+
let bazel_suite_configs = match execution_config.bazel_suite_configs {
200+
Some(path) => BazelConfigs::from_yaml_file(&path).unwrap_or_default(),
201+
None => BazelConfigs::default(),
202+
};
196203
let discovery_service = Arc::new(ResmokeProxy::new(
197204
execution_config.resmoke_command,
198205
execution_config.skip_covered_tests,
199206
execution_config.include_fully_disabled_feature_tests,
207+
bazel_suite_configs,
200208
));
201209
let multiversion_service = Arc::new(MultiversionServiceImpl::new(
202210
discovery_service.get_multiversion_config()?,
@@ -240,6 +248,11 @@ impl Dependencies {
240248
fs_service,
241249
gen_resmoke_config,
242250
execution_config.subtask_limits,
251+
execution_config
252+
.target_directory
253+
.to_str()
254+
.unwrap_or("")
255+
.to_string(),
243256
));
244257
let gen_task_service = Arc::new(GenerateTasksServiceImpl::new(
245258
evg_config_service,

src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ struct Args {
152152
// Maximum number of subtasks that can be generated for tasks
153153
#[clap(long, default_value = DEFAULT_MAX_SUBTASKS_PER_TASK)]
154154
max_subtasks_per_task: usize,
155+
156+
/// YAML file mapping mapping bazel target names of suite configs to their file location location
157+
#[clap(long, value_parser)]
158+
bazel_suite_configs: Option<PathBuf>,
155159
}
156160

157161
/// Configure logging for the command execution.
@@ -194,6 +198,7 @@ async fn main() {
194198
default_subtasks_per_task: args.default_subtasks_per_task,
195199
large_required_task_runtime_threshold: args.large_required_task_runtime_threshold,
196200
},
201+
bazel_suite_configs: args.bazel_suite_configs.as_ref().map(|p| expand_path(p)),
197202
};
198203
let s3_client = build_s3_client().await;
199204
let deps = Dependencies::new(execution_config, s3_client).unwrap();

src/resmoke/resmoke_proxy.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use anyhow::Result;
44
use serde::Deserialize;
55
use tracing::{error, event, Level};
66

7+
use crate::evergreen::evg_config_utils::is_bazel_suite;
8+
79
use super::{external_cmd::run_command, resmoke_suite::ResmokeSuiteConfig};
10+
use std::collections::HashMap;
811

912
/// Interface for discovering details about test suites.
1013
pub trait TestDiscovery: Send + Sync {
@@ -45,6 +48,7 @@ pub struct ResmokeProxy {
4548
skip_covered_tests: bool,
4649
/// True if test discovery should include tests that are tagged with fully disabled features.
4750
include_fully_disabled_feature_tests: bool,
51+
bazel_suite_configs: BazelConfigs,
4852
}
4953

5054
impl ResmokeProxy {
@@ -55,10 +59,12 @@ impl ResmokeProxy {
5559
/// * `resmoke_cmd` - Command to invoke resmoke.
5660
/// * `skip_covered_tests` - Whether the generator should skip tests run in more complex suites.
5761
/// * `include_fully_disabled_feature_tests` - If the generator should include tests that are tagged with fully disabled features.
62+
/// * `bazel_suite_configs` - Optional bazel suite configurations.
5863
pub fn new(
5964
resmoke_cmd: &str,
6065
skip_covered_tests: bool,
6166
include_fully_disabled_feature_tests: bool,
67+
bazel_suite_configs: BazelConfigs,
6268
) -> Self {
6369
let cmd_parts: Vec<_> = resmoke_cmd.split(' ').collect();
6470
let cmd = cmd_parts[0];
@@ -68,10 +74,46 @@ impl ResmokeProxy {
6874
resmoke_script: script,
6975
skip_covered_tests,
7076
include_fully_disabled_feature_tests,
77+
bazel_suite_configs,
7178
}
7279
}
7380
}
7481

82+
#[derive(Debug, Clone, Default)]
83+
pub struct BazelConfigs {
84+
/// Map of bazel resmoke config targets to their generated suite config YAMLs.
85+
configs: HashMap<String, String>,
86+
}
87+
88+
impl BazelConfigs {
89+
pub fn from_yaml_file(path: &Path) -> Result<Self> {
90+
let contents = std::fs::read_to_string(path)?;
91+
let configs: Result<HashMap<String, String>, serde_yaml::Error> =
92+
serde_yaml::from_str(&contents);
93+
if configs.is_err() {
94+
error!(
95+
file = path.display().to_string(),
96+
contents = &contents,
97+
"Failed to parse bazel configs from yaml file",
98+
);
99+
}
100+
Ok(Self { configs: configs? })
101+
}
102+
103+
/// Get the generated suite config for a bazel resmoke target.
104+
///
105+
/// # Arguments
106+
///
107+
/// * `target` - Bazel resmoke test target, like "//buildscripts/resmoke:core".
108+
///
109+
/// # Returns
110+
///
111+
/// The path the the generated suite config YAML, like "bazel-out/buildscripts/resmoke/core_config.yml".
112+
pub fn get(&self, target: &str) -> &str {
113+
self.configs.get(&format!("{}_config", target)).unwrap()
114+
}
115+
}
116+
75117
/// Details about tests comprising a test suite.
76118
#[derive(Debug, Deserialize)]
77119
#[allow(dead_code)]
@@ -94,9 +136,15 @@ impl TestDiscovery for ResmokeProxy {
94136
///
95137
/// A list of tests belonging to given suite.
96138
fn discover_tests(&self, suite_name: &str) -> Result<Vec<String>> {
139+
let suite_config = if is_bazel_suite(suite_name) {
140+
self.bazel_suite_configs.get(suite_name)
141+
} else {
142+
suite_name
143+
};
144+
97145
let mut cmd = vec![&*self.resmoke_cmd];
98146
cmd.append(&mut self.resmoke_script.iter().map(|s| s.as_str()).collect());
99-
cmd.append(&mut vec!["test-discovery", "--suite", suite_name]);
147+
cmd.append(&mut vec!["test-discovery", "--suite", suite_config]);
100148

101149
// When running in a patch build, we use the --skipTestsCoveredByMoreComplexSuites
102150
// flag to tell Resmoke to exclude any tests in the given suite that will
@@ -114,7 +162,7 @@ impl TestDiscovery for ResmokeProxy {
114162

115163
event!(
116164
Level::INFO,
117-
suite_name,
165+
suite_config,
118166
duration_ms = start.elapsed().as_millis() as u64,
119167
"Resmoke test discovery finished"
120168
);
@@ -146,9 +194,15 @@ impl TestDiscovery for ResmokeProxy {
146194
///
147195
/// Resmoke configuration for the given suite.
148196
fn get_suite_config(&self, suite_name: &str) -> Result<ResmokeSuiteConfig> {
197+
let suite_config = if is_bazel_suite(suite_name) {
198+
self.bazel_suite_configs.get(suite_name)
199+
} else {
200+
suite_name
201+
};
202+
149203
let mut cmd = vec![&*self.resmoke_cmd];
150204
cmd.append(&mut self.resmoke_script.iter().map(|s| s.as_str()).collect());
151-
cmd.append(&mut vec!["suiteconfig", "--suite", suite_name]);
205+
cmd.append(&mut vec!["suiteconfig", "--suite", suite_config]);
152206
let cmd_output = run_command(&cmd).unwrap();
153207

154208
Ok(ResmokeSuiteConfig::from_str(&cmd_output)?)

0 commit comments

Comments
 (0)