mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-15 08:33:49 -05:00
Change config, add schema
This commit is contained in:
61
cup.schema.json
Normal file
61
cup.schema.json
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://raw.githubusercontent.com/sergi0g/cup/main/cup.schema.json",
|
||||
"title": "Cup",
|
||||
"description": "A schema for Cup's config file",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"registries": {
|
||||
"type": "object",
|
||||
"description": "Configuration options for specific registries",
|
||||
"additionalProperties": {
|
||||
"authentication": {
|
||||
"description": "An authentication token provided by the registry",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"insecure": {
|
||||
"description": "Whether Cup should connect to the registry insecurely (HTTP) or not. Enable this only if you really need to.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"ignore": {
|
||||
"description": "Whether or not the registry should be ignored when running Cup",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"images": {
|
||||
"type": "object",
|
||||
"description": "Configuration options for specific images",
|
||||
"properties": {
|
||||
"extra": {
|
||||
"type": "array",
|
||||
"description": "Extra image references you want Cup to check",
|
||||
"minItems": 1
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "Image references that should be excluded from the check",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"description": "The theme used by the web UI",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"blue"
|
||||
]
|
||||
},
|
||||
"socket": {
|
||||
"description": "The path to the unix socket you would like Cup to use for communication with the Docker daemon. Useful if you're trying to use Cup with Podman.",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/check.rs
28
src/check.rs
@@ -63,7 +63,11 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
|
||||
// Retrieve an authentication token (if required) for each registry.
|
||||
let mut tokens: FxHashMap<&str, Option<String>> = FxHashMap::default();
|
||||
for registry in registries {
|
||||
let credentials = config.authentication.get(registry);
|
||||
let credentials = if let Some(registry_config) = config.registries.get(registry) {
|
||||
®istry_config.authentication
|
||||
} else {
|
||||
&None
|
||||
};
|
||||
match check_auth(registry, config, &client).await {
|
||||
Some(auth_url) => {
|
||||
let token = get_token(
|
||||
@@ -85,11 +89,27 @@ pub async fn get_updates(references: &Option<Vec<String>>, config: &Config) -> V
|
||||
|
||||
// Create a Vec to store futures so we can await them all at once.
|
||||
let mut handles = Vec::with_capacity(images.len());
|
||||
|
||||
let ignored_registries = config
|
||||
.registries
|
||||
.iter()
|
||||
.filter_map(|(registry, registry_config)| {
|
||||
if registry_config.ignore {
|
||||
Some(registry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<&String>>();
|
||||
|
||||
// Loop through images and get the latest digest for each
|
||||
for image in &images {
|
||||
let token = tokens.get(image.registry.as_str()).unwrap();
|
||||
let future = image.check(token.as_ref(), config, &client);
|
||||
handles.push(future);
|
||||
let is_ignored = ignored_registries.contains(&&image.registry) || config.images.exclude.iter().any(|item| image.reference.starts_with(item));
|
||||
if !is_ignored {
|
||||
let token = tokens.get(image.registry.as_str()).unwrap();
|
||||
let future = image.check(token.as_ref(), config, &client);
|
||||
handles.push(future);
|
||||
}
|
||||
}
|
||||
// Await all the futures
|
||||
join_all(handles).await
|
||||
|
||||
@@ -19,32 +19,44 @@ impl Default for Theme {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(default)]
|
||||
pub struct RegistryConfig {
|
||||
pub authentication: Option<String>,
|
||||
pub insecure: bool,
|
||||
pub ignore: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct ImageConfig {
|
||||
pub extra: Vec<String>,
|
||||
pub exclude: Vec<String>
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct Config {
|
||||
#[serde(default = "FxHashMap::default")]
|
||||
pub authentication: FxHashMap<String, String>,
|
||||
#[serde(default = "Theme::default")]
|
||||
pub registries: FxHashMap<String, RegistryConfig>,
|
||||
pub images: ImageConfig,
|
||||
pub theme: Theme,
|
||||
#[serde(default = "Vec::default")]
|
||||
pub insecure_registries: Vec<String>,
|
||||
pub socket: Option<String>,
|
||||
#[serde(skip_deserializing)]
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// A stupid new function that exists just so calling `load` doesn't require a self argument
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
authentication: FxHashMap::default(),
|
||||
registries: FxHashMap::default(),
|
||||
images: ImageConfig::default(),
|
||||
theme: Theme::Default,
|
||||
insecure_registries: Vec::with_capacity(0),
|
||||
socket: None,
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the config from the file path provided and returns the parsed result.
|
||||
pub fn load(&self, path: Option<PathBuf>) -> Self {
|
||||
let raw_config = match &path {
|
||||
@@ -67,3 +79,9 @@ impl Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub async fn check_auth(registry: &str, config: &Config, client: &Client) -> Option<String> {
|
||||
let protocol = get_protocol(®istry.to_string(), &config.insecure_registries);
|
||||
let protocol = get_protocol(registry, &config.registries);
|
||||
let url = format!("{}://{}/v2/", protocol, registry);
|
||||
let response = client.get(&url, Vec::new(), true).await;
|
||||
match response {
|
||||
@@ -51,7 +51,7 @@ pub async fn get_latest_digest(
|
||||
"Checking for digest update to {}", image.reference
|
||||
);
|
||||
let start = timestamp();
|
||||
let protocol = get_protocol(&image.registry, &config.insecure_registries);
|
||||
let protocol = get_protocol(&image.registry, &config.registries);
|
||||
let url = format!(
|
||||
"{}://{}/v2/{}/manifests/{}",
|
||||
protocol, &image.registry, &image.repository, &image.tag
|
||||
@@ -97,7 +97,7 @@ pub async fn get_latest_digest(
|
||||
pub async fn get_token(
|
||||
images: &Vec<&Image>,
|
||||
auth_url: &str,
|
||||
credentials: &Option<&String>,
|
||||
credentials: &Option<String>,
|
||||
client: &Client,
|
||||
) -> String {
|
||||
let mut url = auth_url.to_owned();
|
||||
@@ -127,7 +127,7 @@ pub async fn get_latest_tag(
|
||||
"Checking for tag update to {}", image.reference
|
||||
);
|
||||
let start = timestamp();
|
||||
let protocol = get_protocol(&image.registry, &config.insecure_registries);
|
||||
let protocol = get_protocol(&image.registry, &config.registries);
|
||||
let url = format!(
|
||||
"{}://{}/v2/{}/tags/list",
|
||||
protocol, &image.registry, &image.repository,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use http_auth::parse_challenges;
|
||||
use json::JsonValue;
|
||||
use reqwest::Response;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::error;
|
||||
use crate::{config::RegistryConfig, error};
|
||||
|
||||
/// Parses the www-authenticate header the registry sends into a challenge URL
|
||||
pub fn parse_www_authenticate(www_auth: &str) -> String {
|
||||
@@ -23,13 +24,20 @@ pub fn parse_www_authenticate(www_auth: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_protocol(registry: &String, insecure_registries: &[String]) -> String {
|
||||
if insecure_registries.contains(registry) {
|
||||
"http"
|
||||
} else {
|
||||
"https"
|
||||
pub fn get_protocol(
|
||||
registry: &str,
|
||||
registry_config: &FxHashMap<String, RegistryConfig>,
|
||||
) -> &'static str {
|
||||
match registry_config.get(registry) {
|
||||
Some(config) => {
|
||||
if config.insecure {
|
||||
"http"
|
||||
} else {
|
||||
"https"
|
||||
}
|
||||
},
|
||||
None => "https"
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn to_bearer_string(token: &Option<&String>) -> Option<String> {
|
||||
|
||||
Reference in New Issue
Block a user