#![allow(dead_code)]
use std::time::Duration;
use crate::stages::{StageCtx, StageOutcome};
use crate::support::*;
const DEFAULT_AWS_REGION: &str = "eu-west-1";
pub struct E2eHarness {
pub client: clickhouse_cloud_api::Client,
pub ctx: TestContext,
pub aws_config: aws_config::SdkConfig,
pub s3: aws_sdk_s3::Client,
pub iam: aws_sdk_iam::Client,
pub ec2: aws_sdk_ec2::Client,
pub aws_region: String,
pub ch: ProvisionedClickHouse,
pub cleanup: CleanupRegistry,
pub aws_cleanup: AwsCleanupRegistry,
}
impl E2eHarness {
pub async fn provision(test_name: &str) -> TestResult<Self> {
let ctx = TestContext::from_env()?;
let client = create_client()?;
let aws_region: String = std::env::var("CLICKHOUSE_CLOUD_TEST_AWS_REGION")
.ok()
.filter(|s| !s.is_empty())
.unwrap_or_else(|| DEFAULT_AWS_REGION.to_string());
let aws_config = aws_config::defaults(aws_config::BehaviorVersion::latest())
.region(aws_sdk_s3::config::Region::new(aws_region.clone()))
.load()
.await;
let s3 = aws_sdk_s3::Client::new(&aws_config);
let iam = aws_sdk_iam::Client::new(&aws_config);
let ec2 = aws_sdk_ec2::Client::new(&aws_config);
let mut cleanup = CleanupRegistry::default();
let aws_cleanup = AwsCleanupRegistry::default();
log_run_header(test_name, &ctx);
let ch = match std::env::var("CLICKHOUSE_CLOUD_TEST_SERVICE_ID")
.ok()
.filter(|s| !s.is_empty())
{
Some(service_id) => {
let password = std::env::var("CLICKHOUSE_CLOUD_TEST_SERVICE_PASSWORD")
.map_err(|_| {
"CLICKHOUSE_CLOUD_TEST_SERVICE_ID set but \
CLICKHOUSE_CLOUD_TEST_SERVICE_PASSWORD missing"
})?;
log_phase("Attach to existing ClickHouse service");
attach_clickhouse(&client, &ctx.org_id, &service_id, &password).await?
}
None => {
log_phase("Provision ClickHouse");
provision_clickhouse(
&client,
&ctx,
&mut cleanup,
&ctx.clickpipe_e2e_service_name(),
ctx.clickpipe_e2e_run_tags(),
)
.await?
}
};
Ok(Self {
client,
ctx,
aws_config,
s3,
iam,
ec2,
aws_region,
ch,
cleanup,
aws_cleanup,
})
}
pub fn make_stage_ctx(&self) -> StageCtx<'_> {
StageCtx {
client: &self.client,
ctx: &self.ctx,
ch: &self.ch,
aws_config: &self.aws_config,
s3: &self.s3,
iam: &self.iam,
ec2: &self.ec2,
aws_region: &self.aws_region,
cleanup: CleanupRegistry::default(),
aws_cleanup: AwsCleanupRegistry::default(),
}
}
pub fn collect(
&mut self,
name: &str,
outcome: StageOutcome,
failures: &mut Vec<(String, String)>,
) {
self.cleanup.merge_from(outcome.cleanup);
self.aws_cleanup.merge_from(outcome.aws_cleanup);
match outcome.result {
Ok(()) => eprintln!(" PASS [{name}]"),
Err(err) => {
eprintln!(" FAIL [{name}]: {}", render_error_chain(err.as_ref()));
failures.push((name.to_string(), err.to_string()));
}
}
}
pub async fn teardown(self, run_result: TestResult<()>) -> TestResult<()> {
log_phase("Teardown");
let Self {
client,
ctx,
aws_config,
iam,
ec2,
ch,
mut cleanup,
mut aws_cleanup,
..
} = self;
let cleanup_result = cleanup
.cleanup(
&client,
&ctx.org_id,
ctx.delete_timeout,
ctx.poll_interval,
Some(&ch.query),
)
.await;
let aws_cleanup_result = aws_cleanup.cleanup(&aws_config, &iam, &ec2).await;
match (run_result, cleanup_result, aws_cleanup_result) {
(Ok(()), Ok(()), Ok(())) => Ok(()),
(Err(error), _, _) => Err(error),
(Ok(()), Err(cleanup_error), Ok(())) => Err(cleanup_error.into()),
(Ok(()), Ok(()), Err(aws_error)) => Err(aws_error.into()),
(Ok(()), Err(cleanup_error), Err(aws_error)) => {
Err(format!("{cleanup_error}\naws cleanup failed:\n{aws_error}").into())
}
}
}
}
pub fn check_failures(failures: Vec<(String, String)>) -> TestResult<()> {
if failures.is_empty() {
return Ok(());
}
let summary = failures
.iter()
.map(|(name, err)| format!(" - {name}: {}", first_line(err)))
.collect::<Vec<_>>()
.join("\n");
Err(format!("{} stage failure(s):\n{summary}", failures.len()).into())
}
pub fn render_error_chain(err: &dyn std::error::Error) -> String {
let mut parts = vec![err.to_string()];
let mut cur = err.source();
while let Some(s) = cur {
parts.push(s.to_string());
cur = s.source();
}
parts.join(" -> ")
}
pub fn first_line(text: &str) -> &str {
text.lines().next().unwrap_or(text)
}
#[allow(dead_code)]
const _DURATION_USED: Duration = Duration::from_secs(0);