diff options
| -rw-r--r-- | Dockerfile | 2 | ||||
| -rw-r--r-- | src/main.rs | 226 |
2 files changed, 26 insertions, 202 deletions
@@ -19,4 +19,4 @@ VOLUME /workdir VOLUME /data EXPOSE 9090 -CMD [ "/app/version-bump", "--continuous", "--metrics-port", "9090", "-i", "--config", "/config/", "--scratch", "/workdir", "/data/db.sqlite" ] +CMD [ "/app/version-bump" ] diff --git a/src/main.rs b/src/main.rs index 24a1b01..c4366ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,27 +12,12 @@ 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::registry::{Registries, HttpRegistry, Config, Credentials}; use crate::updater::{FileInput, FilePatch, update_images}; use rand::distr::{Alphanumeric, SampleString}; use std::io::Write; -fn help(cmd: &str) { - println!( - "{} [options] [--] <FILE> -Search FILE for docker images and suggest updates - -Options: ---auth <REGISTRY> <USER> <PASS> Authenticate against REGISTRY (repeatable) ---anonymous-auth <REGISTRY> Access REGISTRY anonymously (repeatable) ---config <PATH> Read config from PATH ---scratch <DIR> Store temporary files in DIR ---metrics-port <PORT> Serve Prometheus metrics on PORT (default: disabled)", - cmd - ); -} - struct Repository { url: String, key: Option<std::path::PathBuf>, @@ -40,78 +25,19 @@ struct Repository { dest: std::path::PathBuf, } -enum Output { - Overlay(std::fs::File, std::path::PathBuf, std::path::PathBuf), - Stdout(std::io::Stdout), -} - -impl Output { - fn overlay_file(infile_path: &std::path::Path) -> Self { - loop { - let mut filename = std::ffi::OsString::new(); - filename.push(infile_path.file_name().unwrap()); - filename.push(std::ffi::OsStr::new(".edit")); - filename.push(Alphanumeric.sample_string(&mut rand::rng(), 16)); - let outfile_path = Some(infile_path.parent().unwrap().join(filename)); - if let Ok(output_file) = std::fs::File::create_new(outfile_path.as_ref().unwrap()) { - return Output::Overlay(output_file, outfile_path.unwrap().into(), infile_path.into()); - } - } - } - - fn commit(&mut self) { - match self { - Output::Overlay(_, outfile_path, infile_path) => { - std::fs::remove_file(&infile_path).unwrap(); - std::fs::rename(&outfile_path, &infile_path).unwrap(); - } - Output::Stdout(_) => {} - } - } -} - -impl std::ops::Deref for Output { - type Target = dyn Write; - - fn deref(&self) -> &Self::Target { - match self { - Output::Overlay(file, _, _) => file, - Output::Stdout(stdout) => stdout, - } - } -} - -impl std::ops::DerefMut for Output { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - Output::Overlay(file, _, _) => file, - Output::Stdout(stdout) => stdout, +fn create_temp_path(path: &std::path::Path) -> std::path::PathBuf { + loop { + let mut filename = std::ffi::OsString::new(); + filename.push(path.file_name().unwrap()); + filename.push(std::ffi::OsStr::new(".edit")); + filename.push(Alphanumeric.sample_string(&mut rand::rng(), 16)); + let temp_path = path.parent().unwrap().join(filename); + if !temp_path.exists() { + return temp_path; } } } -impl std::io::Write for Output { - fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { - return (**self).write(buf); - } - - fn flush(&mut self) -> std::io::Result<()> { - return (**self).flush(); - } - - fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { - return (**self).write_vectored(bufs); - } - - fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - return (**self).write_all(buf); - } - - fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { - return (**self).write_fmt(args); - } -} - fn is_dockerfile(path: &std::path::Path) -> bool { if let Some(name) = path.file_name() { if let Some(name) = name.to_str() { @@ -130,7 +56,7 @@ fn is_yaml(path: &std::path::Path) -> bool { return false; } -fn run_tool(db: &dyn Db, reg: &dyn Registries, repos: &Vec<Repository>, mut infile_paths: Vec<std::path::PathBuf>, overwrite: bool) -> chrono::DateTime<chrono::Utc> { +fn run_tool(db: &dyn Db, reg: &dyn Registries, repos: &Vec<Repository>, mut infile_paths: Vec<std::path::PathBuf>) -> chrono::DateTime<chrono::Utc> { for repo in repos { if repo.dest.exists() { let mut gitcmd = std::process::Command::new("git"); @@ -252,11 +178,8 @@ fn run_tool(db: &dyn Db, reg: &dyn Registries, repos: &Vec<Repository>, mut infi continue; } - let mut out = if overwrite { - Output::overlay_file(&file.path) - } else { - Output::Stdout(std::io::stdout()) - }; + let temp_path = create_temp_path(&file.path); + let mut out = std::fs::File::create(&temp_path).unwrap(); let mut current_position = 0; for patch in patches { @@ -268,7 +191,8 @@ fn run_tool(db: &dyn Db, reg: &dyn Registries, repos: &Vec<Repository>, mut infi } out.write_all(file.content[current_position..].as_bytes()).unwrap(); - out.commit(); + std::fs::remove_file(&file.path).unwrap(); + std::fs::rename(&temp_path, &file.path).unwrap(); } Err(msg) => { println!("{}: {}", file.path.display(), msg); @@ -345,116 +269,20 @@ fn run_tool(db: &dyn Db, reg: &dyn Registries, repos: &Vec<Repository>, mut infi } fn main() { - let argv: Vec<String> = std::env::args().collect(); - let mut it = argv.iter(); - let cmd = &it.next().unwrap(); + let config_path = std::path::PathBuf::from("/config/"); + let scratch_path = std::path::PathBuf::from("/workdir"); + let db_path = std::path::PathBuf::from("/data/db.sqlite"); - let mut auths = vec![]; - let mut positional: Vec<&str> = vec!(); - let mut overwrite = false; - let mut config_path = None; - let mut scratch_path = None; - let mut continuous = false; - let mut metrics_port: Option<u16> = None; - - loop { - match it.next().map(|x| x.as_str()) { - None => break, - Some("--auth") => { - if let Some(registry) = it.next() && let Some(username) = it.next() && let Some(password) = it.next() { - auths.push(Config { - host: registry.clone(), - credentials: Credentials::Basic(basic_auth(username, password)), - cache_ttl: chrono::Duration::minutes(1440), - }); - } else { - println!("Error: --auth requires three parameters"); - help(cmd); - std::process::exit(1); - } - }, - Some("--anonymous-auth") => { - if let Some(registry) = it.next() { - auths.push(Config { - host: registry.clone(), - credentials: Credentials::Anonymous, - cache_ttl: chrono::Duration::minutes(1440), - }); - } else { - println!("Error: --anonymous-auth requires a registry parameter"); - help(cmd); - std::process::exit(1); - } - }, - Some("--config") => { - if let Some(path) = it.next() { - config_path = Some(std::path::PathBuf::from(path)); - } else { - println!("Error: --config requires an parameters"); - help(cmd); - std::process::exit(1); - } - }, - Some("--scratch") => { - if let Some(path) = it.next() { - scratch_path = Some(std::path::PathBuf::from(path)); - } else { - println!("Error: --scratch requires an parameters"); - help(cmd); - std::process::exit(1); - } - }, - Some("--continuous") => { - continuous = true; - }, - Some("--metrics-port") => { - if let Some(port_str) = it.next() { - match port_str.parse::<u16>() { - Ok(port) => metrics_port = Some(port), - Err(_) => { - println!("Error: --metrics-port requires a valid port number"); - help(cmd); - std::process::exit(1); - } - } - } else { - println!("Error: --metrics-port requires a parameter"); - help(cmd); - std::process::exit(1); - } - }, - Some("-i") | Some("--inplace") => { - overwrite = true; - }, - Some("-h") | Some("--help") => { - help(cmd); - std::process::exit(0); - }, - Some(arg) => positional.push(arg), - }; - } - - let mut infile_paths = vec![]; - if positional.len() < 1 { - panic!("BAD ARGUMENTS"); - } - - let sqlite_db = SqliteDb::new(&std::path::PathBuf::from(positional[0])); - if positional.len() > 1 { - infile_paths.push(std::path::PathBuf::from(positional[1])); - } + let sqlite_db = SqliteDb::new(&db_path); + let infile_paths: Vec<std::path::PathBuf> = vec![]; let mut repos = vec![]; + let mut auths = vec![]; let mut docker_auths: Vec<(String, Credentials)> = vec![]; let mut registry_ttls: Vec<(String, chrono::Duration)> = vec![]; let workdir = std::env::current_dir().unwrap(); - if let Some(config_path) = config_path { - if !config_path.is_dir() { - println!("config is not a directory"); - std::process::exit(1); - } - + if config_path.is_dir() { let mut unsearched = vec![config_path]; while let Some(next) = unsearched.pop() { @@ -600,7 +428,7 @@ fn main() { url: k, key: Some(workdir.join(path.parent().unwrap().join(std::path::PathBuf::from(key_name)))), host: Some(workdir.join(path.parent().unwrap().join(std::path::PathBuf::from(host_name)))), - dest: scratch_path.as_ref().unwrap().join(dest), + dest: (&scratch_path).join(dest), }); possible_key = reader.parse_next(); @@ -716,23 +544,19 @@ fn main() { metrics::init(); metrics::get().state.set(metrics::STATE_IDLE); - if let Some(port) = metrics_port { - metrics::serve(port); - } + metrics::serve(9090); loop { metrics::get().state.set(metrics::STATE_ACTIVE); let run_start = std::time::Instant::now(); - let next_wakeup = run_tool(&sqlite_db, ®istry, &repos, infile_paths.clone(), overwrite); + let next_wakeup = run_tool(&sqlite_db, ®istry, &repos, infile_paths.clone()); let run_duration = run_start.elapsed().as_secs_f64(); metrics::get().run_duration.set(run_duration); metrics::get().next_wakeup.set(next_wakeup.timestamp() as f64); metrics::get().state.set(metrics::STATE_IDLE); - if !continuous { break; } - let sleep_duration = next_wakeup - chrono::offset::Utc::now(); println!("Waiting {} seconds for next run", sleep_duration.num_seconds()); std::thread::sleep(sleep_duration.to_std().unwrap()); |
