diff --git a/src/console/commands/debug/casbin.rs b/src/console/commands/debug/casbin.rs index ba6bb623..afc685ea 100644 --- a/src/console/commands/debug/casbin.rs +++ b/src/console/commands/debug/casbin.rs @@ -1,7 +1,6 @@ use crate::configuration::get_configuration; use actix_web::{rt, post, web, HttpResponse, Result, http::header::ContentType}; use crate::middleware; -//use actix_casbin_auth::casbin::CoreApi; use casbin::CoreApi; use sqlx::PgPool; diff --git a/src/console/commands/debug/dockerhub.rs b/src/console/commands/debug/dockerhub.rs new file mode 100644 index 00000000..7067ce90 --- /dev/null +++ b/src/console/commands/debug/dockerhub.rs @@ -0,0 +1,36 @@ +use actix_web::{rt, Result}; +use crate::helpers::dockerhub::DockerHub; +use crate::forms::project::DockerImage; + +use tracing_subscriber::FmtSubscriber; + +pub struct DockerhubCommand { + json: String, +} + +impl DockerhubCommand { + pub fn new(json: String) -> Self { + Self { json } + } +} + +impl crate::console::commands::CallableTrait for DockerhubCommand { + fn call(&self) -> Result<(), Box> { + let subscriber = FmtSubscriber::builder() + .with_max_level(tracing::Level::DEBUG) + .finish(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + + rt::System::new().block_on(async { + println!("{}", self.json); + let dockerImage: DockerImage = serde_json::from_str(&self.json)?; + let mut dockerhub = DockerHub::try_from(&dockerImage)?; + let isActive = dockerhub.is_active().await?; + + println!("image is active: {isActive}"); + + Ok(()) + }) + } +} diff --git a/src/console/commands/debug/mod.rs b/src/console/commands/debug/mod.rs index 48862746..0b5119d1 100644 --- a/src/console/commands/debug/mod.rs +++ b/src/console/commands/debug/mod.rs @@ -1,5 +1,7 @@ mod json; mod casbin; +mod dockerhub; pub use json::*; pub use casbin::*; +pub use dockerhub::*; diff --git a/src/console/main.rs b/src/console/main.rs index f339b30c..0bdc1f45 100644 --- a/src/console/main.rs +++ b/src/console/main.rs @@ -48,6 +48,10 @@ enum DebugCommands { #[arg(long)] subject: String, }, + Dockerhub { + #[arg(long)] + json: String, + } } #[derive(Debug, Subcommand)] @@ -76,6 +80,9 @@ fn get_command(cli: Cli) -> Result Ok(Box::new( stacker::console::commands::debug::CasbinCommand::new(action, path, subject), )), + DebugCommands::Dockerhub { json } => Ok(Box::new( + stacker::console::commands::debug::DockerhubCommand::new(json), + )), }, Commands::MQ { command} => match command { AppMqCommands::Listen {} => Ok(Box::new( diff --git a/src/helpers/dockerhub.rs b/src/helpers/dockerhub.rs index fda20f0f..3108d5ce 100644 --- a/src/helpers/dockerhub.rs +++ b/src/helpers/dockerhub.rs @@ -6,7 +6,7 @@ use serde_valid::Validate; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] pub struct DockerHubToken { - pub token: Option, + pub token: String, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] @@ -104,30 +104,38 @@ pub struct DockerHub<'a> { } impl<'a> DockerHub<'a> { - pub async fn login(&'a self) -> Result { - let endpoint = "https://hub.docker.com/v2/users/login"; + #[tracing::instrument(name = "Dockerhub login.")] + pub async fn login(&'a self) -> Result { + if self.creds.password.is_empty() { + return Err("Password is empty".to_string()); + } + + if self.creds.username.is_empty() { + return Err("Username is empty".to_string()); + } + + let url = "https://hub.docker.com/v2/users/login"; reqwest::Client::new() - .post(endpoint) + .post(url) .json(&self.creds) .send() .await .map_err(|err| format!("{:?}", err))? .json::() .await + .map(|dockerHubToken| dockerHubToken.token) .map_err(|err| format!("{:?}", err)) } + #[tracing::instrument(name = "Lookup public repos")] pub async fn lookup_public_repos(&'a self) -> Result { - let url = format!("https://hub.docker.com/v2/repositories/{}", self.repos); - tracing::debug!("Validate public repository {:?}", &url); let client = reqwest::Client::new() .get(&url) .header("Accept", "application/json"); - let client = self.set_token(client).await?; - client - .send() + + client.send() .await .map_err(|err| { let msg = format!("πŸŸ₯Error response {:?}", err); @@ -149,29 +157,23 @@ impl<'a> DockerHub<'a> { .results .into_iter() .any(|repo| repo.status == Some(1)); - tracing::debug!("βœ… Image is active. url: {:?}", &url); + tracing::debug!("βœ… Public mage is active. url: {:?}", &url); active } else { - tracing::debug!("πŸŸ₯ Image tag is not active, url: {:?}", &url); + tracing::debug!("πŸŸ₯ Public image tag is not active, url: {:?}", &url); false } }) } + #[tracing::instrument(name = "Lookup official repos")] pub async fn lookup_official_repos(&'a self) -> Result { - // search in official library repositories let url = format!("https://hub.docker.com/v2/repositories/library/{}/tags", self.repos); - return self.lookup(&url).await; - } - - pub async fn lookup(&'a self, url: &String) -> Result { - tracing::debug!("Search official repos {:?}", url); let client = reqwest::Client::new() .get(url) .header("Accept", "application/json"); - let client = self.set_token(client).await?; - client - .send() + + client.send() .await .map_err(|err| format!("πŸŸ₯{}", err))? .json::() @@ -190,32 +192,35 @@ impl<'a> DockerHub<'a> { .any(|tag| { tracing::debug!("official: {:?}", tag); if "active".to_string() == tag.tag_status { - tracing::debug!("βœ… Image is active"); true } else { false } }); - tracing::debug!("βœ… search official repos result is {:?}", result); + tracing::debug!("βœ… Official mage is active. url: {:?}", result); result } else { - tracing::debug!("πŸŸ₯ Image tag is not active"); + tracing::debug!("πŸŸ₯ Official image tag is not active"); false } }) } + #[tracing::instrument(name = "Lookup private repos")] pub async fn lookup_private_repo(&'a self) -> Result { + let token = self.login().await?; + let url = format!( "https://hub.docker.com/v2/namespaces/{}/repositories/{}/tags", &self.creds.username, &self.repos ); - tracing::debug!("Validate image {:?}", url); + + tracing::debug!("Search private repos {:?}", url); let client = reqwest::Client::new() .get(url) .header("Accept", "application/json"); - let client = self.set_token(client).await?; - client + + client.bearer_auth(token) .send() .await .map_err(|err| format!("πŸŸ₯{}", err))? @@ -233,7 +238,6 @@ impl<'a> DockerHub<'a> { .results .into_iter() .any(|tag| tag.tag_status.contains("active")); - tracing::debug!("βœ… Image is active"); return active; } else { tracing::debug!("πŸŸ₯ Image tag is not active"); @@ -241,7 +245,8 @@ impl<'a> DockerHub<'a> { } }) } - pub async fn is_active(&'a mut self) -> Result { + + pub async fn is_active(&'a self) -> Result { // if namespace/user is not set change endpoint and return a different response // let n = self.repos @@ -262,45 +267,23 @@ impl<'a> DockerHub<'a> { // } // } - if self.creds.username.is_empty() { - - if Ok(true) == self.lookup_official_repos().await { - tracing::debug!("official: true"); - return Ok(true); - } else { - tracing::debug!("official: false"); - }; + tokio::select! { + Ok(true) = self.lookup_official_repos() => { + tracing::debug!("official: true"); + return Ok(true); + } - if Ok(true) == self.lookup_public_repos().await { + Ok(true) = self.lookup_public_repos() => { tracing::debug!("public: true"); return Ok(true); - }; - - Ok(false) - - } else { + } - if Ok(true) == self.lookup_private_repo().await { + Ok(true) = self.lookup_private_repo() => { tracing::debug!("private: true"); return Ok(true); - }; - - Ok(false) - } - } - - pub async fn set_token(&'a self, client: RequestBuilder) -> Result { - if self.creds.password.is_empty() { - tracing::debug!("Password is empty. Image should be public"); - return Ok(client); - } else { - } - tracing::debug!("Password is set. Login.."); - let token = self.login().await?; + } - match token.token { - None => Ok(client), - Some(token) => Ok(client.bearer_auth(token)), + else => { return Ok(false); } } } } @@ -362,4 +345,4 @@ impl<'a> TryFrom<&'a DockerImage> for DockerHub<'a> { Ok(hub) } -} \ No newline at end of file +} diff --git a/tests/dockerhub.rs b/tests/dockerhub.rs index 25b30e25..79d46e67 100644 --- a/tests/dockerhub.rs +++ b/tests/dockerhub.rs @@ -120,7 +120,7 @@ async fn test_docker_named_volume() { container_path: Some("/var/www/flaskdata".to_owned()), }; - let cv:ComposeVolume = volume.into(); + let cv:ComposeVolume = (&volume).into(); println!("ComposeVolume: {:?}", cv); println!("{:?}", cv.driver_opts); assert_eq!(Some("flask-data".to_string()), cv.name);