update
This commit is contained in:
parent
8c4096882c
commit
a8d59e48d1
1077
Cargo.lock
generated
Normal file
1077
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,3 +6,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
crossterm = "0.22.1"
|
||||||
|
reqwest = { version = "0.11", features = ["json", "blocking"] }
|
||||||
|
serde = {version = "1.0.136", features = ["derive"]}
|
||||||
|
serde_json = "1.0.78"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
tui = "0.17.0"
|
||||||
|
|
||||||
|
28
src/main.rs
28
src/main.rs
@ -1,3 +1,31 @@
|
|||||||
|
mod registry;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
|
||||||
|
let r = registry::api::Registry {
|
||||||
|
endpoint: "https://docker.mtfos.xyz".to_string(),
|
||||||
|
user: "".to_string(),
|
||||||
|
password: "".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// let images = r.list_images().unwrap();
|
||||||
|
|
||||||
|
// println!("images ::: {:?}", images);
|
||||||
|
|
||||||
|
// let tags = r.get_image_tags("mtfos/go-bot").unwrap();
|
||||||
|
|
||||||
|
// println!("tags ::: {:?}", tags);
|
||||||
|
|
||||||
|
let digest = match r.get_image_tag_manifest("demo/node-hello", "v1") {
|
||||||
|
Ok(digest) => digest,
|
||||||
|
Err(e) => panic!("digest err ::: {:?}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("digest ::: {:?}", digest);
|
||||||
|
|
||||||
|
match r.delete_image_tag("demo/node-hello", &digest) {
|
||||||
|
Ok(_) => println!("deleted"),
|
||||||
|
Err(e) => println!("error ::: {:?}", e),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
154
src/registry/api.rs
Normal file
154
src/registry/api.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
1
src/registry/mod.rs
Normal file
1
src/registry/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod api;
|
Loading…
Reference in New Issue
Block a user