162 lines
3.7 KiB
Rust
162 lines
3.7 KiB
Rust
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<String>,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct RepositoryTags {
|
|
pub name: String,
|
|
pub tags: Option<Vec<String>>,
|
|
}
|
|
|
|
#[allow(dead_code, non_snake_case)]
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct RepositoryTagManifest {
|
|
pub name: String,
|
|
pub tag: String,
|
|
pub history: Vec<HashMap<String, String>>,
|
|
#[serde(skip)]
|
|
pub ts: i64,
|
|
#[serde(skip)]
|
|
pub digest: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct History {
|
|
created: Option<String>,
|
|
}
|
|
|
|
|
|
impl<'a> Registry<'a> {
|
|
pub async fn get_repositories(&self) -> Result<RepositoryList, Box<dyn std::error::Error>> {
|
|
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::<RepositoryList>()
|
|
.await?;
|
|
|
|
Ok(resp)
|
|
}
|
|
|
|
pub async fn get_repository_tags(
|
|
&self,
|
|
repo: &str,
|
|
) -> Result<RepositoryTags, Box<dyn std::error::Error>> {
|
|
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::<RepositoryTags>().await?;
|
|
Ok(data)
|
|
}
|
|
|
|
pub async fn get_repository_tag_manifest(
|
|
&self,
|
|
repo: &str,
|
|
tag: &str,
|
|
) -> Result<RepositoryTagManifest, Box<dyn std::error::Error>> {
|
|
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::<RepositoryTagManifest>().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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|
|
}
|