WP-10: contract and failing tests for OS and package inventory
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / ui (push) Has been cancelled

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-09-02 22:19:25 +02:00
parent b6c3ddf040
commit 7b6d242988
21 changed files with 698 additions and 0 deletions

View File

@ -0,0 +1,55 @@
//! Host inventory: operating system and installed packages.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct OsInfo {
pub hostname: String,
pub name: String,
pub version: String,
pub kernel: String,
pub uptime_secs: u64,
pub reboot_required: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PackageSource {
Apt,
Snap,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Package {
pub name: String,
pub source: PackageSource,
pub installed: String,
/// Newer version available, if any.
pub candidate: Option<String>,
pub is_security: bool,
}
impl Package {
pub fn is_upgradable(&self) -> bool {
self.candidate.is_some()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Inventory {
pub os: OsInfo,
pub packages: Vec<Package>,
pub refreshed_at: DateTime<Utc>,
}
impl Inventory {
pub fn upgradable(&self) -> usize {
self.packages.iter().filter(|p| p.is_upgradable()).count()
}
pub fn security_upgrades(&self) -> usize {
self.packages
.iter()
.filter(|p| p.is_upgradable() && p.is_security)
.count()
}
}

View File

@ -2,6 +2,7 @@
//! layer depends on. No I/O here.
pub mod auth;
pub mod error;
pub mod host;
pub mod jobs;
pub mod ports;
pub mod settings;

View File

@ -3,6 +3,7 @@ use async_trait::async_trait;
use uuid::Uuid;
use crate::auth::{AccessClaims, AuthEvent, RefreshToken};
use crate::host::{Inventory, OsInfo, Package};
use crate::jobs::{JobKind, JobRun, JobStatus};
use crate::settings::SmtpSettings;
use crate::user::{User, UserUpdate};
@ -76,3 +77,16 @@ pub trait JobRunRepository: Send + Sync {
async fn find_running(&self, kind: JobKind) -> Result<Option<JobRun>, DomainError>;
async fn last_finished(&self, kind: JobKind) -> Result<Option<JobRun>, DomainError>;
}
/// Reads the state of the Debian host.
#[async_trait]
pub trait HostInspector: Send + Sync {
async fn os_info(&self) -> Result<OsInfo, DomainError>;
async fn packages(&self) -> Result<Vec<Package>, DomainError>;
}
#[async_trait]
pub trait InventoryRepository: Send + Sync {
async fn save(&self, inventory: &Inventory) -> Result<(), DomainError>;
async fn load(&self) -> Result<Option<Inventory>, DomainError>;
}