1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use std::time::Duration;
use crate::{Record, TypedValue};
pub const RETRY_INTERVAL: Duration = Duration::from_secs(1);
pub const MAX_RETRIES: usize = 120;
pub fn debug_print_records<T>(records: &[Record<T>])
where
T: TypedValue,
{
for record in records {
debug_print_record(record)
}
}
pub fn debug_print_record<T>(record: &Record<T>)
where
T: TypedValue,
{
eprintln!(
r#"<Record {}_{} [{}]>"#,
record.id(),
record.typ(),
record.value.label().unwrap_or_default()
);
}
pub struct RetryOpts {
pub max_retries: usize,
pub name: Option<String>,
pub interval: Duration,
}
impl Default for RetryOpts {
fn default() -> Self {
Self {
max_retries: MAX_RETRIES,
interval: RETRY_INTERVAL,
name: None,
}
}
}
impl RetryOpts {
pub fn with_name(name: String) -> Self {
Self {
name: Some(name),
..Default::default()
}
}
}
pub async fn wait_for_ready(
client: &reqwest::Client,
opts: RetryOpts,
req_builder: impl Fn() -> Result<reqwest::Request, reqwest::Error>,
) -> Result<(), std::io::Error> {
let mut interval = tokio::time::interval(opts.interval);
let name = opts.name.unwrap_or_default();
for _i in 0..opts.max_retries {
let req = req_builder()
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", err)))?;
let url = req.url().to_string();
match client.execute(req).await {
Ok(res) => {
if res.status().is_success() {
return Ok(());
} else {
log::warn!(
"Failed to connect to {} at {}: {}",
name,
url,
res.status().canonical_reason().unwrap()
);
}
}
Err(err) => {
log::warn!("Failed to connect to {} at {}: {}", name, url, err);
}
}
interval.tick().await;
}
Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Cannot reach service {}",
))
}