summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs179
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),