const IMDS_BASE: &str = "http://169.254.169.254";
const IMDS_TOKEN_PATH: &str = "/latest/api/token";
const IMDS_TTL_HEADER: &str = "X-aws-ec2-metadata-token-ttl-seconds";
const IMDS_TOKEN_HEADER: &str = "X-aws-ec2-metadata-token";
const IMDS_TOKEN_TTL: &str = "21600";
const IMDS_TIMEOUT_SECS: u64 = 2;
pub(super) struct ImdsClient {
client: reqwest::blocking::Client,
token: String,
}
impl ImdsClient {
pub(super) fn new() -> Option<Self> {
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(IMDS_TIMEOUT_SECS))
.build()
.ok()?;
let token = client
.put(format!("{IMDS_BASE}{IMDS_TOKEN_PATH}"))
.header(IMDS_TTL_HEADER, IMDS_TOKEN_TTL)
.send()
.ok()?
.text()
.ok()?;
Some(Self { client, token })
}
pub(super) fn get(&self, path: &str) -> Option<String> {
let url = format!("{IMDS_BASE}/latest/meta-data/{path}");
self.client
.get(&url)
.header(IMDS_TOKEN_HEADER, &self.token)
.send()
.ok()
.and_then(|r| {
if r.status().is_success() {
r.text().ok()
} else {
None
}
})
}
pub(super) fn get_json<T: serde::de::DeserializeOwned>(&self, path: &str) -> Option<T> {
let url = format!("{IMDS_BASE}/latest/meta-data/{path}");
self.client
.get(&url)
.header(IMDS_TOKEN_HEADER, &self.token)
.send()
.ok()
.and_then(|r| {
if r.status().is_success() {
r.json().ok()
} else {
None
}
})
}
}