From 7e2eeb6fc31c755b8b1380393ae09754e749a0e8 Mon Sep 17 00:00:00 2001 From: overtrue Date: Thu, 13 Aug 2026 08:22:04 +0800 Subject: [PATCH] fix(kms): teach the observing Vault deserializer the wrap-budget field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge race broke main: #6019 added wrap_budget_reserved to VaultKeyData (with a serde default), and #6040 — green against a base without that field — landed its unknown-field-observing manual Deserialize with an initializer that does not mention it, so rustfs-kms no longer compiles. Beyond the missing initializer field, the visitor would also have misclassified wrap_budget_reserved as an unknown field and dropped its value on every read, silently regressing the persisted wrap-budget count that #6019's whole design exists to preserve. The field is now a first-class visitor member: parsed when present, duplicate-checked, defaulting to 0 when absent (mirroring the struct's #[serde(default)] for records written before the field existed). Verification: cargo check -p rustfs-kms (fails on main, passes here); cargo test -p rustfs-kms --lib vault (178 passed); clippy --all-targets -D warnings clean. --- crates/kms/src/backends/vault.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/kms/src/backends/vault.rs b/crates/kms/src/backends/vault.rs index 86caf430f..e6c3b3c2e 100644 --- a/crates/kms/src/backends/vault.rs +++ b/crates/kms/src/backends/vault.rs @@ -208,6 +208,7 @@ impl<'de> Deserialize<'de> for VaultKeyData { RotatedAt, EncryptedKeyMaterial, BaselineVersion, + WrapBudgetReserved, Unknown(BoundedUnknownFieldName), } @@ -242,6 +243,7 @@ impl<'de> Deserialize<'de> for VaultKeyData { "rotated_at" => Field::RotatedAt, "encrypted_key_material" => Field::EncryptedKeyMaterial, "baseline_version" => Field::BaselineVersion, + "wrap_budget_reserved" => Field::WrapBudgetReserved, _ => Field::Unknown(BoundedUnknownFieldName::new(value)), }) } @@ -285,6 +287,7 @@ impl<'de> Deserialize<'de> for VaultKeyData { let mut rotated_at = None; let mut encrypted_key_material = None; let mut baseline_version = None; + let mut wrap_budget_reserved = None; let mut unknown_fields = UnknownFieldSummary::default(); while let Some(field) = map.next_key()? { @@ -301,6 +304,7 @@ impl<'de> Deserialize<'de> for VaultKeyData { Field::RotatedAt => read_field!(rotated_at, "rotated_at"), Field::EncryptedKeyMaterial => read_field!(encrypted_key_material, "encrypted_key_material"), Field::BaselineVersion => read_field!(baseline_version, "baseline_version"), + Field::WrapBudgetReserved => read_field!(wrap_budget_reserved, "wrap_budget_reserved"), Field::Unknown(field) => { let _: IgnoredAny = map.next_value()?; unknown_fields.observe(field); @@ -322,6 +326,9 @@ impl<'de> Deserialize<'de> for VaultKeyData { encrypted_key_material: encrypted_key_material .ok_or_else(|| de::Error::missing_field("encrypted_key_material"))?, baseline_version: baseline_version.unwrap_or(None), + // Mirrors the struct's #[serde(default)]: absent on records + // written before the wrap-budget field existed. + wrap_budget_reserved: wrap_budget_reserved.unwrap_or(0), }; unknown_fields.record_for_vault_kv2_key(); Ok(key_data)