diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 179 |
1 files changed, 2 insertions, 177 deletions
diff --git a/src/main.rs b/src/main.rs index 8c7f5d8..57f0d76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,16 +6,16 @@ mod dockerfile; mod metrics; mod db; mod registry; +mod updater; -use crate::version::{VersionPattern, CompareOutcome}; use crate::docker::DockerRef; use crate::manifest::ManifestFile; use crate::dockerfile::DockerfileFile; use crate::db::{Db, SqliteDb}; use crate::registry::{Registries, HttpRegistry, Config, Credentials, basic_auth}; +use crate::updater::{FileInput, FilePatch, update_images}; use rand::distr::{Alphanumeric, SampleString}; -use std::ops::Range; use std::io::Write; fn help(cmd: &str) { @@ -40,181 +40,6 @@ struct Repository { dest: std::path::PathBuf, } -#[derive(Debug)] -struct FilePatch { - position: Range<usize>, - content: String, -} - -struct FileInput { - path: std::path::PathBuf, - content: String, - images: Vec<Range<usize>>, -} - -fn update_images(now: &chrono::DateTime<chrono::Utc>, db: &dyn Db, reg: &dyn Registries, files: &[FileInput], outcomes: &mut Vec<Result<Vec<FilePatch>, String>>) { - for file in files { - let mut patches = vec![]; - let mut error: Option<String> = None; - - let mut imgs: Vec<DockerRef> = vec![]; - let mut image_ids: Vec<i64> = vec![]; - let mut last_checkeds: Vec<chrono::DateTime<chrono::Utc>> = vec![]; - - for image in &file.images { - let img = DockerRef::parse(&file.content, image); - let registry = img.registry.as_ref() - .map(|x| &file.content[x.clone()]) - .unwrap_or("registry.hub.docker.com"); - let image_name = &file.content[img.image.clone()]; - - let (image_id, last_checked) = match db.get_image(registry, image_name) { - Some(x) => x, - None => { - let epoch = chrono::DateTime::<chrono::Utc>::UNIX_EPOCH; - let id = db.insert_image(registry, image_name, &epoch); - (id, epoch) - }, - }; - - imgs.push(img); - image_ids.push(image_id); - last_checkeds.push(last_checked); - } - - for i in 0..imgs.len() { - let img = &imgs[i]; - let registry = img.registry.as_ref() - .map(|x| &file.content[x.clone()]) - .unwrap_or("registry.hub.docker.com"); - let cache_max_age = reg.get_cache_ttl(registry); - let mut tag = img.tag.as_ref().map(|x| file.content[x.clone()].to_string()); - let image_name = &file.content[img.image.clone()]; - let image_id = image_ids[i]; - let last_checked = last_checkeds[i]; - - println!("Checking image {}", &file.content[file.images[i].clone()]); - metrics::get().images_checked.with_label_values(&[registry]).inc(); - - let cache_stale = *now - last_checked > cache_max_age; - - let cached_tags: Vec<String> = db.get_tags_sorted(image_id); - - let tags_result: Result<(Vec<String>, Option<String>), String> = if cache_stale { - println!("Invalid in cache"); - match reg.get_tags(registry, image_name) { - Some(mut fetched_tags) => { - fetched_tags.sort(); - let mut existing = cached_tags.iter().peekable(); - for t in &fetched_tags { - while existing.peek().is_some_and(|e| *e < t) { - let gone = existing.next().unwrap(); - db.delete_tag(image_id, gone); - } - if existing.peek() == Some(&t) { - existing.next(); - } else { - db.insert_tag(image_id, t, now); - } - } - for gone in existing { - db.delete_tag(image_id, gone); - } - db.update_last_checked(image_id, now); - - Ok((fetched_tags, None)) - } - None => { - db.update_last_checked(image_id, now); - Err(format!("image not found: {}", image_name)) - } - } - } else { - println!("Valid in cache"); - let tag_for_digest = tag.as_deref().unwrap_or("latest"); - let digest = db.get_tag_digest(image_id, tag_for_digest); - - Ok((cached_tags, digest)) - }; - - let (tags, cached_digest) = match tags_result { - Ok(v) => v, - Err(msg) => { - error = Some(msg); - break; - } - }; - - if let Some(ref tag_str) = tag { - let mut current = VersionPattern::parse(&tag_str); - let mut new_tag = None; - - for candidate_str in &tags { - let candidate = VersionPattern::parse(&candidate_str); - match current.compare(&candidate) { - CompareOutcome::Higher => { - new_tag = Some(candidate_str.clone()); - current = candidate; - }, - CompareOutcome::Lower => {}, - CompareOutcome::Incompatible => {}, - CompareOutcome::Identical => {}, - } - } - - if let Some(new_tag) = new_tag { - tag = Some(new_tag.clone()); - patches.push(FilePatch { - position: img.tag.clone().unwrap(), - content: new_tag, - }); - metrics::get().images_updated.with_label_values(&[registry]).inc(); - } - } - - if let Some(ref digest) = img.digest { - let tag_for_digest = tag.as_deref().unwrap_or("latest"); - - let digest_result = match cached_digest { - Some(d) => Ok(d), - None => { - match reg.get_digest(registry, image_name, tag_for_digest) { - Some(fetched) => { - db.update_tag_digest(image_id, tag_for_digest, &fetched, now); - Ok(fetched) - } - None => Err(format!("digest not found for {}:{}", image_name, tag_for_digest)), - } - } - }; - - let digest_string = match digest_result { - Ok(d) => d, - Err(msg) => { - error = Some(msg); - break; - } - }; - - if file.content[digest.clone()] != digest_string { - patches.push(FilePatch { - position: img.digest.clone().unwrap(), - content: digest_string, - }); - metrics::get().images_updated.with_label_values(&[registry]).inc(); - } - } - } - - if let Some(msg) = error { - outcomes.push(Err(msg)); - } else { - patches.sort_by_key(|e| e.position.start); - outcomes.push(Ok(patches)); - } - } -} - enum Output { Overlay(std::fs::File, std::path::PathBuf, std::path::PathBuf), Stdout(std::io::Stdout), |
