Page MenuHomeDevCentral

D1871.id4727.diff
No OneTemporary

D1871.id4727.diff

diff --git a/Cargo.toml b/Cargo.toml
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,8 +6,10 @@
[dependencies]
env_logger = "^0.5.13"
+lazy_static = "1.1.0"
limiting-factor = { path = "../limiting-factor", features = ["minimal"] }
log = "^0.4.5"
+regex = "1.0.5"
rocket = "^0.3.16"
rocket_codegen = "^0.3.16"
rocket_contrib = { version = "*", features = ["json"] }
diff --git a/src/app.rs b/src/app.rs
--- a/src/app.rs
+++ b/src/app.rs
@@ -9,6 +9,7 @@
status,
favicon,
get_registry_stats,
+ get_repository_info,
];
MinimalApplication::start_application(routes);
diff --git a/src/lib.rs b/src/lib.rs
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,6 +5,8 @@
extern crate log;
extern crate rocket_contrib;
+extern crate rocket;
+extern crate limiting_factor;
pub mod app;
pub mod registry;
diff --git a/src/registry.rs b/src/registry.rs
--- a/src/registry.rs
+++ b/src/registry.rs
@@ -1,6 +1,8 @@
+use lazy_static::lazy_static;
+use regex::Regex;
use serde_derive::{Deserialize, Serialize};
-use std::fs::{DirEntry, read_dir};
-use std::io::Result as IOResult;
+use std::fs::{DirEntry, File, read_dir};
+use std::io::{Read, Result as IOResult};
use std::path::Path;
/* -------------------------------------------------------------
@@ -9,7 +11,7 @@
/// Represents a Docker registry
#[serde(rename_all = "camelCase")]
-#[derive(Serialize, Deserialize)]
+#[derive(Serialize, Deserialize, Clone)]
pub struct Registry {
#[serde(skip_serializing)]
pub directory: String,
@@ -40,7 +42,7 @@
}
pub fn count_repositories (&self) -> i32 {
- let path_name = format!("{}/docker/registry/v2/repositories", self.directory);
+ let path_name = self.get_repositories_path();
let path = Path::new(&path_name);
if path.exists() && path.is_dir() {
@@ -60,6 +62,116 @@
0
}
}
+
+ pub fn get_repositories_path (&self) -> String {
+ format!("{}/docker/registry/v2/repositories", self.directory)
+ }
+
+ pub fn get_repository (&self, repository_name: &str) -> Option<Repository> {
+ if !Repository::is_valid_name(repository_name) {
+ return None
+ }
+
+ let path = Path::new(&self.get_repositories_path()).join(repository_name);
+
+ let directory = match path.as_path().to_str() {
+ Some(name) => String::from(name),
+ None => { return None; }
+ };
+
+ let mut repository = Repository {
+ directory,
+ name: String::from(repository_name),
+ tags: Vec::new(),
+ };
+
+ repository.update_tags();
+
+ Some(repository)
+ }
+}
+
+/* -------------------------------------------------------------
+ Repository
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/// Represents a repository from the Docker registry
+#[serde(rename_all = "camelCase")]
+#[derive(Serialize, Deserialize)]
+pub struct Repository {
+ #[serde(skip_serializing)]
+ pub directory: String,
+
+ pub name: String,
+ pub tags: Vec<Tag>,
+}
+
+impl Repository {
+ pub fn exists(&self) -> bool {
+ let path = Path::new(&self.directory);
+
+ path.exists() && path.is_dir()
+ }
+
+ pub fn is_valid_name(name: &str) -> bool {
+ lazy_static! {
+ static ref RE: Regex = Regex::new("^/?[a-zA-Z0-9_-]+$").unwrap();
+ }
+
+ RE.is_match(name) && name.len() <= 30
+ }
+
+ pub fn update_tags(&mut self) {
+ let path = Path::new(&self.directory).join("_manifests/tags");
+
+ let tag_names = get_subdirectories_names(&path);
+ self.tags = tag_names.iter()
+ .map(|name| Tag {
+ name: name.clone(),
+ hash: self.get_hash_for_tag(&name).unwrap_or(String::new()),
+ })
+ .collect();
+ }
+
+ fn get_hash_for_tag(&self, tag_name: &str) -> IOResult<String> {
+ let mut buffer = String::new();
+
+ let path = Path::new(&self.directory)
+ .join("_manifests/tags")
+ .join(tag_name)
+ .join("current/link");
+
+ let mut f = File::open(path)?;
+ f.read_to_string(&mut buffer)?;
+
+ buffer = Tag::clean_tag(&buffer);
+
+ Ok(buffer)
+ }
+}
+
+/* -------------------------------------------------------------
+ Tag
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/// Represents a repository tag
+#[serde(rename_all = "camelCase")]
+#[derive(Serialize, Deserialize, Clone)]
+pub struct Tag {
+ pub name: String,
+ pub hash: String,
+}
+
+impl Tag {
+ pub fn clean_tag (tag: &str) -> String {
+ let fragments: Vec<&str> = tag.split(":").collect();
+
+ if fragments.len() == 1 {
+ String::from(tag)
+ } else {
+ String::from(fragments[1])
+ }
+ }
}
@@ -81,3 +193,30 @@
Err(_) => false,
}
}
+
+fn get_entry_name(entry: &IOResult<DirEntry>) -> String {
+ match entry {
+ Ok(e) => match e.file_name().into_string() {
+ Ok(name) => String::from(name),
+ Err(_) => String::new(),
+ }
+ Err(_) => String::new(),
+ }
+}
+
+fn get_subdirectories_names (dir: &Path) -> Vec<String> {
+ match std::fs::read_dir(dir) {
+ Ok(iterator) => {
+ iterator
+ .filter(|entry| is_entry_sub_directory(entry))
+ .map(|entry| get_entry_name(&entry))
+ .filter(|name| name != "")
+ .collect::<Vec<_>>()
+ },
+ Err(_) => {
+ error!("Can't get subdirectories of {:?}", dir);
+
+ Vec::new()
+ }
+ }
+}
diff --git a/src/requests.rs b/src/requests.rs
--- a/src/requests.rs
+++ b/src/requests.rs
@@ -1,9 +1,9 @@
//!
-//! Requests handled by the microservice.
+//! HTTP requests handled by the microservice.
//!
-use crate::registry::Registry;
-use limiting_factor::api::replies::{ApiJsonResponse, ApiResponse};
+use crate::registry::{Registry, Repository};
+use limiting_factor::api::replies::*;
use rocket::response::NamedFile;
#[get("/status")]
@@ -25,3 +25,24 @@
Registry::with_default_location()
.into_json_response()
}
+
+// -------------------------------------------------------------
+// /repository
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#[get("/repository/<repository_name>")]
+pub fn get_repository_info(repository_name: String) -> ApiJsonResponse<Repository> {
+ let repository = Registry::with_default_location()
+ .get_repository(&repository_name);
+
+ match repository {
+ None => Err(build_bad_request_response()),
+ Some(repo) => {
+ if repo.exists() {
+ Ok(repo.into_json_response()?)
+ } else {
+ Err(build_not_found_response())
+ }
+ }
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 26, 18:52 (21 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2264980
Default Alt Text
D1871.id4727.diff (6 KB)

Event Timeline