registry-cli/src/registry/api.rs

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(())
}
}