A focused Docker Compose management web application.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: list projects function

Brooke cfa9511f 4289fcb6

+104 -54
+6 -7
packages/node/src/api/mod.rs
··· 1 - use axum::Router; 2 - 3 - use crate::state::LuminaryState; 4 - 5 - pub fn router() -> Router<LuminaryState> { 6 - Router::new().route("/ping", axum::routing::get(|| async { "pong" })) 7 - } 1 + use axum::routing::get; 2 + use axum::Router; 3 + 4 + pub fn router() -> Router { 5 + Router::new().route("/ping", get(|| async { "pong" })) 6 + }
+16
packages/node/src/core/model.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use serde::{Deserialize, Serialize}; 4 + use specta::Type; 5 + 6 + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Type)] 7 + pub struct LuminaryProject { 8 + pub name: String, 9 + pub dir: String, 10 + pub services: HashMap<String, LuminaryService>, 11 + } 12 + 13 + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Type)] 14 + pub struct LuminaryService { 15 + pub name: String, 16 + }
+42
packages/node/src/core/project.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use bollard::query_parameters::ListContainersOptionsBuilder; 4 + use color_eyre::eyre::{Ok, Result}; 5 + 6 + use crate::core::{ 7 + LuminaryCore, 8 + model::{LuminaryProject, LuminaryService}, 9 + }; 10 + 11 + const COMPOSE_PROJECT_DIR_LABEL: &str = "com.docker.compose.project.working_dir"; 12 + const COMPOSE_PROJECT_LABEL: &str = "com.docker.compose.project"; 13 + const COMPOSE_SERVICE_LABEL: &str = "com.docker.compose.service"; 14 + 15 + impl LuminaryCore { 16 + pub async fn get_projects(&self) -> Result<HashMap<String, LuminaryProject>> { 17 + let options = ListContainersOptionsBuilder::default().all(true).build(); 18 + let projects = self.docker.list_containers(Some(options)).await?.iter_mut().fold( 19 + HashMap::<String, LuminaryProject>::new(), 20 + |mut acc, container| { 21 + if let Some(mut labels) = container.labels.take() 22 + && let Some(service) = labels.remove(COMPOSE_SERVICE_LABEL) 23 + && let Some(project) = labels.remove(COMPOSE_PROJECT_LABEL) 24 + && let Some(dir) = labels.remove(COMPOSE_PROJECT_DIR_LABEL) 25 + { 26 + acc.entry(project.clone()) 27 + .or_insert_with(|| LuminaryProject { 28 + services: HashMap::new(), 29 + name: project, 30 + dir, 31 + }) 32 + .services 33 + .insert(service.clone(), LuminaryService { name: service }); 34 + } 35 + 36 + return acc; 37 + }, 38 + ); 39 + 40 + return Ok(projects); 41 + } 42 + }
-1
packages/node/src/docker/list.rs
··· 1 -
+36 -31
packages/node/src/docker/mod.rs packages/node/src/core/mod.rs
··· 1 - use std::process::Stdio; 2 - 3 - use color_eyre::eyre::{Context, Result}; 4 - use luminary_macros::wrap_err; 5 - use tokio::process::Command; 6 - 7 - mod list; 8 - 9 - pub struct DockerClient {} 10 - 11 - impl DockerClient { 12 - pub fn new() -> Self { 13 - Self {} 14 - } 15 - 16 - #[wrap_err("Failed to read from docker compose command line interface")] 17 - async fn read_cli(&self, args: Vec<&str>) -> Result<String> { 18 - let output = Command::new("docker") 19 - .arg("compose") 20 - .args(args) 21 - .stdout(Stdio::piped()) 22 - .stderr(Stdio::null()) 23 - .spawn() 24 - .wrap_err("Failed to spawn child process")? 25 - .wait_with_output() 26 - .await 27 - .wrap_err("Failed to wait on child process")?; 28 - let string = String::from_utf8(output.stdout).wrap_err("Invalid UTF-8 from child process")?; 29 - return Ok(string); 30 - } 31 - } 1 + use std::process::Stdio; 2 + 3 + use bollard::Docker; 4 + use color_eyre::eyre::{Context, Result}; 5 + use luminary_macros::wrap_err; 6 + use tokio::process::Command; 7 + 8 + mod model; 9 + mod project; 10 + 11 + pub struct LuminaryCore { 12 + docker: Docker, 13 + } 14 + 15 + impl LuminaryCore { 16 + pub fn new() -> Result<Self> { 17 + let docker = Docker::connect_with_defaults().wrap_err("Failed to connect to docker engine.")?; 18 + return Ok(Self { docker }); 19 + } 20 + 21 + #[wrap_err("Failed to read from docker compose command line interface")] 22 + async fn read_cli(&self, args: Vec<&str>) -> Result<String> { 23 + let output = Command::new("docker") 24 + .arg("compose") 25 + .args(args) 26 + .stdout(Stdio::piped()) 27 + .stderr(Stdio::null()) 28 + .spawn() 29 + .wrap_err("Failed to spawn child process")? 30 + .wait_with_output() 31 + .await 32 + .wrap_err("Failed to wait on child process")?; 33 + let string = String::from_utf8(output.stdout).wrap_err("Invalid UTF-8 from child process")?; 34 + return Ok(string); 35 + } 36 + }
+4 -7
packages/node/src/main.rs
··· 1 + use std::fs::read_dir; 2 + 1 3 use axum::Router; 2 4 use color_eyre::eyre::Result; 3 5 use tokio::net::TcpListener; 4 6 5 - use crate::state::LuminaryState; 6 - 7 7 mod api; 8 - mod docker; 9 - mod state; 8 + mod core; 10 9 11 10 #[tokio::main] 12 11 async fn main() -> Result<()> { 13 12 let listener = TcpListener::bind("0.0.0.0:9000").await?; 14 - let router = Router::<LuminaryState>::new() 15 - .nest("/api/", api::router()) 16 - .with_state(LuminaryState::new()); 13 + let router = Router::new().nest("/api/", api::router()); 17 14 18 15 println!("Listening on http://127.0.0.1:{}", listener.local_addr()?.port()); 19 16 axum::serve(listener, router).await?;
-8
packages/node/src/state.rs
··· 1 - #[derive(Clone, Debug)] 2 - pub struct LuminaryState {} 3 - 4 - impl LuminaryState { 5 - pub fn new() -> Self { 6 - Self {} 7 - } 8 - }