use base64::Engine;
use super::CHECKSUM;
use super::keyspace::Keyspace;
use crate::error::Error;
const KEY: &str = ".probe/checksum";
const CONDITIONAL_KEY: &str = ".probe/conditional";
const BODY: &[u8] = b"lfsx probe";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Checksums {
Enforced,
Ignored,
Unknown,
}
fn wrong_digest() -> String {
base64::engine::general_purpose::STANDARD.encode([0u8; 32])
}
pub(crate) async fn checksums(keys: &Keyspace) -> Checksums {
let signed = keys.signed_upload(KEY, vec![(CHECKSUM.to_owned(), wrong_digest())]);
let mut request = keys.client().put(&signed.href).body(BODY.to_vec());
for (name, value) in &signed.headers {
request = request.header(name, value);
}
if let Err(error) = request.send().await {
tracing::warn!(%error, "the object store could not be reached to check it verifies uploads");
return Checksums::Unknown;
}
match keys.head(KEY).await {
Err(Error::NotFound) => Checksums::Enforced,
Err(error) => {
tracing::warn!(%error, "the object store could not say whether it kept the probe");
Checksums::Unknown
}
Ok(_) => {
if let Err(error) = keys.delete(KEY).await {
tracing::warn!(%error, key = KEY, "the probe object could not be cleaned up");
}
Checksums::Ignored
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Conditional {
Enforced,
Ignored,
Unknown,
}
pub(crate) async fn conditional_writes(keys: &Keyspace) -> Conditional {
let _ = keys.delete(CONDITIONAL_KEY).await;
match keys.put_if_absent(CONDITIONAL_KEY, BODY.to_vec()).await {
Ok(true) => {}
Ok(false) => {
tracing::warn!(
key = CONDITIONAL_KEY,
"the probe key could not be cleared, so whether this store refuses a conditional \
write was not established"
);
return Conditional::Unknown;
}
Err(error) => {
tracing::warn!(%error, "the object store could not be reached to check it refuses a conditional write");
return Conditional::Unknown;
}
}
let verdict = match keys.put_if_absent(CONDITIONAL_KEY, BODY.to_vec()).await {
Ok(false) => Conditional::Enforced,
Ok(true) => Conditional::Ignored,
Err(error) => {
tracing::warn!(%error, "the object store gave no usable answer to a conditional write");
Conditional::Unknown
}
};
if let Err(error) = keys.delete(CONDITIONAL_KEY).await {
tracing::warn!(%error, key = CONDITIONAL_KEY, "the probe object could not be cleaned up");
}
verdict
}
#[cfg(test)]
mod tests;