pub(crate) mod cmd_wait;
pub(crate) mod exit_strategy;
pub(crate) mod health_strategy;
#[cfg(feature = "http_wait_plain")]
pub(crate) mod http_strategy;
pub(crate) mod log_strategy;
pub use cmd_wait::CmdWaitFor;
pub use exit_strategy::ExitWaitStrategy;
pub use health_strategy::HealthWaitStrategy;
#[cfg(feature = "http_wait_plain")]
pub use http_strategy::{HttpResponse, HttpWaitError, HttpWaitStrategy};
pub use log_strategy::LogWaitStrategy;
use std::time::Duration;
use crate::{
ContainerAsync, Image,
core::{client::Client, error::Result},
};
#[derive(Debug, Clone)]
pub enum WaitFor {
Nothing,
Log(LogWaitStrategy),
Duration {
length: Duration,
},
Healthcheck(HealthWaitStrategy),
#[cfg(feature = "http_wait_plain")]
Http(Box<HttpWaitStrategy>),
Exit(ExitWaitStrategy),
}
impl WaitFor {
pub fn message_on_stdout(message: impl AsRef<[u8]>) -> WaitFor {
Self::log(LogWaitStrategy::new(
crate::core::logs::LogSource::StdOut,
message,
))
}
pub fn message_on_stderr(message: impl AsRef<[u8]>) -> WaitFor {
Self::log(LogWaitStrategy::new(
crate::core::logs::LogSource::StdErr,
message,
))
}
pub fn message_on_either_std(message: impl AsRef<[u8]>) -> WaitFor {
Self::log(LogWaitStrategy::new(
crate::core::logs::LogSource::BothStd,
message,
))
}
pub fn log(log_strategy: LogWaitStrategy) -> WaitFor {
WaitFor::Log(log_strategy)
}
#[cfg(feature = "http_wait_plain")]
pub fn http(http_strategy: HttpWaitStrategy) -> WaitFor {
WaitFor::Http(Box::new(http_strategy))
}
pub fn healthcheck() -> WaitFor {
WaitFor::Healthcheck(HealthWaitStrategy::default())
}
pub fn exit(exit_strategy: ExitWaitStrategy) -> WaitFor {
WaitFor::Exit(exit_strategy)
}
pub fn seconds(length: u64) -> WaitFor {
WaitFor::Duration {
length: Duration::from_secs(length),
}
}
pub fn millis(length: u64) -> WaitFor {
WaitFor::Duration {
length: Duration::from_millis(length),
}
}
pub fn millis_in_env_var(name: &'static str) -> WaitFor {
let additional_sleep_period = std::env::var(name).map(|value| value.parse());
(|| {
let length = additional_sleep_period.ok()?.ok()?;
Some(WaitFor::Duration {
length: Duration::from_millis(length),
})
})()
.unwrap_or(WaitFor::Nothing)
}
}
#[cfg(feature = "http_wait_plain")]
impl From<HttpWaitStrategy> for WaitFor {
fn from(value: HttpWaitStrategy) -> Self {
Self::Http(Box::new(value))
}
}
impl WaitFor {
pub(crate) async fn wait_until_ready<I: Image>(
self,
client: &Client,
container: &ContainerAsync<I>,
) -> Result<()> {
match self {
WaitFor::Log(strategy) => strategy.wait_until_ready(client, container).await?,
WaitFor::Duration { length } => {
tokio::time::sleep(length).await;
}
WaitFor::Healthcheck(strategy) => {
strategy.wait_until_ready(client, container).await?;
}
#[cfg(feature = "http_wait_plain")]
WaitFor::Http(strategy) => {
strategy.wait_until_ready(client, container).await?;
}
WaitFor::Exit(strategy) => {
strategy.wait_until_ready(client, container).await?;
}
WaitFor::Nothing => {}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
const WAIT_MILLIS_ENV: &str = "SHIGUREDO_CONTAINER_WAIT_MILLIS_TEST";
const WAIT_MILLIS_CHILD_ENV: &str = "SHIGUREDO_CONTAINER_WAIT_MILLIS_TEST_CHILD";
fn run_env_case(value: Option<&str>) {
let executable = std::env::current_exe().expect("テストバイナリのパスを取得できること");
let mut command = std::process::Command::new(executable);
command.args(["--exact", "core::wait::tests::millis_in_env_var_child"]);
command.env(WAIT_MILLIS_CHILD_ENV, "1");
match value {
Some(value) => {
command.env(WAIT_MILLIS_ENV, value);
}
None => {
command.env_remove(WAIT_MILLIS_ENV);
}
}
let status = command
.status()
.expect("環境変数を検証する子テストを起動できること");
assert!(status.success(), "子テストが成功すること: {status}");
}
#[test]
fn millis_in_env_var_handles_missing_invalid_and_valid_values() {
run_env_case(None);
run_env_case(Some("invalid"));
run_env_case(Some("125"));
}
#[test]
fn millis_in_env_var_child() {
if std::env::var_os(WAIT_MILLIS_CHILD_ENV).is_none() {
return;
}
match std::env::var(WAIT_MILLIS_ENV).ok().as_deref() {
Some("125") => {
assert!(matches!(
WaitFor::millis_in_env_var(WAIT_MILLIS_ENV),
WaitFor::Duration { length } if length == Duration::from_millis(125)
));
}
None | Some("invalid") => {
assert!(matches!(
WaitFor::millis_in_env_var(WAIT_MILLIS_ENV),
WaitFor::Nothing
));
}
Some(value) => panic!("想定外の子プロセス環境変数値: {value}"),
}
}
#[cfg(feature = "http_wait_plain")]
#[test]
fn wait_for_from_http_wait_strategy() {
let strategy = HttpWaitStrategy::new("/health");
let wait = WaitFor::from(strategy);
assert!(matches!(wait, WaitFor::Http(_)));
}
}