diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2844e..d11993b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.25.0] - 2024-05-02 + +### Added +- Added `--min-age` argument. + ## [0.24.0] - 2024-04-05 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index f2bb73e..040d538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "docuum" -version = "0.24.0" +version = "0.25.0" dependencies = [ "atty", "byte-unit", @@ -229,6 +229,7 @@ dependencies = [ "dirs", "env_logger", "log", + "parse_duration", "regex", "serde", "serde_json", @@ -437,6 +438,73 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -462,6 +530,17 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "parse_duration" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d" +dependencies = [ + "lazy_static", + "num", + "regex", +] + [[package]] name = "proc-macro2" version = "1.0.66" diff --git a/Cargo.toml b/Cargo.toml index ab75564..8070427 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "docuum" -version = "0.24.0" +version = "0.25.0" authors = ["Stephan Boyer "] edition = "2021" description = "LRU eviction of Docker images." @@ -28,6 +28,7 @@ regex = { version = "1.5.5", default-features = false, features = ["std", "unico serde_json = "1.0" serde_yaml = "0.8" tempfile = "3" +parse_duration = "2.1.1" [target.'cfg(target_os = "linux")'.dependencies] sysinfo = "0.23.5" diff --git a/README.md b/README.md index 8e5e32f..aff7731 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ OPTIONS: -k, --keep ... Prevents deletion of images for which repository:tag matches + -m, --min-age + Sets the minimum age of images to be considered for deletion + -t, --threshold Sets the maximum amount of space to be used for Docker images (default: 10 GB) diff --git a/src/main.rs b/src/main.rs index 255cc82..89e913c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ use { clap::{App, AppSettings, Arg}, env_logger::{fmt::Color, Builder}, log::{Level, LevelFilter}, + parse_duration::parse, regex::RegexSet, std::{ env, @@ -37,6 +38,7 @@ const DEFAULT_THRESHOLD: &str = "10 GB"; const DELETION_CHUNK_SIZE_OPTION: &str = "deletion-chunk-size"; const KEEP_OPTION: &str = "keep"; const THRESHOLD_OPTION: &str = "threshold"; +const MIN_AGE_OPTION: &str = "min-age"; // Size threshold argument, absolute or relative to filesystem size #[derive(Copy, Clone)] @@ -110,6 +112,7 @@ pub struct Settings { threshold: Threshold, keep: Option, deletion_chunk_size: usize, + min_age: Option, } // Set up the logger. @@ -197,6 +200,13 @@ fn settings() -> io::Result { (default: {DEFAULT_DELETION_CHUNK_SIZE})", )), ) + .arg( + Arg::with_name(MIN_AGE_OPTION) + .value_name("MIN AGE") + .short("m") + .long(MIN_AGE_OPTION) + .help("Sets the minimum age of images to be considered for deletion"), + ) .get_matches(); // Read the threshold. @@ -225,10 +235,20 @@ fn settings() -> io::Result { None => DEFAULT_DELETION_CHUNK_SIZE, }; + // Determine the minimum age for images to be considered for deletion. + let min_age = match matches.value_of(MIN_AGE_OPTION) { + Some(value) => match parse(value) { + Ok(duration) => Some(duration), + Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), + }, + None => None, + }; + Ok(Settings { threshold, keep, deletion_chunk_size, + min_age, }) } diff --git a/src/run.rs b/src/run.rs index cb3967c..c0fd9ab 100644 --- a/src/run.rs +++ b/src/run.rs @@ -628,6 +628,7 @@ fn vacuum( threshold: Byte, keep: &Option, deletion_chunk_size: usize, + min_age: &Option, ) -> io::Result<()> { // Find all images. let image_records = list_image_records(state)?; @@ -670,6 +671,29 @@ fn vacuum( }); } + // If the `--min-age` argument is provided, we need to filter out images + // which are newer than the provided duration. + if let Some(duration) = min_age { + match (SystemTime::now() - *duration).duration_since(UNIX_EPOCH) { + Ok(time_stamp) => { + sorted_image_nodes.retain(|(image_id, image_node)| { + if image_node.last_used_since_epoch > time_stamp { + debug!( + "Ignored image {} due to the {} flag.", + image_id.code_str(), + "--min-age".code_str(), + ); + + return false; + } + + true + }); + } + Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidInput, e)), + }; + } + // Check if we're over the threshold. let mut deleted_image_ids = HashSet::new(); let space = space_usage()?; @@ -764,6 +788,7 @@ pub fn run( threshold, &settings.keep, settings.deletion_chunk_size, + &settings.min_age, )?; state::save(state)?; *first_run = false; @@ -842,6 +867,7 @@ pub fn run( threshold, &settings.keep, settings.deletion_chunk_size, + &settings.min_age, )?; }