summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2026-02-09 22:03:13 +0100
committerJesper Jensen <jesper@jnsn.dev>2026-02-09 22:03:13 +0100
commit343eb8bb594ca32f78b91d220ca3c303ff1aa7a2 (patch)
tree3f4b1bca6e2774c68437ab2b63a28fe8d0202f7c /src/main.rs
parent22c7e9566c439e2134946611ccba81c7499ac62c (diff)
Simplify running in docker
We don't need all the commandline stuff we when're designing it for running as a docker container app. If we want to we can reintroduce the command syntax as a separate bin later
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs226
1 files changed, 25 insertions, 201 deletions
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, &registry, &repos, infile_paths.clone(), overwrite);
+ let next_wakeup = run_tool(&sqlite_db, &registry, &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());