m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-17 09:33:38 -05:00

Refactor logging, create context for passing around between functions instead of config

This commit is contained in:
Sergio
2025-02-14 19:24:35 +02:00
parent 6ae95bf83b
commit 550fb955a3
11 changed files with 156 additions and 137 deletions

View File

@@ -3,21 +3,19 @@ use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use crate::{ use crate::{
config::Config,
debug,
docker::get_images_from_docker_daemon, docker::get_images_from_docker_daemon,
http::Client, http::Client,
registry::{check_auth, get_token}, registry::{check_auth, get_token},
structs::{image::Image, update::Update}, structs::{image::Image, update::Update},
utils::request::{get_response_body, parse_json}, utils::request::{get_response_body, parse_json},
warn, Context,
}; };
/// Fetches image data from other Cup instances /// Fetches image data from other Cup instances
async fn get_remote_updates(servers: &FxHashMap<String, String>, client: &Client) -> Vec<Update> { async fn get_remote_updates(ctx: &Context, client: &Client) -> Vec<Update> {
let mut remote_images = Vec::new(); let mut remote_images = Vec::new();
let handles: Vec<_> = servers let handles: Vec<_> = ctx.config.servers
.iter() .iter()
.map(|(name, url)| async { .map(|(name, url)| async {
let url = if url.starts_with("http://") || url.starts_with("https://") { let url = if url.starts_with("http://") || url.starts_with("https://") {
@@ -28,7 +26,7 @@ async fn get_remote_updates(servers: &FxHashMap<String, String>, client: &Client
match client.get(&url, vec![], false).await { match client.get(&url, vec![], false).await {
Ok(response) => { Ok(response) => {
if response.status() != 200 { if response.status() != 200 {
warn!("GET {}: Failed to fetch updates from server. Server returned invalid response code: {}",url,response.status()); ctx.logger.warn(format!("GET {}: Failed to fetch updates from server. Server returned invalid response code: {}",url,response.status()));
return Vec::new(); return Vec::new();
} }
let json = parse_json(&get_response_body(response).await); let json = parse_json(&get_response_body(response).await);
@@ -48,7 +46,7 @@ async fn get_remote_updates(servers: &FxHashMap<String, String>, client: &Client
Vec::new() Vec::new()
} }
Err(e) => { Err(e) => {
warn!("Failed to fetch updates from server. {}", e); ctx.logger.warn(format!("Failed to fetch updates from server. {}", e));
Vec::new() Vec::new()
}, },
} }
@@ -63,12 +61,12 @@ async fn get_remote_updates(servers: &FxHashMap<String, String>, client: &Client
} }
/// Returns a list of updates for all images passed in. /// Returns a list of updates for all images passed in.
pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> Vec<Update> { pub async fn get_updates(references: &Option<Vec<String>>, ctx: &Context) -> Vec<Update> {
let client = Client::new(); let client = Client::new(ctx);
// Get local images // Get local images
debug!(config.debug, "Retrieving images to be checked"); ctx.logger.debug("Retrieving images to be checked");
let mut images = get_images_from_docker_daemon(config, references).await; let mut images = get_images_from_docker_daemon(ctx, references).await;
// Add extra images from references // Add extra images from references
if let Some(refs) = references { if let Some(refs) = references {
@@ -82,18 +80,17 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
} }
// Get remote images from other servers // Get remote images from other servers
let remote_updates = if !config.servers.is_empty() { let remote_updates = if !ctx.config.servers.is_empty() {
debug!(config.debug, "Fetching updates from remote servers"); ctx.logger.debug("Fetching updates from remote servers");
get_remote_updates(&config.servers, &client).await get_remote_updates(ctx, &client).await
} else { } else {
Vec::new() Vec::new()
}; };
debug!( ctx.logger.debug(format!(
config.debug,
"Checking {:?}", "Checking {:?}",
images.iter().map(|image| &image.reference).collect_vec() images.iter().map(|image| &image.reference).collect_vec()
); ));
// Get a list of unique registries our images belong to. We are unwrapping the registry because it's guaranteed to be there. // Get a list of unique registries our images belong to. We are unwrapping the registry because it's guaranteed to be there.
let registries: Vec<&String> = images let registries: Vec<&String> = images
@@ -104,7 +101,7 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
// Create request client. All network requests share the same client for better performance. // Create request client. All network requests share the same client for better performance.
// This client is also configured to retry a failed request up to 3 times with exponential backoff in between. // This client is also configured to retry a failed request up to 3 times with exponential backoff in between.
let client = Client::new(); let client = Client::new(ctx);
// Create a map of images indexed by registry. This solution seems quite inefficient, since each iteration causes a key to be looked up. I can't find anything better at the moment. // Create a map of images indexed by registry. This solution seems quite inefficient, since each iteration causes a key to be looked up. I can't find anything better at the moment.
let mut image_map: FxHashMap<&String, Vec<&Image>> = FxHashMap::default(); let mut image_map: FxHashMap<&String, Vec<&Image>> = FxHashMap::default();
@@ -119,12 +116,12 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
// Retrieve an authentication token (if required) for each registry. // Retrieve an authentication token (if required) for each registry.
let mut tokens: FxHashMap<&str, Option<String>> = FxHashMap::default(); let mut tokens: FxHashMap<&str, Option<String>> = FxHashMap::default();
for registry in registries { for registry in registries {
let credentials = if let Some(registry_config) = config.registries.get(registry) { let credentials = if let Some(registry_config) = ctx.config.registries.get(registry) {
&registry_config.authentication &registry_config.authentication
} else { } else {
&None &None
}; };
match check_auth(registry, config, &client).await { match check_auth(registry, ctx, &client).await {
Some(auth_url) => { Some(auth_url) => {
let token = get_token( let token = get_token(
image_map.get(registry).unwrap(), image_map.get(registry).unwrap(),
@@ -141,9 +138,10 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
} }
} }
debug!(config.debug, "Tokens: {:?}", tokens); ctx.logger.debug(format!("Tokens: {:?}", tokens));
let ignored_registries = config let ignored_registries = ctx
.config
.registries .registries
.iter() .iter()
.filter_map(|(registry, registry_config)| { .filter_map(|(registry, registry_config)| {
@@ -160,14 +158,15 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
// Loop through images check for updates // Loop through images check for updates
for image in &images { for image in &images {
let is_ignored = ignored_registries.contains(&&image.parts.registry) let is_ignored = ignored_registries.contains(&&image.parts.registry)
|| config || ctx
.config
.images .images
.exclude .exclude
.iter() .iter()
.any(|item| image.reference.starts_with(item)); .any(|item| image.reference.starts_with(item));
if !is_ignored { if !is_ignored {
let token = tokens.get(image.parts.registry.as_str()).unwrap(); let token = tokens.get(image.parts.registry.as_str()).unwrap();
let future = image.check(token.as_deref(), config, &client); let future = image.check(token.as_deref(), ctx, &client);
handles.push(future); handles.push(future);
} }
} }

View File

@@ -40,8 +40,6 @@ pub struct ImageConfig {
pub struct Config { pub struct Config {
version: u8, version: u8,
pub agent: bool, pub agent: bool,
#[serde(skip_deserializing)]
pub debug: bool,
pub images: ImageConfig, pub images: ImageConfig,
pub refresh_interval: Option<String>, pub refresh_interval: Option<String>,
pub registries: FxHashMap<String, RegistryConfig>, pub registries: FxHashMap<String, RegistryConfig>,
@@ -55,7 +53,6 @@ impl Config {
Self { Self {
version: 3, version: 3,
agent: false, agent: false,
debug: false,
images: ImageConfig::default(), images: ImageConfig::default(),
refresh_interval: None, refresh_interval: None,
registries: FxHashMap::default(), registries: FxHashMap::default(),

View File

@@ -2,7 +2,7 @@ use bollard::{models::ImageInspect, ClientVersion, Docker};
use futures::future::join_all; use futures::future::join_all;
use crate::{config::Config, error, structs::image::Image}; use crate::{error, structs::image::Image, Context};
fn create_docker_client(socket: Option<&str>) -> Docker { fn create_docker_client(socket: Option<&str>) -> Docker {
let client: Result<Docker, bollard::errors::Error> = match socket { let client: Result<Docker, bollard::errors::Error> = match socket {
@@ -38,10 +38,10 @@ fn create_docker_client(socket: Option<&str>) -> Docker {
/// Retrieves images from Docker daemon. If `references` is Some, return only the images whose references match the ones specified. /// Retrieves images from Docker daemon. If `references` is Some, return only the images whose references match the ones specified.
pub async fn get_images_from_docker_daemon( pub async fn get_images_from_docker_daemon(
config: &Config, ctx: &Context,
references: &Option<Vec<String>>, references: &Option<Vec<String>>,
) -> Vec<Image> { ) -> Vec<Image> {
let client: Docker = create_docker_client(config.socket.as_deref()); let client: Docker = create_docker_client(ctx.config.socket.as_deref());
match references { match references {
Some(refs) => { Some(refs) => {
let mut inspect_handles = Vec::with_capacity(refs.len()); let mut inspect_handles = Vec::with_capacity(refs.len());

View File

@@ -4,7 +4,7 @@ use reqwest::Response;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware}; use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use crate::{error, warn}; use crate::{error, Context};
pub enum RequestMethod { pub enum RequestMethod {
GET, GET,
@@ -23,16 +23,18 @@ impl Display for RequestMethod {
/// A struct for handling HTTP requests. Takes care of the repetitive work of checking for errors, etc and exposes a simple interface /// A struct for handling HTTP requests. Takes care of the repetitive work of checking for errors, etc and exposes a simple interface
pub struct Client { pub struct Client {
inner: ClientWithMiddleware, inner: ClientWithMiddleware,
ctx: Context,
} }
impl Client { impl Client {
pub fn new() -> Self { pub fn new(ctx: &Context) -> Self {
Self { Self {
inner: ClientBuilder::new(reqwest::Client::new()) inner: ClientBuilder::new(reqwest::Client::new())
.with(RetryTransientMiddleware::new_with_policy( .with(RetryTransientMiddleware::new_with_policy(
ExponentialBackoff::builder().build_with_max_retries(3), ExponentialBackoff::builder().build_with_max_retries(3),
)) ))
.build(), .build(),
ctx: ctx.clone(),
} }
} }
@@ -57,14 +59,14 @@ impl Client {
let status = response.status(); let status = response.status();
if status == 404 { if status == 404 {
let message = format!("{} {}: Not found!", method, url); let message = format!("{} {}: Not found!", method, url);
warn!("{}", message); self.ctx.logger.warn(&message);
Err(message) Err(message)
} else if status == 401 { } else if status == 401 {
if ignore_401 { if ignore_401 {
Ok(response) Ok(response)
} else { } else {
let message = format!("{} {}: Unauthorized! Please configure authentication for this registry or if you have already done so, please make sure it is correct.", method, url); let message = format!("{} {}: Unauthorized! Please configure authentication for this registry or if you have already done so, please make sure it is correct.", method, url);
warn!("{}", message); self.ctx.logger.warn(&message);
Err(message) Err(message)
} }
} else if status.as_u16() <= 400 { } else if status.as_u16() <= 400 {
@@ -87,11 +89,11 @@ impl Client {
Err(error) => { Err(error) => {
if error.is_connect() { if error.is_connect() {
let message = format!("{} {}: Connection failed!", method, url); let message = format!("{} {}: Connection failed!", method, url);
warn!("{}", message); self.ctx.logger.warn(&message);
Err(message) Err(message)
} else if error.is_timeout() { } else if error.is_timeout() {
let message = format!("{} {}: Connection timed out!", method, url); let message = format!("{} {}: Connection timed out!", method, url);
warn!("{}", message); self.ctx.logger.warn(&message);
Err(message) Err(message)
} else { } else {
error!( error!(
@@ -123,9 +125,3 @@ impl Client {
self.request(url, RequestMethod::HEAD, headers, false).await self.request(url, RequestMethod::HEAD, headers, false).await
} }
} }
impl Default for Client {
fn default() -> Self {
Self::new()
}
}

42
src/logging.rs Normal file
View File

@@ -0,0 +1,42 @@
#[macro_export]
macro_rules! error {
($($arg:tt)*) => ({
eprintln!("\x1b[31;1mERROR\x1b[0m {}", format!($($arg)*));
std::process::exit(1);
})
}
/// This struct mostly exists so we can print stuff without passing debug or raw every time.
#[derive(Clone)]
pub struct Logger {
debug: bool,
raw: bool,
}
impl Logger {
pub fn new(debug: bool, raw: bool) -> Self {
Self { debug, raw }
}
pub fn warn(&self, msg: impl AsRef<str>) {
if !self.raw {
eprintln!("\x1b[33;1m WARN\x1b[0m {}", msg.as_ref());
}
}
pub fn info(&self, msg: impl AsRef<str>) {
if !self.raw {
println!("\x1b[36;1m INFO\x1b[0m {}", msg.as_ref());
}
}
pub fn debug(&self, msg: impl AsRef<str>) {
if self.debug {
println!("\x1b[35;1mDEBUG\x1b[0m {}", msg.as_ref());
}
}
pub fn set_raw(&mut self, raw: bool) {
self.raw = raw
}
}

View File

@@ -4,6 +4,7 @@ use config::Config;
use formatting::spinner::Spinner; use formatting::spinner::Spinner;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
use formatting::{print_raw_updates, print_updates}; use formatting::{print_raw_updates, print_updates};
use logging::Logger;
#[cfg(feature = "server")] #[cfg(feature = "server")]
use server::serve; use server::serve;
use std::path::PathBuf; use std::path::PathBuf;
@@ -15,6 +16,7 @@ pub mod docker;
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
pub mod formatting; pub mod formatting;
pub mod http; pub mod http;
pub mod logging;
pub mod registry; pub mod registry;
#[cfg(feature = "server")] #[cfg(feature = "server")]
pub mod server; pub mod server;
@@ -62,6 +64,12 @@ enum Commands {
}, },
} }
#[derive(Clone)]
pub struct Context {
pub config: Config,
pub logger: Logger,
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let cli = Cli::parse(); let cli = Cli::parse();
@@ -73,7 +81,10 @@ async fn main() {
if let Some(socket) = cli.socket { if let Some(socket) = cli.socket {
config.socket = Some(socket) config.socket = Some(socket)
} }
config.debug = cli.debug; let mut ctx = Context {
config,
logger: Logger::new(cli.debug, false),
};
match &cli.command { match &cli.command {
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
Some(Commands::Check { Some(Commands::Check {
@@ -82,23 +93,26 @@ async fn main() {
raw, raw,
}) => { }) => {
let start = SystemTime::now(); let start = SystemTime::now();
match *raw || config.debug { if *raw {
ctx.logger.set_raw(true);
}
match *raw || cli.debug {
true => { true => {
let updates = get_updates(references, &config).await; let updates = get_updates(references, &ctx).await;
print_raw_updates(&updates); print_raw_updates(&updates);
} }
false => { false => {
let spinner = Spinner::new(); let spinner = Spinner::new();
let updates = get_updates(references, &config).await; let updates = get_updates(references, &ctx).await;
spinner.succeed(); spinner.succeed();
print_updates(&updates, icons); print_updates(&updates, icons);
info!("✨ Checked {} images in {}ms", updates.len(), start.elapsed().unwrap().as_millis()); ctx.logger.info(format!("✨ Checked {} images in {}ms", updates.len(), start.elapsed().unwrap().as_millis()));
} }
}; };
} }
#[cfg(feature = "server")] #[cfg(feature = "server")]
Some(Commands::Serve { port }) => { Some(Commands::Serve { port }) => {
let _ = serve(port, &config).await; let _ = serve(port, &ctx).await;
} }
None => error!("Whoops! It looks like you haven't specified a command to run! Try `cup help` to see available options."), None => error!("Whoops! It looks like you haven't specified a command to run! Try `cup help` to see available options."),
} }

View File

@@ -3,8 +3,7 @@ use std::time::SystemTime;
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
config::Config, error,
debug, error,
http::Client, http::Client,
structs::{ structs::{
image::{DigestInfo, Image, VersionInfo}, image::{DigestInfo, Image, VersionInfo},
@@ -17,10 +16,11 @@ use crate::{
}, },
time::{elapsed, now}, time::{elapsed, now},
}, },
Context,
}; };
pub async fn check_auth(registry: &str, config: &Config, client: &Client) -> Option<String> { pub async fn check_auth(registry: &str, ctx: &Context, client: &Client) -> Option<String> {
let protocol = get_protocol(registry, &config.registries); let protocol = get_protocol(registry, &ctx.config.registries);
let url = format!("{}://{}/v2/", protocol, registry); let url = format!("{}://{}/v2/", protocol, registry);
let response = client.get(&url, Vec::new(), true).await; let response = client.get(&url, Vec::new(), true).await;
match response { match response {
@@ -45,15 +45,13 @@ pub async fn check_auth(registry: &str, config: &Config, client: &Client) -> Opt
pub async fn get_latest_digest( pub async fn get_latest_digest(
image: &Image, image: &Image,
token: Option<&str>, token: Option<&str>,
config: &Config, ctx: &Context,
client: &Client, client: &Client,
) -> Image { ) -> Image {
debug!( ctx.logger
config.debug, .debug(format!("Checking for digest update to {}", image.reference));
"Checking for digest update to {}", image.reference
);
let start = SystemTime::now(); let start = SystemTime::now();
let protocol = get_protocol(&image.parts.registry, &config.registries); let protocol = get_protocol(&image.parts.registry, &ctx.config.registries);
let url = format!( let url = format!(
"{}://{}/v2/{}/manifests/{}", "{}://{}/v2/{}/manifests/{}",
protocol, &image.parts.registry, &image.parts.repository, &image.parts.tag protocol, &image.parts.registry, &image.parts.repository, &image.parts.tag
@@ -63,10 +61,10 @@ pub async fn get_latest_digest(
let response = client.head(&url, headers).await; let response = client.head(&url, headers).await;
let time = start.elapsed().unwrap().as_millis() as u32; let time = start.elapsed().unwrap().as_millis() as u32;
debug!( ctx.logger.debug(format!(
config.debug, "Checked for digest update to {} in {}ms",
"Checked for digest update to {} in {}ms", image.reference, time image.reference, time
); ));
match response { match response {
Ok(res) => match res.headers().get("docker-content-digest") { Ok(res) => match res.headers().get("docker-content-digest") {
Some(digest) => { Some(digest) => {
@@ -121,15 +119,13 @@ pub async fn get_latest_tag(
image: &Image, image: &Image,
base: &Version, base: &Version,
token: Option<&str>, token: Option<&str>,
config: &Config, ctx: &Context,
client: &Client, client: &Client,
) -> Image { ) -> Image {
debug!( ctx.logger
config.debug, .debug(format!("Checking for tag update to {}", image.reference));
"Checking for tag update to {}", image.reference
);
let start = now(); let start = now();
let protocol = get_protocol(&image.parts.registry, &config.registries); let protocol = get_protocol(&image.parts.registry, &ctx.config.registries);
let url = format!( let url = format!(
"{}://{}/v2/{}/tags/list", "{}://{}/v2/{}/tags/list",
protocol, &image.parts.registry, &image.parts.repository, protocol, &image.parts.registry, &image.parts.repository,
@@ -144,12 +140,11 @@ pub async fn get_latest_tag(
let mut next_url = Some(url); let mut next_url = Some(url);
while next_url.is_some() { while next_url.is_some() {
debug!( ctx.logger.debug(format!(
config.debug,
"{} has extra tags! Current number of valid tags: {}", "{} has extra tags! Current number of valid tags: {}",
image.reference, image.reference,
tags.len() tags.len()
); ));
let (new_tags, next) = match get_extra_tags( let (new_tags, next) = match get_extra_tags(
&next_url.unwrap(), &next_url.unwrap(),
headers.clone(), headers.clone(),
@@ -172,20 +167,19 @@ pub async fn get_latest_tag(
next_url = next; next_url = next;
} }
let tag = tags.iter().max(); let tag = tags.iter().max();
debug!( ctx.logger.debug(format!(
config.debug,
"Checked for tag update to {} in {}ms", "Checked for tag update to {} in {}ms",
image.reference, image.reference,
elapsed(start) elapsed(start)
); ));
match tag { match tag {
Some(t) => { Some(t) => {
if t == base && image.digest_info.is_some() { if t == base && image.digest_info.is_some() {
// Tags are equal so we'll compare digests // Tags are equal so we'll compare digests
debug!( ctx.logger.debug(format!(
config.debug, "Tags for {} are equal, comparing digests.",
"Tags for {} are equal, comparing digests.", image.reference image.reference
); ));
get_latest_digest( get_latest_digest(
&Image { &Image {
version_info: Some(VersionInfo { version_info: Some(VersionInfo {
@@ -196,7 +190,7 @@ pub async fn get_latest_tag(
..image.clone() ..image.clone()
}, },
token, token,
config, ctx,
client, client,
) )
.await .await

View File

@@ -18,14 +18,14 @@ use xitca_web::{
use crate::{ use crate::{
check::get_updates, check::get_updates,
config::{Config, Theme}, config::Theme,
info,
structs::update::Update, structs::update::Update,
utils::{ utils::{
json::{to_full_json, to_simple_json}, json::{to_full_json, to_simple_json},
sort_update_vec::sort_update_vec, sort_update_vec::sort_update_vec,
time::{elapsed, now}, time::{elapsed, now},
}, },
Context,
}; };
const HTML: &str = include_str!("static/index.html"); const HTML: &str = include_str!("static/index.html");
@@ -46,13 +46,13 @@ const SORT_ORDER: [&str; 8] = [
"unknown", "unknown",
]; // For Liquid rendering ]; // For Liquid rendering
pub async fn serve(port: &u16, config: &Config) -> std::io::Result<()> { pub async fn serve(port: &u16, ctx: &Context) -> std::io::Result<()> {
info!("Starting server, please wait..."); ctx.logger.info("Starting server, please wait...");
let data = ServerData::new(config).await; let data = ServerData::new(ctx).await;
let scheduler = JobScheduler::new().await.unwrap(); let scheduler = JobScheduler::new().await.unwrap();
let data = Arc::new(Mutex::new(data)); let data = Arc::new(Mutex::new(data));
let data_copy = data.clone(); let data_copy = data.clone();
if let Some(interval) = &config.refresh_interval { if let Some(interval) = &ctx.config.refresh_interval {
scheduler scheduler
.add( .add(
Job::new_async(interval, move |_uuid, _lock| { Job::new_async(interval, move |_uuid, _lock| {
@@ -67,14 +67,14 @@ pub async fn serve(port: &u16, config: &Config) -> std::io::Result<()> {
.unwrap(); .unwrap();
} }
scheduler.start().await.unwrap(); scheduler.start().await.unwrap();
info!("Ready to start!"); ctx.logger.info("Ready to start!");
let mut app_builder = App::new() let mut app_builder = App::new()
.with_state(data) .with_state(data)
.at("/api/v2/json", get(handler_service(api_simple))) .at("/api/v2/json", get(handler_service(api_simple)))
.at("/api/v3/json", get(handler_service(api_full))) .at("/api/v3/json", get(handler_service(api_full)))
.at("/api/v2/refresh", get(handler_service(refresh))) .at("/api/v2/refresh", get(handler_service(refresh)))
.at("/api/v3/refresh", get(handler_service(refresh))); .at("/api/v3/refresh", get(handler_service(refresh)));
if !config.agent { if !ctx.config.agent {
app_builder = app_builder app_builder = app_builder
.at("/", get(handler_service(_static))) .at("/", get(handler_service(_static)))
.at("/*", get(handler_service(_static))); .at("/*", get(handler_service(_static)));
@@ -151,14 +151,14 @@ struct ServerData {
raw_updates: Vec<Update>, raw_updates: Vec<Update>,
simple_json: Value, simple_json: Value,
full_json: Value, full_json: Value,
config: Config, ctx: Context,
theme: &'static str, theme: &'static str,
} }
impl ServerData { impl ServerData {
async fn new(config: &Config) -> Self { async fn new(ctx: &Context) -> Self {
let mut s = Self { let mut s = Self {
config: config.clone(), ctx: ctx.clone(),
template: String::new(), template: String::new(),
simple_json: Value::Null, simple_json: Value::Null,
full_json: Value::Null, full_json: Value::Null,
@@ -171,14 +171,14 @@ impl ServerData {
async fn refresh(&mut self) { async fn refresh(&mut self) {
let start = now(); let start = now();
if !self.raw_updates.is_empty() { if !self.raw_updates.is_empty() {
info!("Refreshing data"); self.ctx.logger.info("Refreshing data");
} }
let updates = sort_update_vec(&get_updates(&None, &self.config).await); let updates = sort_update_vec(&get_updates(&None, &self.ctx).await);
info!( self.ctx.logger.info(format!(
"✨ Checked {} images in {}ms", "✨ Checked {} images in {}ms",
updates.len(), updates.len(),
elapsed(start) elapsed(start)
); ));
self.raw_updates = updates; self.raw_updates = updates;
let template = liquid::ParserBuilder::with_stdlib() let template = liquid::ParserBuilder::with_stdlib()
.build() .build()
@@ -193,7 +193,7 @@ impl ServerData {
.to_string() .to_string()
.into(); .into();
self.full_json["last_updated"] = self.simple_json["last_updated"].clone(); self.full_json["last_updated"] = self.simple_json["last_updated"].clone();
self.theme = match &self.config.theme { self.theme = match &self.ctx.config.theme {
Theme::Default => "neutral", Theme::Default => "neutral",
Theme::Blue => "gray", Theme::Blue => "gray",
}; };

View File

@@ -1,10 +1,10 @@
use crate::{ use crate::{
config::Config,
error, error,
http::Client, http::Client,
registry::{get_latest_digest, get_latest_tag}, registry::{get_latest_digest, get_latest_tag},
structs::{status::Status, version::Version}, structs::{status::Status, version::Version},
utils::reference::split, utils::reference::split,
Context,
}; };
use super::{ use super::{
@@ -168,8 +168,20 @@ impl Image {
.replacen("{}", &new_tag.minor.unwrap_or(0).to_string(), 1) .replacen("{}", &new_tag.minor.unwrap_or(0).to_string(), 1)
.replacen("{}", &new_tag.patch.unwrap_or(0).to_string(), 1), .replacen("{}", &new_tag.patch.unwrap_or(0).to_string(), 1),
// Throwing these in, because they're useful for the CLI output, however we won't (de)serialize them // Throwing these in, because they're useful for the CLI output, however we won't (de)serialize them
current_version: self.version_info.as_ref().unwrap().current_tag.to_string(), current_version: self
new_version: self.version_info.as_ref().unwrap().latest_remote_tag.as_ref().unwrap().to_string() .version_info
.as_ref()
.unwrap()
.current_tag
.to_string(),
new_version: self
.version_info
.as_ref()
.unwrap()
.latest_remote_tag
.as_ref()
.unwrap()
.to_string(),
}) })
} }
"digest" => { "digest" => {
@@ -185,7 +197,7 @@ impl Image {
}) })
} }
"none" => UpdateInfo::None, "none" => UpdateInfo::None,
_ => unreachable!() _ => unreachable!(),
}, },
}, },
error: self.error.clone(), error: self.error.clone(),
@@ -197,11 +209,11 @@ impl Image {
} }
/// Checks if the image has an update /// Checks if the image has an update
pub async fn check(&self, token: Option<&str>, config: &Config, client: &Client) -> Self { pub async fn check(&self, token: Option<&str>, ctx: &Context, client: &Client) -> Self {
match &self.version_info { match &self.version_info {
Some(data) => get_latest_tag(self, &data.current_tag, token, config, client).await, Some(data) => get_latest_tag(self, &data.current_tag, token, ctx, client).await,
None => match self.digest_info { None => match self.digest_info {
Some(_) => get_latest_digest(self, token, config, client).await, Some(_) => get_latest_digest(self, token, ctx, client).await,
None => unreachable!(), None => unreachable!(),
}, },
} }

View File

@@ -1,34 +0,0 @@
// Logging utilites
/// This macro is an alternative to panic. It prints the message you give it and exits the process with code 1, without printing a stack trace. Useful for when the program has to exit due to a user error or something unexpected which is unrelated to the program (e.g. a failed web request)
#[macro_export]
macro_rules! error {
($($arg:tt)*) => ({
eprintln!("\x1b[31;1mERROR\x1b[0m {}", format!($($arg)*));
std::process::exit(1);
})
}
// A small macro to print in yellow as a warning
#[macro_export]
macro_rules! warn {
($($arg:tt)*) => ({
eprintln!("\x1b[33;1mWARN \x1b[0m {}", format!($($arg)*));
})
}
#[macro_export]
macro_rules! info {
($($arg:tt)*) => ({
println!("\x1b[36;1mINFO \x1b[0m {}", format!($($arg)*));
})
}
#[macro_export]
macro_rules! debug {
($debg:expr, $($arg:tt)*) => ({
if $debg {
println!("\x1b[35;1mDEBUG\x1b[0m {}", format!($($arg)*));
}
})
}

View File

@@ -1,6 +1,5 @@
pub mod json; pub mod json;
pub mod link; pub mod link;
pub mod logging;
pub mod reference; pub mod reference;
pub mod request; pub mod request;
pub mod sort_update_vec; pub mod sort_update_vec;