mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-31 02:22:13 +00:00
feat(admin): expose KMS backend capabilities in status response
Surface the backend capability matrix as an optional, additive 'capabilities' field on the /v3/kms/status response so management clients can discover supported lifecycle operations. The field is skipped when unset, keeping the response shape unchanged for existing consumers, and legacy payloads without it still deserialize. Refs rustfs/backlog#1571 (part of rustfs/backlog#1562)
This commit is contained in:
@@ -438,7 +438,9 @@ mod tests {
|
||||
)
|
||||
.with_insecure_development_defaults();
|
||||
// Constructing the client performs no network I/O with token auth.
|
||||
let backend = vault::VaultKmsBackend::new(config).await.expect("vault kv2 backend should build");
|
||||
let backend = vault::VaultKmsBackend::new(config)
|
||||
.await
|
||||
.expect("vault kv2 backend should build");
|
||||
|
||||
insta::assert_json_snapshot!("vault_kv2_backend_capabilities", capabilities_snapshot(backend.capabilities()));
|
||||
}
|
||||
|
||||
@@ -159,6 +159,11 @@ impl KmsManager {
|
||||
pub async fn health_check(&self) -> Result<bool> {
|
||||
self.backend.health_check().await
|
||||
}
|
||||
|
||||
/// Report the capabilities of the configured backend
|
||||
pub fn backend_capabilities(&self) -> crate::backends::BackendCapabilities {
|
||||
self.backend.capabilities()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -184,6 +184,15 @@ impl ObjectEncryptionService {
|
||||
self.kms_manager.health_check().await
|
||||
}
|
||||
|
||||
/// Report the capabilities of the configured backend
|
||||
///
|
||||
/// # Returns
|
||||
/// The capability matrix advertised by the active KMS backend
|
||||
///
|
||||
pub fn backend_capabilities(&self) -> crate::backends::BackendCapabilities {
|
||||
self.kms_manager.backend_capabilities()
|
||||
}
|
||||
|
||||
/// Create a data encryption key for object encryption
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@@ -72,6 +72,10 @@ pub struct KmsStatusResponse {
|
||||
pub cache_enabled: bool,
|
||||
pub cache_stats: Option<CacheStatsResponse>,
|
||||
pub default_key_id: Option<String>,
|
||||
/// Capability matrix of the active backend. Additive field: omitted by
|
||||
/// older servers, so it must stay optional for consumers.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub capabilities: Option<rustfs_kms::backends::BackendCapabilities>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -204,6 +208,7 @@ impl Operation for KmsStatusHandler {
|
||||
cache_enabled: config.as_ref().is_some_and(|cfg| cfg.enable_cache),
|
||||
cache_stats,
|
||||
default_key_id: service.get_default_key_id().cloned(),
|
||||
capabilities: Some(service.backend_capabilities()),
|
||||
};
|
||||
|
||||
let data = serde_json::to_vec(&response).map_err(|e| s3_error!(InternalError, "failed to serialize response: {}", e))?;
|
||||
@@ -339,4 +344,34 @@ mod tests {
|
||||
fn kms_clear_cache_rejects_server_info_fallback() {
|
||||
assert_lacks_action(&kms_clear_cache_actions(), Action::AdminAction(AdminAction::ServerInfoAdminAction));
|
||||
}
|
||||
|
||||
/// The `capabilities` field is additive: payloads produced by older
|
||||
/// servers (without the field) must keep deserializing, and the field
|
||||
/// must be omitted from JSON when unset so existing consumers see an
|
||||
/// unchanged response shape.
|
||||
#[test]
|
||||
fn kms_status_response_capabilities_field_is_additive() {
|
||||
let legacy_json = serde_json::json!({
|
||||
"backend_type": "local",
|
||||
"backend_status": "healthy",
|
||||
"cache_enabled": true,
|
||||
"cache_stats": null,
|
||||
"default_key_id": null,
|
||||
});
|
||||
let legacy: super::KmsStatusResponse =
|
||||
serde_json::from_value(legacy_json).expect("legacy status payload should deserialize");
|
||||
assert!(legacy.capabilities.is_none());
|
||||
|
||||
let serialized = serde_json::to_value(&legacy).expect("status response should serialize");
|
||||
assert!(serialized.get("capabilities").is_none(), "unset capabilities must be omitted");
|
||||
|
||||
let with_capabilities = super::KmsStatusResponse {
|
||||
capabilities: Some(rustfs_kms::backends::BackendCapabilities::minimal()),
|
||||
..legacy
|
||||
};
|
||||
let serialized = serde_json::to_value(&with_capabilities).expect("status response should serialize");
|
||||
let capabilities = serialized.get("capabilities").expect("capabilities must be present when set");
|
||||
assert_eq!(capabilities.get("encrypt"), Some(&serde_json::Value::Bool(true)));
|
||||
assert_eq!(capabilities.get("rotate"), Some(&serde_json::Value::Bool(false)));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user