hive-agent: order a stored login by when the store wrote it, not by its number
A delete removes the leaf's metadata, so the KV number restarts at 1. A login made right after a delete, before the next poll, was version 1 again and looked no newer than the recorded 1, so it never installed. The version file now records the store's write time next to the number and a later write installs whatever its number. A record from before the time was kept falls back to the number and gets its time when the same number is read again.
This commit is contained in:
parent
5f5933fde9
commit
4b1012056c
1 changed files with 275 additions and 65 deletions
|
|
@ -48,6 +48,19 @@ pub const CERT_ROLE_TOKEN_TTL_SECS: u64 = 3600;
|
|||
/// this bound the call fails with [`Error::Vault`] instead.
|
||||
const VAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
/// The identity of one stored version of an object: its KV v2 number, and the
|
||||
/// time the store wrote it.
|
||||
///
|
||||
/// The number restarts when the object is deleted outright; the time does not,
|
||||
/// so a caller that must tell "a newer write" from "the same one again" across
|
||||
/// a delete compares the time.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Version {
|
||||
pub number: u64,
|
||||
/// RFC 3339, as the store spells it.
|
||||
pub created_time: String,
|
||||
}
|
||||
|
||||
/// Where the store is and which identity we present to it.
|
||||
///
|
||||
/// Separate from the connect so the environment can be checked without one:
|
||||
|
|
@ -234,18 +247,20 @@ impl SecretStore {
|
|||
}
|
||||
}
|
||||
|
||||
/// The newest version of the object at `path` and its KV v2 version
|
||||
/// number, or `None` when nothing is stored there.
|
||||
/// The newest version of the object at `path` and its KV v2 [`Version`], or
|
||||
/// `None` when nothing is stored there.
|
||||
///
|
||||
/// One read, so the number is the one the value was stored as. Every
|
||||
/// [`write`][Self::write] makes a larger one.
|
||||
/// One read, so the version is the one the value was stored as. Every
|
||||
/// [`write`][Self::write] makes a larger number, until the path is deleted
|
||||
/// outright and its numbering starts again; the time it was written, which
|
||||
/// the store stamps, keeps ordering across that.
|
||||
///
|
||||
/// # Errors
|
||||
/// As [`read_optional`][Self::read_optional].
|
||||
pub async fn read_versioned<T: DeserializeOwned>(
|
||||
&self,
|
||||
path: &str,
|
||||
) -> Result<Option<(T, u64)>, Error> {
|
||||
) -> Result<Option<(T, Version)>, Error> {
|
||||
let endpoint = vaultrs::api::kv2::requests::ReadSecretRequest::builder()
|
||||
.mount(MOUNT)
|
||||
.path(path)
|
||||
|
|
@ -255,7 +270,13 @@ impl SecretStore {
|
|||
Ok(res) => {
|
||||
let value = serde_json::from_value(res.data)
|
||||
.map_err(|source| vaultrs::error::ClientError::JsonParseError { source })?;
|
||||
Ok(Some((value, res.metadata.version)))
|
||||
Ok(Some((
|
||||
value,
|
||||
Version {
|
||||
number: res.metadata.version,
|
||||
created_time: res.metadata.created_time,
|
||||
},
|
||||
)))
|
||||
}
|
||||
Err(vaultrs::error::ClientError::APIError { code: 404, .. }) => Ok(None),
|
||||
Err(e) => Err(e.into()),
|
||||
|
|
|
|||
Loading…
Reference in a new issue