Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance/multi docker test #358

Merged
merged 4 commits into from
Apr 7, 2024
Merged
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
13 changes: 8 additions & 5 deletions clash_lib/src/proxy/shadowsocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,20 +291,22 @@ impl OutboundHandler for Handler {
#[cfg(all(test, not(ci)))]
mod tests {

use super::super::utils::test_utils::{
consts::*, docker_runner::DockerTestRunner, run_default_test_suites_and_cleanup,
};
use crate::proxy::utils::test_utils::docker_runner::DockerTestRunnerBuilder;

use super::super::utils::test_utils::{consts::*, docker_runner::DockerTestRunner, run};

use super::*;

const PASSWORD: &str = "FzcLbKs2dY9mhL";
const CIPHER: &str = "aes-256-gcm";

async fn get_runner() -> anyhow::Result<DockerTestRunner> {
async fn get_ss_runner(port: u16) -> anyhow::Result<DockerTestRunner> {
let host = format!("0.0.0.0:{}", port);
DockerTestRunnerBuilder::new()
.image(IMAGE_SS_RUST)
.entrypoint(&["ssserver"])
.cmd(&["-s", "0.0.0.0:10002", "-m", CIPHER, "-k", PASSWORD, "-U"])
.cmd(&["-s", &host, "-m", CIPHER, "-k", PASSWORD, "-U"])
.build()
.await
}
Expand All @@ -322,7 +324,8 @@ mod tests {
plugin_opts: Default::default(),
udp: false,
};
let port = opts.port;
let handler = Handler::new(opts);
run(handler, get_runner()).await
run_default_test_suites_and_cleanup(handler, get_ss_runner(port).await?).await
}
}
6 changes: 3 additions & 3 deletions clash_lib/src/proxy/trojan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ mod tests {
config_helper::test_config_base_dir,
consts::*,
docker_runner::{DockerTestRunner, DockerTestRunnerBuilder},
run,
run_default_test_suites_and_cleanup,
};

use super::*;
Expand Down Expand Up @@ -278,7 +278,7 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run(handler, get_ws_runner()).await
run_default_test_suites_and_cleanup(handler, get_ws_runner().await?).await
}

async fn get_grpc_runner() -> anyhow::Result<DockerTestRunner> {
Expand Down Expand Up @@ -317,6 +317,6 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run(handler, get_grpc_runner()).await
run_default_test_suites_and_cleanup(handler, get_grpc_runner().await?).await
}
}
1 change: 1 addition & 0 deletions clash_lib/src/proxy/utils/test_utils/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub const EXAMPLE_REQ: &[u8] = b"GET / HTTP/1.1\r\nHost: example.com\r\nAccept:
pub const EXAMLE_RESP_200: &[u8] = b"HTTP/1.1 200";

pub const IMAGE_SS_RUST: &str = "ghcr.io/shadowsocks/ssserver-rust:latest";
pub const IMAGE_SHADOW_TLS: &str = "ghcr.io/ihciah/shadow-tls:latest";
pub const IMAGE_TROJAN_GO: &str = "p4gefau1t/trojan-go:latest";
pub const IMAGE_VMESS: &str = "v2fly/v2fly-core:v4.45.2";
pub const IMAGE_XRAY: &str = "teddysun/xray:latest";
109 changes: 91 additions & 18 deletions clash_lib/src/proxy/utils/test_utils/docker_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct DockerTestRunner {
}

impl DockerTestRunner {
pub async fn new<'a>(
pub async fn try_new<'a>(
image_conf: Option<CreateImageOptions<'a, String>>,
container_conf: Config<String>,
) -> Result<Self> {
Expand All @@ -39,10 +39,62 @@ impl DockerTestRunner {
})
}

// will make sure the container is cleaned up after the future is finished
pub async fn run_and_cleanup(
// you can run the cleanup manually
pub async fn cleanup(self) -> anyhow::Result<()> {
let s = self
.instance
.remove_container(
&self.id,
Some(RemoveContainerOptions {
force: true,
..Default::default()
}),
)
.await?;
Ok(s)
}
}

#[derive(Default)]
pub struct MultiDockerTestRunner {
runners: Vec<DockerTestRunner>,
}

// TODO: use this test runner to support test of ss's plugins
#[allow(unused)]
impl MultiDockerTestRunner {
pub fn new(runners: Vec<DockerTestRunner>) -> Self {
Self { runners }
}

pub async fn add(&mut self, creator: impl Future<Output = anyhow::Result<DockerTestRunner>>) {
match creator.await {
Ok(runner) => {
self.runners.push(runner);
}
Err(e) => {
tracing::warn!(
"cannot start container, please check the docker environment, error: {:?}",
e
);
}
}
}
}

#[async_trait::async_trait]
pub trait RunAndCleanup {
async fn run_and_cleanup(
self,
f: impl Future<Output = anyhow::Result<()>> + Send + 'static,
) -> anyhow::Result<()>;
}

#[async_trait::async_trait]
impl RunAndCleanup for DockerTestRunner {
async fn run_and_cleanup(
self,
f: impl Future<Output = anyhow::Result<()>>,
f: impl Future<Output = anyhow::Result<()>> + Send + 'static,
) -> anyhow::Result<()> {
let fut = Box::pin(f);
// let res = fut.await;
Expand All @@ -61,20 +113,33 @@ impl DockerTestRunner {

res
}
}

// you can run the cleanup manually
pub async fn cleanup(self) -> anyhow::Result<()> {
let s = self
.instance
.remove_container(
&self.id,
Some(RemoveContainerOptions {
force: true,
..Default::default()
}),
)
.await?;
Ok(s)
#[async_trait::async_trait]
impl RunAndCleanup for MultiDockerTestRunner {
async fn run_and_cleanup(
self,
f: impl Future<Output = anyhow::Result<()>> + Send + 'static,
) -> anyhow::Result<()> {
let fut = Box::pin(f);
// let res = fut.await;
// make sure the container is cleaned up
let res = tokio::select! {
res = fut => {
res
},
_ = tokio::time::sleep(std::time::Duration::from_secs(TIMEOUT_DURATION))=> {
tracing::warn!("timeout");
Err(anyhow::anyhow!("timeout"))
}
};

// cleanup all the docker containers
for runner in self.runners {
runner.cleanup().await?;
}

res
}
}

Expand All @@ -89,6 +154,7 @@ pub struct DockerTestRunnerBuilder {
host_config: HostConfig,
exposed_ports: Vec<String>,
cmd: Option<Vec<String>>,
env: Option<Vec<String>>,
entrypoint: Option<Vec<String>>,
_server_port: u16,
}
Expand All @@ -103,6 +169,7 @@ impl Default for DockerTestRunnerBuilder {
.map(|x| x.to_string())
.collect::<Vec<_>>(),
cmd: None,
env: None,
entrypoint: None,
_server_port: PORT,
}
Expand Down Expand Up @@ -135,6 +202,11 @@ impl DockerTestRunnerBuilder {
self
}

pub fn env(mut self, env: &[&str]) -> Self {
self.env = Some(env.iter().map(|x| x.to_string()).collect());
self
}

pub fn entrypoint(mut self, entrypoint: &[&str]) -> Self {
self.entrypoint = Some(entrypoint.iter().map(|x| x.to_string()).collect());
self
Expand Down Expand Up @@ -165,7 +237,7 @@ impl DockerTestRunnerBuilder {
.map(|x| (x, Default::default()))
.collect::<HashMap<_, _>>();

DockerTestRunner::new(
DockerTestRunner::try_new(
Some(CreateImageOptions {
from_image: self.image.clone(),
..Default::default()
Expand All @@ -175,6 +247,7 @@ impl DockerTestRunnerBuilder {
tty: Some(true),
entrypoint: self.entrypoint,
cmd: self.cmd,
env: self.env,
exposed_ports: Some(exposed),
host_config: Some(self.host_config),
..Default::default()
Expand Down
18 changes: 5 additions & 13 deletions clash_lib/src/proxy/utils/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use crate::{
proxy::OutboundHandler,
session::{Session, SocksAddr},
};
use futures::{future::select_all, Future};
use futures::future::select_all;
use tokio::{
io::{split, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::TcpListener,
};
use tracing::info;

use self::docker_runner::DockerTestRunner;
use self::docker_runner::RunAndCleanup;

pub mod config_helper;
pub mod consts;
Expand Down Expand Up @@ -176,19 +176,11 @@ pub async fn latency_test(
Ok(end_time.duration_since(start_time))
}

pub async fn run(
pub async fn run_default_test_suites_and_cleanup(
handler: Arc<dyn OutboundHandler>,
runner_creater: impl Future<Output = anyhow::Result<DockerTestRunner>>,
docker_test_runner: impl RunAndCleanup,
) -> anyhow::Result<()> {
let watch = match runner_creater.await {
Ok(runner) => runner,
Err(e) => {
tracing::warn!("cannot start container, please check the docker environment");
return Err(e);
}
};

watch
docker_test_runner
.run_and_cleanup(async move {
let rv = ping_pong_test(handler.clone(), 10001).await;
if rv.is_err() {
Expand Down
8 changes: 4 additions & 4 deletions clash_lib/src/proxy/vmess/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ mod tests {
config_helper::test_config_base_dir,
consts::*,
docker_runner::{DockerTestRunner, DockerTestRunnerBuilder},
run,
run_default_test_suites_and_cleanup,
};

use super::*;
Expand Down Expand Up @@ -303,7 +303,7 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run(handler, get_ws_runner()).await
run_default_test_suites_and_cleanup(handler, get_ws_runner().await?).await
}

async fn get_grpc_runner() -> anyhow::Result<DockerTestRunner> {
Expand Down Expand Up @@ -346,7 +346,7 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run(handler, get_grpc_runner()).await
run_default_test_suites_and_cleanup(handler, get_grpc_runner().await?).await
}

async fn get_h2_runner() -> anyhow::Result<DockerTestRunner> {
Expand Down Expand Up @@ -389,6 +389,6 @@ mod tests {
})),
};
let handler = Handler::new(opts);
run(handler, get_h2_runner()).await
run_default_test_suites_and_cleanup(handler, get_h2_runner().await?).await
}
}