Record and show that an image is already on its newest tag

An image that had been checked and is current looked exactly like one
that was never checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dennis Nemec
2026-09-03 20:37:46 +02:00
parent 1cb634afca
commit 4efff51aa7
6 changed files with 69 additions and 15 deletions

View File

@ -64,15 +64,21 @@ impl ImageUpdateService {
}
/// Scan the newest available tag and record which of the open findings it fixes.
pub async fn check(
&self,
image: &str,
log: &dyn JobLog,
) -> Result<Option<ImageUpdate>, DomainError> {
pub async fn check(&self, image: &str, log: &dyn JobLog) -> Result<ImageUpdate, DomainError> {
let Some(candidate) = self.candidate(image).await? else {
log.line(&format!("{image}: already on the newest tag"))
.await;
return Ok(None);
// record the check so the view can tell "current" from "never checked"
let update = ImageUpdate {
image: image.to_string(),
candidate: String::new(),
checked_at: Utc::now(),
fixed: Vec::new(),
fixed_counts: SeverityCounts::default(),
candidate_total: 0,
};
self.updates.upsert(&update).await?;
return Ok(update);
};
log.line(&format!("{image}: candidate {candidate}, scanning it"))
.await;
@ -124,7 +130,7 @@ impl ImageUpdateService {
update.candidate_total
))
.await;
Ok(Some(update))
Ok(update)
}
/// Check every image that currently runs on the cluster.
@ -138,8 +144,8 @@ impl ImageUpdateService {
let mut found = 0;
for image in images {
match self.check(&image, log).await {
Ok(Some(_)) => found += 1,
Ok(None) => {}
Ok(u) if !u.up_to_date() => found += 1,
Ok(_) => {}
Err(e) => log.line(&format!("{image}: check failed: {e}")).await,
}
}

View File

@ -92,7 +92,8 @@ async fn a_check_records_which_findings_the_candidate_fixes() {
seed(f.findings.clone(), running, Arc::new(MemCluster::default())).await;
let log = VecLog::default();
let update = svc.check(GITEA, &log).await.unwrap().unwrap();
let update = svc.check(GITEA, &log).await.unwrap();
assert!(!update.up_to_date());
assert_eq!(update.candidate, "gitea/gitea:1.23.0");
assert_eq!(update.fixed, vec!["CVE-1", "CVE-2"]);
assert_eq!(update.fixed_counts.critical, 1);
@ -111,11 +112,19 @@ async fn a_check_records_which_findings_the_candidate_fixes() {
}
#[tokio::test]
async fn nothing_is_recorded_when_the_image_is_current() {
async fn being_up_to_date_is_recorded_as_a_result_too() {
let (f, svc) = fixture(FakeScanner::default(), &["1.22.3"]);
let log = VecLog::default();
assert!(svc.check(GITEA, &log).await.unwrap().is_none());
assert!(f.updates.0.lock().unwrap().is_empty());
let update = svc.check(GITEA, &log).await.unwrap();
assert!(update.up_to_date(), "no newer tag exists");
assert!(update.candidate.is_empty());
assert!(update.fixed.is_empty());
// the result is stored, so the view can tell "checked and current" from "never checked"
assert_eq!(f.updates.0.lock().unwrap().len(), 1);
assert_eq!(
svc.stored(GITEA).await.unwrap().unwrap().checked_at,
update.checked_at
);
assert!(log
.0
.lock()