summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs107
-rw-r--r--src/registry.rs56
2 files changed, 133 insertions, 30 deletions
diff --git a/src/main.rs b/src/main.rs
index 96d3d1e..7c62521 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,7 @@ use crate::docker::DockerRef;
use crate::manifest::ManifestFile;
use crate::dockerfile::DockerfileFile;
use crate::db::{Db, SqliteDb};
-use crate::registry::{Registries, HttpRegistry, AuthInfo, Credentials, basic_auth};
+use crate::registry::{Registries, HttpRegistry, Config, Credentials, basic_auth};
use rand::distr::{Alphanumeric, SampleString};
use std::ops::Range;
@@ -82,13 +82,12 @@ fn update_images(now: &chrono::DateTime<chrono::Utc>, db: &dyn Db, reg: &dyn Reg
last_checkeds.push(last_checked);
}
- let cache_max_age = chrono::Duration::hours(24);
-
for i in 0..imgs.len() {
let img = &imgs[i];
let registry = img.registry.as_ref()
.map(|x| &file.content[x.clone()])
.unwrap_or("registry.hub.docker.com");
+ let cache_max_age = reg.get_cache_ttl(registry);
let mut tag = img.tag.as_ref().map(|x| file.content[x.clone()].to_string());
let image_name = &file.content[img.image.clone()];
let image_id = image_ids[i];
@@ -513,9 +512,10 @@ fn main() {
None => break,
Some("--auth") => {
if let Some(registry) = it.next() && let Some(username) = it.next() && let Some(password) = it.next() {
- auths.push(AuthInfo {
+ 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");
@@ -525,9 +525,10 @@ fn main() {
},
Some("--anonymous-auth") => {
if let Some(registry) = it.next() {
- auths.push(AuthInfo {
+ auths.push(Config {
host: registry.clone(),
credentials: Credentials::Anonymous,
+ cache_ttl: chrono::Duration::minutes(1440),
});
} else {
println!("Error: --anonymous-auth requires a registry parameter");
@@ -594,6 +595,8 @@ fn main() {
}
let mut repos = 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 {
@@ -652,10 +655,7 @@ fn main() {
match reader.parse_next() {
Ok(json_event_parser::JsonEvent::String(v)) => {
- auths.push(AuthInfo {
- host: key,
- credentials: Credentials::Basic(v.into_owned()),
- });
+ docker_auths.push((key, Credentials::Basic(v.into_owned())));
},
_ => panic!("Invalid config json"),
}
@@ -766,6 +766,65 @@ fn main() {
_ => panic!("Invalid config json"),
}
}
+ Some("registry") => {
+ let file = std::fs::File::open(&path).unwrap();
+ let mut reader = json_event_parser::ReaderJsonParser::new(file);
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "registries" => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ let mut possible_key = reader.parse_next();
+ while let Ok(json_event_parser::JsonEvent::ObjectKey(k)) = possible_key {
+ let host = k.into_owned();
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::StartObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::ObjectKey(k)) if &k == "cache_ttl_minutes" => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::Number(n)) => {
+ let minutes: i64 = n.parse().unwrap();
+ registry_ttls.push((host, chrono::Duration::minutes(minutes)));
+ },
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ possible_key = reader.parse_next();
+ }
+
+ match possible_key {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+
+ match reader.parse_next() {
+ Ok(json_event_parser::JsonEvent::EndObject) => {},
+ _ => panic!("Invalid registry config json"),
+ }
+ }
Some(_) | None => {}
}
}
@@ -773,6 +832,36 @@ fn main() {
}
}
+ // Merge docker_auths and registry_ttls into final configs
+ for (host, creds) in docker_auths {
+ let mut ttl = chrono::Duration::minutes(1440);
+ for (h, t) in &registry_ttls {
+ if h == &host {
+ ttl = *t;
+ break;
+ }
+ }
+ auths.push(Config { host, credentials: creds, cache_ttl: ttl });
+ }
+
+ // Add registry_ttls entries that don't have auth (anonymous)
+ for (host, ttl) in registry_ttls {
+ let mut found = false;
+ for c in &auths {
+ if c.host == host {
+ found = true;
+ break;
+ }
+ }
+ if !found {
+ auths.push(Config {
+ host,
+ credentials: Credentials::Anonymous,
+ cache_ttl: ttl,
+ });
+ }
+ }
+
let registry = HttpRegistry::new(auths);
metrics::init();
diff --git a/src/registry.rs b/src/registry.rs
index 45cf625..b18c1eb 100644
--- a/src/registry.rs
+++ b/src/registry.rs
@@ -6,6 +6,7 @@ use std::collections::HashMap;
pub trait Registries {
fn get_tags(&self, registry: &str, image: &str) -> Option<Vec<String>>;
fn get_digest(&self, registry: &str, image: &str, tag: &str) -> Option<String>;
+ fn get_cache_ttl(&self, registry: &str) -> chrono::Duration;
}
#[derive(Debug)]
@@ -44,9 +45,10 @@ pub enum Credentials {
Basic(String),
}
-pub struct AuthInfo {
+pub struct Config {
pub host: String,
pub credentials: Credentials,
+ pub cache_ttl: chrono::Duration,
}
enum AuthStage {
@@ -54,15 +56,15 @@ enum AuthStage {
Authorized(String),
}
-struct AuthState {
- info: AuthInfo,
- stage: AuthStage,
+struct RegistryState {
+ config: Config,
+ auth_stage: AuthStage,
}
-impl AuthState {
+impl RegistryState {
fn add_to_request<T>(&self, req: ureq::RequestBuilder<T>) -> ureq::RequestBuilder<T> {
- if let AuthStage::Authorized(x) = &self.stage {
+ if let AuthStage::Authorized(x) = &self.auth_stage {
return req.header("Authorization", x);
}
@@ -70,25 +72,25 @@ impl AuthState {
}
fn authenticate(&mut self, response: &ureq::http::Response<ureq::Body>) -> Result<(), String> {
- match self.stage {
+ match self.auth_stage {
AuthStage::Authorized(_) =>
// The token must have expired
- self.stage = AuthStage::Idle,
+ self.auth_stage = AuthStage::Idle,
AuthStage::Idle => {}
}
let auth_header = response.headers().get("www-authenticate").unwrap();
match AuthMethod::from_header(auth_header) {
Some(AuthMethod::Basic) => {
- match &self.info.credentials {
+ match &self.config.credentials {
Credentials::Basic(creds) => {
- self.stage = AuthStage::Authorized(format!("Basic {}", creds));
+ self.auth_stage = AuthStage::Authorized(format!("Basic {}", creds));
return Ok(());
}
Credentials::Anonymous => {
return Err(format!(
"Server {} requires Basic auth but configured as anonymous",
- self.info.host
+ self.config.host
));
}
}
@@ -101,7 +103,7 @@ impl AuthState {
.http_status_as_error(false)
.build();
- if let Credentials::Basic(creds) = &self.info.credentials {
+ if let Credentials::Basic(creds) = &self.config.credentials {
request = request.header("Authorization", format!("Basic {}", creds));
}
@@ -111,7 +113,7 @@ impl AuthState {
.map_err(|x| {
format!(
"Server {} authentication request failed: {}",
- self.info.host,
+ self.config.host,
x.to_string()
)
})?
@@ -120,7 +122,7 @@ impl AuthState {
.expect(
format!(
"Server {} responded with something non-string like",
- self.info.host
+ self.config.host
)
.as_str(),
);
@@ -128,13 +130,13 @@ impl AuthState {
let body = body.parse::<tinyjson::JsonValue>().unwrap();
let token = body["token"].get::<String>().unwrap();
- self.stage = AuthStage::Authorized(format!("Bearer {}", token));
+ self.auth_stage = AuthStage::Authorized(format!("Bearer {}", token));
return Ok(());
}
None => {
return Err(format!(
"Server {} provided us with a challenge, but we didn't understand it",
- self.info.host
+ self.config.host
))
}
}
@@ -148,16 +150,16 @@ enum RegistryError {
}
pub struct HttpRegistry {
- auth: RefCell<HashMap<String, AuthState>>,
+ auth: RefCell<HashMap<String, RegistryState>>,
}
impl HttpRegistry {
- pub fn new(infos: Vec<AuthInfo>) -> Self {
+ pub fn new(infos: Vec<Config>) -> Self {
let mut states = HashMap::new();
for info in infos {
- states.insert(info.host.clone(), AuthState {
- info: info,
- stage: AuthStage::Idle,
+ states.insert(info.host.clone(), RegistryState {
+ config: info,
+ auth_stage: AuthStage::Idle,
});
}
return HttpRegistry { auth: RefCell::new(states) };
@@ -311,6 +313,14 @@ impl Registries for HttpRegistry {
.to_string();
return Some(digest);
}
+
+ fn get_cache_ttl(&self, registry: &str) -> chrono::Duration {
+ let auth = self.auth.borrow();
+ if let Some(state) = auth.get(registry) {
+ return state.config.cache_ttl;
+ }
+ return chrono::Duration::minutes(1440);
+ }
}
#[cfg(test)]
@@ -384,6 +394,10 @@ impl Registries for StubRegistry {
}
return None;
}
+
+ fn get_cache_ttl(&self, _registry: &str) -> chrono::Duration {
+ return chrono::Duration::minutes(1440);
+ }
}
#[cfg(test)]