Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/xtask/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod generate_pipeline;
pub mod hello;
pub mod xdp_test;
56 changes: 55 additions & 1 deletion ci/xtask/src/commands/generate_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ fn generate_private_pipeline() -> Result<buildkite::Pipeline> {
pipeline.add_step(default_local_cluster_step(10));
pipeline.add_step(default_docs_check_step());
pipeline.add_step(default_localnet_step());
pipeline.add_step(default_xdp_test_step());

pipeline.add_step(buildkite::Step::Wait(buildkite::WaitStep {}));

Expand Down Expand Up @@ -232,6 +233,7 @@ struct PullRequestPipelineFlags {
stable_sbf: bool,
shuttle: bool,
coverage: bool,
xdp_tests: bool,
}

impl PullRequestPipelineFlags {
Expand All @@ -250,6 +252,13 @@ impl PullRequestPipelineFlags {
file.ends_with("Cargo.toml") || file.ends_with("Cargo.lock") || file.ends_with(".rs")
});

let xdp_changed = changed_files.iter().any(|file| {
file == "Cargo.lock"
|| file == "Cargo.toml"
|| file.starts_with("xdp/")
|| file.starts_with("xdp-ebpf/")
});

Self {
shellcheck: changed_files.iter().any(|file| file.ends_with(".sh")),
checks: trigger_all
Expand Down Expand Up @@ -338,6 +347,7 @@ impl PullRequestPipelineFlags {
|| file.ends_with("ci/test-coverage.sh")
|| file.starts_with("ci/coverage/")
}),
xdp_tests: trigger_all || xdp_changed,
}
}
}
Expand Down Expand Up @@ -385,6 +395,9 @@ async fn generate_pull_request_pipeline(
if flags.localnet {
pipeline.add_step(default_localnet_step());
}
if flags.xdp_tests {
pipeline.add_step(default_xdp_test_step());
}

pipeline.add_step(buildkite::Step::Wait(buildkite::WaitStep {}));

Expand Down Expand Up @@ -420,6 +433,7 @@ fn generate_full_pipeline() -> Result<buildkite::Pipeline> {
pipeline.add_step(default_local_cluster_step(10));
pipeline.add_step(default_docs_check_step());
pipeline.add_step(default_localnet_step());
pipeline.add_step(default_xdp_test_step());

pipeline.add_step(buildkite::Step::Wait(buildkite::WaitStep {}));

Expand Down Expand Up @@ -643,6 +657,35 @@ fn default_localnet_step() -> buildkite::Step {
})
}

fn default_xdp_test_step() -> buildkite::Step {
buildkite::Step::Command(buildkite::CommandStep {
name: String::from("xdp-test"),
// The container runs as root for network namespace privileges; keep
// root-owned Cargo artifacts out of the mounted checkout.
command: String::from(concat!(
"ci/docker-run-default-image.sh env ",
"CARGO_TARGET_DIR=/tmp/agave-xdp-target ",
"cargo xtask xdp-test --release-with-debug",
)),
agents: Some(HashMap::from([(
String::from("queue"),
String::from("default"),
)])),
timeout_in_minutes: Some(25),
env: Some(HashMap::from([
(
String::from("EXTRA_DOCKER_RUN_ARGS"),
String::from("--cap-add NET_ADMIN --cap-add NET_RAW --cap-add SYS_ADMIN"),
),
(
String::from("SOLANA_DOCKER_RUN_NOSETUID"),
String::from("1"),
),
])),
..Default::default()
})
}

fn default_stable_sbf_step() -> buildkite::Step {
buildkite::Step::Command(buildkite::CommandStep {
name: String::from("stable-sbf"),
Expand Down Expand Up @@ -858,6 +901,7 @@ mod tests {
assert!(!f.stable_sbf);
assert!(!f.shuttle);
assert!(!f.coverage);
assert!(!f.xdp_tests);
}

#[test]
Expand All @@ -874,10 +918,11 @@ mod tests {
assert!(f.stable_sbf);
assert!(f.shuttle);
assert!(f.coverage);
assert!(f.xdp_tests);
}

#[test]
fn test_rust_change_triggers_all() {
fn test_rust_change_triggers_standard_rust_jobs() {
let f = flags(&["core/src/lib.rs"]);
assert!(f.checks);
assert!(f.feature_check);
Expand All @@ -890,6 +935,13 @@ mod tests {
assert!(f.stable_sbf);
assert!(f.shuttle);
assert!(f.coverage);
assert!(!f.xdp_tests);
}

#[test]
fn test_xdp_change_triggers_xdp_tests() {
let f = flags(&["xdp/tests/transmitter_smoke.rs"]);
assert!(f.xdp_tests);
}

#[test]
Expand All @@ -907,6 +959,7 @@ mod tests {
assert!(!f.stable_sbf);
assert!(!f.shuttle);
assert!(!f.coverage);
assert!(!f.xdp_tests);
}

#[test]
Expand All @@ -924,5 +977,6 @@ mod tests {
assert!(!f.stable_sbf);
assert!(!f.shuttle);
assert!(!f.coverage);
assert!(!f.xdp_tests);
}
}
101 changes: 101 additions & 0 deletions ci/xtask/src/commands/xdp_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use {
anyhow::{bail, ensure, Context, Result},
clap::Args,
log::info,
std::{
env,
path::{Path, PathBuf},
process::Command,
},
};

const DEFAULT_TESTS: &[&str] = &[
"netlink_snapshot",
"route_monitor",
"router_snapshot",
"transmitter_smoke",
];

#[derive(Args)]
pub struct CommandArgs {
#[arg(
long,
help = "Build and run the tests with the release-with-debug profile"
)]
pub release_with_debug: bool,

#[arg(
long,
help = "Optional command prefix used to run cargo with privileges, for example: sudo -n -E"
)]
runner: Option<String>,

#[arg(long = "test", value_name = "TEST")]
tests: Vec<String>,

#[arg(last = true)]
run_args: Vec<std::ffi::OsString>,
}

pub fn run(args: CommandArgs) -> Result<()> {
let repo_root = repo_root();
let tests = test_selection(&args.tests);
let mut cmd = command_with_runner(args.runner.as_deref(), &cargo_bin())?;
cmd.current_dir(&repo_root);
cmd.arg("test")
.arg("-p")
.arg("agave-xdp")
.arg("--features")
.arg("agave-unstable-api");
if args.release_with_debug {
cmd.arg("--profile").arg("release-with-debug");
}
for test in &tests {
cmd.arg("--test").arg(test);
}
cmd.arg("--")
.arg("--include-ignored")
.arg("--test-threads=1");
for arg in &args.run_args {
cmd.arg(arg);
}

info!("running local xdp tests from {}", repo_root.display());
let status = cmd.status().context("failed to run cargo test")?;
ensure!(status.success(), "xdp tests failed with {status}");
Ok(())
}

fn test_selection(selected: &[String]) -> Vec<String> {
if selected.is_empty() {
return DEFAULT_TESTS
.iter()
.map(|test| (*test).to_string())
.collect();
}
selected.to_vec()
}

fn repo_root() -> PathBuf {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
root.canonicalize().unwrap_or(root)
}

fn command_with_runner(runner: Option<&str>, program: &Path) -> Result<Command> {
let Some(runner) = runner else {
return Ok(Command::new(program));
};
let mut parts = runner.split_whitespace();
let Some(runner_program) = parts.next() else {
bail!("runner cannot be empty");
};
let mut cmd = Command::new(runner_program);
cmd.args(parts).arg(program);
Ok(cmd)
}

fn cargo_bin() -> PathBuf {
env::var_os("CARGO")
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("cargo"))
}
5 changes: 5 additions & 0 deletions ci/xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ enum Commands {
Publish(xtask_shared::commands::publish::CommandArgs),
#[command(about = "Generate Buildkite pipeline")]
GeneratePipeline(commands::generate_pipeline::CommandArgs),
#[command(about = "Run XDP integration tests")]
XdpTest(commands::xdp_test::CommandArgs),
}

#[derive(Args, Debug)]
Expand Down Expand Up @@ -78,6 +80,9 @@ async fn try_main(xtask: Xtask) -> Result<()> {
Commands::GeneratePipeline(args) => {
commands::generate_pipeline::run(args).await?;
}
Commands::XdpTest(args) => {
commands::xdp_test::run(args)?;
}
}

Ok(())
Expand Down
20 changes: 20 additions & 0 deletions xdp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,25 @@ arrayvec = { workspace = true }
aya = { workspace = true }
caps = { workspace = true }

[[test]]
name = "netlink_snapshot"
path = "tests/netlink_snapshot.rs"
required-features = ["agave-unstable-api"]

[[test]]
name = "route_monitor"
path = "tests/route_monitor.rs"
required-features = ["agave-unstable-api"]

[[test]]
name = "router_snapshot"
path = "tests/router_snapshot.rs"
required-features = ["agave-unstable-api"]

[[test]]
name = "transmitter_smoke"
path = "tests/transmitter_smoke.rs"
required-features = ["agave-unstable-api"]

[lints]
workspace = true
Loading