use std::sync::Arc;
use astrid_core::principal::PrincipalId;
use astrid_core::profile::PrincipalProfile;
use astrid_events::kernel_api::{AdminResponseBody, ResourceUsage};
use super::handlers::{
err_bad_input, err_profile, principal_profile_path, require_principal_exists, success_json,
};
pub(super) async fn quota_set(
kernel: &Arc<crate::Kernel>,
principal: PrincipalId,
quotas: astrid_core::profile::Quotas,
) -> AdminResponseBody {
if let Err(e) = quotas.validate() {
return err_bad_input(format!("quotas rejected: {e}"));
}
let _guard = kernel.admin_write_lock.lock().await;
let path = principal_profile_path(kernel, &principal);
if let Err(msg) = require_principal_exists(&principal, &path) {
return err_bad_input(msg);
}
let mut profile = match PrincipalProfile::load_from_path(&path) {
Ok(p) => p,
Err(e) => return err_profile(&principal, &e),
};
profile.quotas = quotas;
if let Err(e) = profile.save_to_path(&path) {
return err_profile(&principal, &e);
}
kernel.profile_cache.invalidate(&principal);
success_json(serde_json::json!({ "principal": principal.as_str() }))
}
pub(super) fn quota_get(kernel: &Arc<crate::Kernel>, principal: &PrincipalId) -> AdminResponseBody {
let path = principal_profile_path(kernel, principal);
if let Err(msg) = require_principal_exists(principal, &path) {
return err_bad_input(msg);
}
match kernel.profile_cache.resolve(principal) {
Ok(profile) => AdminResponseBody::Quotas(profile.quotas.clone()),
Err(e) => err_profile(principal, &e),
}
}
pub(super) fn usage_get(kernel: &Arc<crate::Kernel>, principal: &PrincipalId) -> AdminResponseBody {
let path = principal_profile_path(kernel, principal);
if let Err(msg) = require_principal_exists(principal, &path) {
return err_bad_input(msg);
}
match kernel.profile_cache.resolve(principal) {
Ok(profile) => {
let exempt = principal_is_exempt(kernel, principal, &profile);
AdminResponseBody::Usage(ResourceUsage {
principal: principal.clone(),
cpu_fuel_consumed_total: kernel.fuel_ledger.total(principal),
cpu_fuel_per_sec_limit: profile.quotas.max_cpu_fuel_per_sec,
exempt,
memory_bytes_limit_per_instance: profile.quotas.max_memory_bytes,
memory_bytes_current_total: None,
memory_bytes_peak_total: match kernel.memory_ledger.peak(principal) {
0 => None,
bytes => Some(bytes),
},
})
},
Err(e) => err_profile(principal, &e),
}
}
fn principal_is_exempt(
kernel: &Arc<crate::Kernel>,
principal: &PrincipalId,
profile: &PrincipalProfile,
) -> bool {
let groups = kernel.groups.load_full();
let check =
astrid_capabilities::CapabilityCheck::new(profile, groups.as_ref(), principal.clone());
astrid_core::EXEMPT_CAPABILITIES
.iter()
.any(|&cap| check.has(cap))
}