This repository has been archived on 2024-03-03. You can view files and clone it, but cannot push or open issues or pull requests.
clear-docker-images/src/main.rs

137 lines
3.4 KiB
Rust
Raw Normal View History

2022-02-20 23:06:15 +01:00
mod images;
2022-02-08 13:50:34 +01:00
use chrono::{NaiveDateTime, Utc};
2022-02-08 13:50:34 +01:00
use clap::Parser;
2022-03-06 00:24:47 +01:00
use log::{error, info};
use simple_logger::SimpleLogger;
2022-03-16 12:17:15 +01:00
use std::process::exit;
2022-02-20 23:06:15 +01:00
2022-03-16 12:17:15 +01:00
use crate::images::DockerActions;
2022-02-08 13:50:34 +01:00
const TWO_DAYS_TIMESTAMP: i64 = 172_800;
2022-02-08 13:50:34 +01:00
/// Clear docker images from
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
2022-02-20 16:59:06 +01:00
/// filter by date.
///
2022-03-06 04:29:22 +01:00
/// Can filter by a minimum age $DATE or from $START|$STOP (format example: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) [default: $NOW - 2d]
#[clap(short, long, parse(try_from_str = parse_user_date))]
2022-03-06 04:29:22 +01:00
date: Option<DateArgs>,
2022-02-17 20:28:39 +01:00
2022-02-08 16:24:20 +01:00
/// filter by repository name
2022-02-08 13:50:34 +01:00
repository: Option<String>,
2022-02-08 16:24:20 +01:00
/// add tags exclusion
#[clap(short, long)]
tags: Option<Vec<String>>,
2022-02-20 16:59:06 +01:00
/// image cleanup will not be triggered [default: false]
2022-02-08 13:50:34 +01:00
#[clap(long, takes_value = false)]
dry_run: bool,
2022-02-08 16:24:20 +01:00
2022-03-16 23:57:15 +01:00
/// should force image deletion [default: false]
#[clap(short, long, takes_value = false)]
force: bool,
2022-03-16 12:17:15 +01:00
/// where is located the docker socket (can be a UNIX socket or TCP protocol)
#[clap(short, long, default_value = "/var/run/docker.sock")]
socket: String,
2022-02-08 13:50:34 +01:00
}
#[derive(Debug)]
pub struct DateArgs {
start: i64,
stop: Option<i64>,
}
2022-03-16 12:17:15 +01:00
#[tokio::main]
async fn main() {
2022-03-06 04:29:22 +01:00
let args = Args::parse();
let logger = SimpleLogger::new()
.without_timestamps()
2022-03-16 13:49:01 +01:00
.with_level(log::LevelFilter::Info);
2022-03-06 04:29:22 +01:00
if let Some(e) = logger.init().err() {
eprintln!("failed to initialize logger: {}", e);
exit(1);
}
2022-03-16 23:57:15 +01:00
let actions = match DockerActions::new(
2022-03-16 12:17:15 +01:00
args.socket,
2022-03-06 04:29:22 +01:00
args.repository,
2022-03-16 12:17:15 +01:00
args.tags.map_or(vec![], |t| t),
2022-03-06 04:29:22 +01:00
args.date.map_or(
DateArgs {
start: Utc::now().timestamp() - TWO_DAYS_TIMESTAMP,
stop: None,
},
|d| d,
),
2022-03-16 23:57:15 +01:00
) {
Ok(d) => d,
Err(e) => {
error!("failed to connect to docker socket: {}", e);
exit(1);
}
};
2022-02-08 13:50:34 +01:00
2022-03-16 12:17:15 +01:00
let images = match actions.get().await {
Ok(i) => i,
Err(e) => {
error!("failed to retrieve docker images: {}", e);
exit(1);
2022-02-17 20:28:39 +01:00
}
2022-03-16 12:17:15 +01:00
};
2022-02-17 20:28:39 +01:00
2022-03-16 23:57:15 +01:00
let saved = match actions
.delete(actions.filter(images), args.force, args.dry_run)
.await
{
2022-03-16 12:17:15 +01:00
Ok(s) => s,
Err(e) => {
2022-03-16 23:57:15 +01:00
error!("failed to delete docker images: {}", e);
2022-03-16 12:17:15 +01:00
exit(1);
2022-02-08 16:24:20 +01:00
}
2022-03-16 12:17:15 +01:00
};
2022-02-08 16:24:20 +01:00
2022-03-06 00:24:47 +01:00
info!(
2022-02-17 20:28:39 +01:00
"Total disk space saved: {}",
2022-03-16 12:17:15 +01:00
if saved / 1000_000 >= 1000 {
format!("{:.2}GB", saved as f64 / 1000_000_000.0)
2022-02-17 20:28:39 +01:00
} else {
2022-03-16 12:17:15 +01:00
format!("{:.2}MB", saved as f32 / 1000_000.0)
2022-02-17 20:28:39 +01:00
}
);
}
fn parse_user_date(date: &str) -> Result<DateArgs, &'static str> {
if date.contains("|") {
let dates: Vec<&str> = date.split("|").collect();
return Ok(DateArgs {
start: format_user_date(dates[0]),
stop: Some(format_user_date(dates[1])),
});
}
Ok(DateArgs {
start: format_user_date(date),
stop: None,
})
}
fn format_user_date(user_date: &str) -> i64 {
let date = if user_date.contains("T") {
user_date.to_string()
} else {
format!("{}T00:00:00", user_date)
};
NaiveDateTime::parse_from_str(&date, "%FT%T")
.map_or(Utc::now().timestamp() - TWO_DAYS_TIMESTAMP, |d| {
d.timestamp()
})
}