use reqwest; use serde::Deserialize; use std::collections::HashMap; use url::Url; pub struct Registry<'a> { pub url: &'a str, } #[allow(dead_code)] #[derive(Debug, Deserialize)] pub struct RepositoryList { pub repositories: Vec, } #[allow(dead_code)] #[derive(Debug, Deserialize)] pub struct RepositoryTags { pub name: String, pub tags: Option>, } #[allow(dead_code, non_snake_case)] #[derive(Debug, Deserialize)] pub struct RepositoryTagManifest { pub name: String, pub tag: String, pub history: Vec>, #[serde(skip)] pub ts: i64, #[serde(skip)] pub digest: String, } #[derive(Debug, Deserialize)] struct History { created: Option, } impl<'a> Registry<'a> { pub async fn get_repositories(&self) -> Result> { let registry_url = Url::parse(self.url).unwrap(); let api_url = registry_url.join("/v2/_catalog").unwrap(); let resp = reqwest::get(api_url) .await? .json::() .await?; Ok(resp) } pub async fn get_repository_tags( &self, repo: &str, ) -> Result> { let registry_url = Url::parse(self.url).unwrap(); let api_url = registry_url .join(format!("/v2/{}/tags/list", repo).as_str()) .unwrap(); let resp = reqwest::get(api_url).await?; let data = resp.json::().await?; Ok(data) } pub async fn get_repository_tag_manifest( &self, repo: &str, tag: &str, ) -> Result> { let registry_url = Url::parse(self.url).unwrap(); let api_url = registry_url .join(format!("/v2/{}/manifests/{}", repo, tag).as_str()) .unwrap(); let resp = reqwest::get(api_url.clone()).await?; // let digest = resp // .headers() // .get("Docker-Content-Digest") // .unwrap() // .to_str() // .unwrap() // .to_string(); let digest = reqwest::Client::new() .get(api_url.clone()) .header( reqwest::header::ACCEPT, "application/vnd.docker.distribution.manifest.v2+json", ) .send() .await? .headers() .get("Docker-Content-Digest") .unwrap() .to_str() .unwrap() .to_string(); let mut data = resp.json::().await?; let ts = data.history.iter().fold(0, |acc, d| { match d.get("v1Compatibility") { None => acc, Some(s) => { // println!("{}", (*s)) let h: History = serde_json::from_str(s).unwrap_or(History { created: None }); match h.created { None => acc, Some(time_str) => { let result = chrono::DateTime::parse_from_rfc3339(time_str.as_str()); match result { Ok(t) => { let tt = t.timestamp(); if acc > tt { acc } else { tt } } Err(_) => acc, } } } } } }); data.history.clear(); data.ts = ts; data.digest = digest; Ok(data) } pub async fn delete_repository_tag( &self, repo: &str, digest: &str, ) -> Result<(), Box> { let registry_url = Url::parse(self.url).unwrap(); let api_url = registry_url .join(format!("/v2/{}/manifests/{}", repo, digest).as_str()) .unwrap(); // println!("url:: {}", api_url); let client = reqwest::Client::new(); client.delete(api_url).send().await?; // println!("Status : {}", resp.status()); // if resp.status().as_u16() >= 400 { // } Ok(()) } }