summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.rs166
-rw-r--r--src/updater.rs46
2 files changed, 110 insertions, 102 deletions
diff --git a/src/db.rs b/src/db.rs
index f1a65fd..263d88d 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -19,15 +19,15 @@ pub struct Tag {
}
pub trait Db {
- fn insert_image(&self, image: &mut Image);
-
fn get_image(&self, registry: &str, image: &str) -> Option<Image>;
fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image>;
+ fn insert_images(&self, images: &mut [Image]);
+ fn update_images(&self, images: &[Image]);
+
fn get_tags_sorted(&self, image_id: i64) -> Vec<Tag>;
fn insert_tags(&self, tags: &mut [Tag]);
fn delete_tags(&self, tag_ids: &[i64]);
fn update_tags(&self, tags: &[Tag]);
- fn set_expires_at(&self, image_id: i64, expires_at: &DateTime<Utc>);
}
pub struct SqliteDb {
@@ -143,12 +143,22 @@ impl Db for SqliteDb {
})).optional().unwrap();
}
- fn insert_image(&self, image: &mut Image) {
+ fn insert_images(&self, images: &mut [Image]) {
+ let _timer = crate::metrics::get().db_query_duration.start_timer();
+ for image in images {
+ self.conn.execute("
+ INSERT INTO images(registry, image, expires_at) VALUES (?1, ?2, ?3)
+ ", (&image.registry, &image.image, &image.expires_at)).unwrap();
+ image.id = self.conn.last_insert_rowid();
+ }
+ }
+
+ fn update_images(&self, images: &[Image]) {
let _timer = crate::metrics::get().db_query_duration.start_timer();
- self.conn.execute("
- INSERT INTO images(registry, image, expires_at) VALUES (?1, ?2, ?3)
- ", (&image.registry, &image.image, &image.expires_at)).unwrap();
- image.id = self.conn.last_insert_rowid();
+ for image in images {
+ self.conn.execute("UPDATE images SET expires_at = ?1 WHERE id = ?2",
+ (&image.expires_at, &image.id)).unwrap();
+ }
}
fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image> {
@@ -209,10 +219,6 @@ impl Db for SqliteDb {
}
}
- fn set_expires_at(&self, image_id: i64, expires_at: &DateTime<Utc>) {
- let _timer = crate::metrics::get().db_query_duration.start_timer();
- self.conn.execute("UPDATE images SET expires_at = ?1 WHERE id = ?2", (expires_at, image_id)).unwrap();
- }
}
#[cfg(test)]
@@ -249,16 +255,24 @@ impl Db for StubDb {
return None;
}
- fn insert_image(&self, image: &mut Image) {
- let id = *self.next_id.borrow();
- *self.next_id.borrow_mut() += 1;
- image.id = id;
- self.images.borrow_mut().push(Image {
- id,
- registry: image.registry.clone(),
- image: image.image.clone(),
- expires_at: image.expires_at,
- });
+ fn insert_images(&self, images: &mut [Image]) {
+ for image in images {
+ let id = *self.next_id.borrow();
+ *self.next_id.borrow_mut() += 1;
+ image.id = id;
+ self.images.borrow_mut().push(image.clone());
+ }
+ }
+
+ fn update_images(&self, images: &[Image]) {
+ for image in images {
+ for img in self.images.borrow_mut().iter_mut() {
+ if img.id == image.id {
+ img.expires_at = image.expires_at;
+ break;
+ }
+ }
+ }
}
fn get_expired_images(&self, now: &DateTime<Utc>) -> Vec<Image> {
@@ -311,15 +325,6 @@ impl Db for StubDb {
}
}
}
-
- fn set_expires_at(&self, image_id: i64, expires_at: &DateTime<Utc>) {
- for img in self.images.borrow_mut().iter_mut() {
- if img.id == image_id {
- img.expires_at = *expires_at;
- return;
- }
- }
- }
}
#[cfg(test)]
@@ -327,13 +332,14 @@ mod tests {
use super::*;
use chrono::TimeZone;
- fn test_insert_image_returns_incrementing_ids(db: &dyn Db) {
+ fn test_insert_images_returns_incrementing_ids(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img1 = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- let mut img2 = Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: now };
- db.insert_image(&mut img1);
- db.insert_image(&mut img2);
- assert!(img2.id > img1.id);
+ let mut images = [
+ Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now },
+ Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: now },
+ ];
+ db.insert_images(&mut images);
+ assert!(images[1].id > images[0].id);
}
fn test_get_image_returns_none_for_unknown(db: &dyn Db) {
@@ -342,99 +348,101 @@ mod tests {
fn test_get_image_returns_inserted(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
let got = db.get_image("docker.io", "nginx").unwrap();
- assert_eq!(got.id, img.id);
+ assert_eq!(got.id, images[0].id);
assert_eq!(got.expires_at, now);
}
fn test_get_tags_returns_empty_for_no_tags(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
- assert!(db.get_tags_sorted(img.id).is_empty());
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
+ assert!(db.get_tags_sorted(images[0].id).is_empty());
}
fn test_get_tags_returns_sorted(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
db.insert_tags(&mut [
- Tag { id: 0, image_id: img.id, tag: "2.0".into(), digest: None },
- Tag { id: 0, image_id: img.id, tag: "1.0".into(), digest: None },
- Tag { id: 0, image_id: img.id, tag: "latest".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "2.0".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "latest".into(), digest: None },
]);
- let tags: Vec<String> = db.get_tags_sorted(img.id).into_iter().map(|t| t.tag).collect();
+ let tags: Vec<String> = db.get_tags_sorted(images[0].id).into_iter().map(|t| t.tag).collect();
assert_eq!(tags, vec!["1.0", "2.0", "latest"]);
}
fn test_delete_tags_removes_tags(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
let mut tags = [
- Tag { id: 0, image_id: img.id, tag: "1.0".into(), digest: None },
- Tag { id: 0, image_id: img.id, tag: "2.0".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "2.0".into(), digest: None },
];
db.insert_tags(&mut tags);
db.delete_tags(&[tags[0].id]);
- let result: Vec<String> = db.get_tags_sorted(img.id).into_iter().map(|t| t.tag).collect();
+ let result: Vec<String> = db.get_tags_sorted(images[0].id).into_iter().map(|t| t.tag).collect();
assert_eq!(result, vec!["2.0"]);
}
- fn test_set_expires_at(db: &dyn Db) {
+ fn test_update_images(db: &dyn Db) {
let t1 = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
let t2 = Utc.with_ymd_and_hms(2000, 1, 1, 1, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: t1 };
- db.insert_image(&mut img);
- db.set_expires_at(img.id, &t2);
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: t1 }];
+ db.insert_images(&mut images);
+ images[0].expires_at = t2;
+ db.update_images(&images);
assert_eq!(db.get_image("docker.io", "nginx").unwrap().expires_at, t2);
}
fn test_tag_digest_none_when_unset(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
- db.insert_tags(&mut [Tag { id: 0, image_id: img.id, tag: "1.0".into(), digest: None }]);
- assert_eq!(db.get_tags_sorted(img.id)[0].digest, None);
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
+ db.insert_tags(&mut [Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None }]);
+ assert_eq!(db.get_tags_sorted(images[0].id)[0].digest, None);
}
fn test_update_tags(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
- let mut img = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now };
- db.insert_image(&mut img);
- let mut tags = [Tag { id: 0, image_id: img.id, tag: "1.0".into(), digest: None }];
+ let mut images = [Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: now }];
+ db.insert_images(&mut images);
+ let mut tags = [Tag { id: 0, image_id: images[0].id, tag: "1.0".into(), digest: None }];
db.insert_tags(&mut tags);
tags[0].digest = Some("sha256:abc".into());
db.update_tags(&tags);
- assert_eq!(db.get_tags_sorted(img.id)[0].digest, Some("sha256:abc".to_string()));
+ assert_eq!(db.get_tags_sorted(images[0].id)[0].digest, Some("sha256:abc".to_string()));
}
fn test_get_expired_images(db: &dyn Db) {
let now = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
let past = now - chrono::Duration::days(1);
let future = now + chrono::Duration::days(1);
- let mut expired = Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: past };
- let mut fresh = Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: future };
- db.insert_image(&mut expired);
- db.insert_image(&mut fresh);
+ let mut images = [
+ Image { id: 0, registry: "docker.io".into(), image: "nginx".into(), expires_at: past },
+ Image { id: 0, registry: "docker.io".into(), image: "redis".into(), expires_at: future },
+ ];
+ db.insert_images(&mut images);
let result = db.get_expired_images(&now);
assert_eq!(result.len(), 1);
- assert_eq!(result[0].id, expired.id);
+ assert_eq!(result[0].id, images[0].id);
assert_eq!(result[0].image, "nginx");
}
#[test]
- fn conformance_stub_insert_image_ids() {
- test_insert_image_returns_incrementing_ids(&StubDb::default());
+ fn conformance_stub_insert_images() {
+ test_insert_images_returns_incrementing_ids(&StubDb::default());
}
#[test]
- fn conformance_sqlite_insert_image_ids() {
+ fn conformance_sqlite_insert_images() {
crate::metrics::init();
let dir = tempfile::tempdir().unwrap();
- test_insert_image_returns_incrementing_ids(&SqliteDb::new(&dir.path().join("db.sqlite")));
+ test_insert_images_returns_incrementing_ids(&SqliteDb::new(&dir.path().join("db.sqlite")));
}
#[test]
@@ -498,15 +506,15 @@ mod tests {
}
#[test]
- fn conformance_stub_set_expires_at() {
- test_set_expires_at(&StubDb::default());
+ fn conformance_stub_update_images() {
+ test_update_images(&StubDb::default());
}
#[test]
- fn conformance_sqlite_set_expires_at() {
+ fn conformance_sqlite_update_images() {
crate::metrics::init();
let dir = tempfile::tempdir().unwrap();
- test_set_expires_at(&SqliteDb::new(&dir.path().join("db.sqlite")));
+ test_update_images(&SqliteDb::new(&dir.path().join("db.sqlite")));
}
#[test]
diff --git a/src/updater.rs b/src/updater.rs
index 7ab6483..b9dc555 100644
--- a/src/updater.rs
+++ b/src/updater.rs
@@ -34,14 +34,14 @@ pub fn update_images(now: &DateTime<Utc>, db: &dyn Db, reg: &dyn Registries, fil
let image_record = match db.get_image(registry, image_name) {
Some(x) => x,
None => {
- let mut new_image = Image {
+ let mut new_images = [Image {
id: 0,
registry: registry.to_string(),
image: image_name.to_string(),
expires_at: DateTime::<Utc>::UNIX_EPOCH,
- };
- db.insert_image(&mut new_image);
- new_image
+ }];
+ db.insert_images(&mut new_images);
+ new_images[0].clone()
},
};
@@ -52,7 +52,7 @@ pub fn update_images(now: &DateTime<Utc>, db: &dyn Db, reg: &dyn Registries, fil
}
// Refresh all expired images in the database
- for expired in db.get_expired_images(now) {
+ for mut expired in db.get_expired_images(now) {
let cache_max_age = reg.get_cache_ttl(&expired.registry);
let cached_tags = db.get_tags_sorted(expired.id);
@@ -78,12 +78,12 @@ pub fn update_images(now: &DateTime<Utc>, db: &dyn Db, reg: &dyn Registries, fil
}
db.delete_tags(&to_delete);
db.insert_tags(&mut to_insert);
- let new_expires_at = *now + cache_max_age;
- db.set_expires_at(expired.id, &new_expires_at);
+ expired.expires_at = *now + cache_max_age;
+ db.update_images(&[expired]);
}
None => {
- let new_expires_at = *now + cache_max_age;
- db.set_expires_at(expired.id, &new_expires_at);
+ expired.expires_at = *now + cache_max_age;
+ db.update_images(&[expired]);
}
}
}
@@ -215,11 +215,11 @@ mod tests {
]);
let past_expiry = now - chrono::Duration::days(1);
- let mut img = Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: past_expiry };
- db.insert_image(&mut img);
+ let mut images = [Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: past_expiry }];
+ db.insert_images(&mut images);
db.insert_tags(&mut [
- Tag { id: 0, image_id: img.id, tag: "1.20".into(), digest: None },
- Tag { id: 0, image_id: img.id, tag: "1.21".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "1.20".into(), digest: None },
+ Tag { id: 0, image_id: images[0].id, tag: "1.21".into(), digest: None },
]);
let files = vec![FileInput {
@@ -230,7 +230,7 @@ mod tests {
let mut outcomes = vec![];
update_images(&now, &db, &reg, &files, &mut outcomes);
- let tags: Vec<String> = db.get_tags_sorted(img.id).into_iter().map(|t| t.tag).collect();
+ let tags: Vec<String> = db.get_tags_sorted(images[0].id).into_iter().map(|t| t.tag).collect();
assert_eq!(tags, vec!["1.21", "1.22"]);
let got = db.get_image("registry.hub.docker.com", "nginx").unwrap();
assert!(got.expires_at > now);
@@ -244,9 +244,9 @@ mod tests {
let reg = StubRegistry::new(vec![]);
let future_expiry = now + chrono::Duration::minutes(30);
- let mut img = Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: future_expiry };
- db.insert_image(&mut img);
- db.insert_tags(&mut [Tag { id: 0, image_id: img.id, tag: "1.21".into(), digest: None }]);
+ let mut images = [Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: future_expiry }];
+ db.insert_images(&mut images);
+ db.insert_tags(&mut [Tag { id: 0, image_id: images[0].id, tag: "1.21".into(), digest: None }]);
let files = vec![FileInput {
path: "/test".into(),
@@ -352,9 +352,9 @@ mod tests {
let reg = StubRegistry::new(vec![]);
let future_expiry = now + chrono::Duration::minutes(30);
- let mut img = Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: future_expiry };
- db.insert_image(&mut img);
- db.insert_tags(&mut [Tag { id: 0, image_id: img.id, tag: "1.21".into(), digest: Some("sha256:0000000000000000000000000000000000000000000000000000000000000003".into()) }]);
+ let mut images = [Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: future_expiry }];
+ db.insert_images(&mut images);
+ db.insert_tags(&mut [Tag { id: 0, image_id: images[0].id, tag: "1.21".into(), digest: Some("sha256:0000000000000000000000000000000000000000000000000000000000000003".into()) }]);
let files = vec![FileInput {
path: "/test".into(),
@@ -585,9 +585,9 @@ mod tests {
]);
let past_expiry = now - chrono::Duration::days(1);
- let mut img = Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: past_expiry };
- db.insert_image(&mut img);
- db.insert_tags(&mut [Tag { id: 0, image_id: img.id, tag: "1.21".into(), digest: Some("sha256:0000000000000000000000000000000000000000000000000000000000000003".into()) }]);
+ let mut images = [Image { id: 0, registry: "registry.hub.docker.com".into(), image: "nginx".into(), expires_at: past_expiry }];
+ db.insert_images(&mut images);
+ db.insert_tags(&mut [Tag { id: 0, image_id: images[0].id, tag: "1.21".into(), digest: Some("sha256:0000000000000000000000000000000000000000000000000000000000000003".into()) }]);
let files = vec![FileInput {
path: "/test".into(),