155 lines
3.8 KiB
Rust
155 lines
3.8 KiB
Rust
use reqwest;
|
|
use serde::Deserialize;
|
|
// use serde_json;
|
|
|
|
#[warn(dead_code)]
|
|
pub struct Registry {
|
|
pub endpoint: String,
|
|
pub user: String,
|
|
pub password: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct RegistryRepository {
|
|
repositories: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct ImageTags {
|
|
pub name: String,
|
|
pub tags: Vec<String>,
|
|
}
|
|
|
|
impl Registry {
|
|
fn get_request(&self, method: &str, path: &str) -> reqwest::blocking::RequestBuilder {
|
|
let client = reqwest::blocking::Client::new();
|
|
let url = format!("{}{}", self.endpoint, path);
|
|
|
|
let mut m = match method {
|
|
"GET" => client.get(&url),
|
|
"POST" => client.post(&url),
|
|
"PUT" => client.put(&url),
|
|
"DELETE" => client.delete(&url),
|
|
_ => panic!("Unsupported method"),
|
|
};
|
|
|
|
if (self.user.len() > 0) && (self.password.len() > 0) {
|
|
m = m.basic_auth(&self.user, Some(&self.password));
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
pub fn list_images(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
|
let url = "/v2/_catalog";
|
|
let builder = self.get_request("GET", &url);
|
|
let resp = builder.send()?;
|
|
|
|
let headers = resp.headers().to_owned();
|
|
|
|
if !headers
|
|
.get("Content-Type")
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.starts_with("application/json")
|
|
{
|
|
return Err(Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"Invalid Content-Type",
|
|
)));
|
|
}
|
|
|
|
let data = resp.json::<RegistryRepository>()?;
|
|
|
|
Ok(data.repositories)
|
|
}
|
|
|
|
pub fn get_image_tags(
|
|
&self,
|
|
image_name: &str,
|
|
) -> Result<ImageTags, Box<dyn std::error::Error>> {
|
|
let url = format!("/v2/{}/tags/list", image_name);
|
|
|
|
let builder = self.get_request("GET", &url);
|
|
|
|
let resp = builder.send()?;
|
|
|
|
let headers = resp.headers().to_owned();
|
|
|
|
// println!("\n{:?}\n", headers);
|
|
|
|
if !headers
|
|
.get("Content-Type")
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.starts_with("application/json")
|
|
{
|
|
return Err(Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"Invalid Content-Type",
|
|
)));
|
|
}
|
|
|
|
let data = resp.json::<ImageTags>()?;
|
|
|
|
Ok(data)
|
|
}
|
|
|
|
pub fn get_image_tag_manifest(
|
|
&self,
|
|
image_name: &str,
|
|
tag: &str,
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
let url = format!("/v2/{}/manifests/{}", image_name, tag);
|
|
|
|
let mut builder = self.get_request("GET", &url);
|
|
|
|
builder = builder.header(
|
|
reqwest::header::ACCEPT,
|
|
"application/vnd.docker.distribution.manifest.v2+json",
|
|
);
|
|
let resp = builder.send()?;
|
|
|
|
if !resp.status().is_success() {
|
|
return Err(Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"request failed",
|
|
)));
|
|
}
|
|
|
|
let headers = resp.headers().to_owned();
|
|
|
|
let digest = headers
|
|
.get("Docker-Content-Digest")
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap()
|
|
.to_string();
|
|
|
|
Ok(digest)
|
|
}
|
|
|
|
pub fn delete_image_tag(
|
|
&self,
|
|
image_name: &str,
|
|
digest: &str,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let url = format!("/v2/{}/manifests/{}", image_name, digest);
|
|
|
|
let builder = self.get_request("DELETE", &url);
|
|
|
|
let resp = builder.send()?;
|
|
|
|
if !resp.status().is_success() {
|
|
return Err(Box::new(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"request failed",
|
|
)));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|