From 32f899b71484ee28ce807f998e507719ecbb3541 Mon Sep 17 00:00:00 2001 From: unitexe Date: Sat, 7 Jun 2025 23:43:27 -0500 Subject: List container image archives via gRPC server --- Cargo.lock | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ proto/ormos.proto | 16 +++++++++++ src/main.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 171 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 49138e8..2e1a2fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,15 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytes" version = "1.10.1" @@ -139,6 +148,35 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "either" version = "1.15.0" @@ -218,6 +256,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -267,6 +315,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "1.3.1" @@ -678,11 +732,24 @@ dependencies = [ "syn", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "skopos" version = "0.1.0" dependencies = [ + "hex", "prost", + "sha2", "tokio", "tonic", "tonic-build", @@ -919,12 +986,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index c309728..c572423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] +hex = "0.4.3" prost = "0.13.5" +sha2 = "0.10.9" tokio = { version = "1.45.1", features = ["macros", "rt-multi-thread"] } tonic = "0.13.1" tonic-reflection = "0.13.1" diff --git a/proto/ormos.proto b/proto/ormos.proto index 340dcf5..05ae661 100644 --- a/proto/ormos.proto +++ b/proto/ormos.proto @@ -6,6 +6,8 @@ service Ormos { rpc ListUsbDevices (ListUsbDevicesRequest) returns (ListUsbDevicesResponse) {} rpc MountUsbDevice (MountUsbDeviceRequest) returns (MountUsbDeviceResponse) {} rpc UnmountUsbDevice (UnmountUsbDeviceRequest) returns (UnmountUsbDeviceResponse) {} + + rpc ListImageArchives (ListImageArchivesRequest) returns (ListImageArchivesResponse) {} } message MountUsbDeviceRequest { @@ -38,3 +40,17 @@ message UsbDevice { bool is_mounted = 2; string mount_point = 3; } + +message ListImageArchivesRequest { + string path = 1; +} + +message ListImageArchivesResponse { + repeated ImageArchive image_archives = 1; +} + +message ImageArchive { + string file_path = 1; + int64 file_size_bytes = 2; + string sha256_checksum = 3; +} diff --git a/src/main.rs b/src/main.rs index d0bff1c..d55b67a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ use tonic::{transport::Server, Request, Response, Status}; use skopos::ormos_server::{Ormos, OrmosServer}; -use skopos::{ListUsbDevicesRequest, ListUsbDevicesResponse, UsbDevice, MountUsbDeviceRequest, MountUsbDeviceResponse, UnmountUsbDeviceRequest, UnmountUsbDeviceResponse}; +use skopos::{ListUsbDevicesRequest, ListUsbDevicesResponse, UsbDevice, MountUsbDeviceRequest, MountUsbDeviceResponse, UnmountUsbDeviceRequest, UnmountUsbDeviceResponse, ListImageArchivesRequest, ListImageArchivesResponse, ImageArchive}; use std::io; use std::fs; use std::process::Command; +use std::path::Path; +use sha2::{Sha256, Digest}; pub mod skopos { tonic::include_proto!("unit.containers.v0"); @@ -132,6 +134,58 @@ fn unmount_usb_device(mount_point: &str) -> Result<(), io::Error> { } } +fn is_file_a_container_image_archive(path: &Path) -> bool { + let output = Command::new("skopeo") + .arg("inspect") + .arg(format!("docker-archive:{}", path.display())) + .output(); + + match output { + Ok(result) => result.status.success(), + Err(_) => false, + } +} + + +fn list_container_image_archives(dir_path: &str) -> std::io::Result> { + let mut images = Vec::new(); + + for entry in std::fs::read_dir(dir_path)? { + let entry = entry?; + let path = entry.path(); + + if path.extension().map_or(false, |ext| ext == "tar") { + if is_file_a_container_image_archive(&path) { + images.push(path.display().to_string()); + } + } + } + + Ok(images) +} + +fn create_container_image_archives(archive_paths: Vec) -> Result, Box> { + archive_paths + .into_iter() + .map(|archive_path| { + let metadata = fs::metadata(&archive_path)?; + let archive_size_bytes = metadata.len() as i64; + + let mut file = fs::File::open(&archive_path)?; + let mut hasher = Sha256::new(); + io::copy(&mut file, &mut hasher)?; + let hash_bytes = hasher.finalize(); + let hash_str = hex::encode(hash_bytes); + + Ok(ImageArchive { + file_path: archive_path, + file_size_bytes: archive_size_bytes, + sha256_checksum: hash_str, + }) + }) + .collect() +} + #[tonic::async_trait] impl Ormos for MyOrmos { async fn list_usb_devices( @@ -208,6 +262,25 @@ impl Ormos for MyOrmos { } } } + + async fn list_image_archives( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + let path = if req.path.is_empty() { + "/mnt/usb".to_string() // Default mount point + } else { + req.path + }; + let container_image_archive_paths = list_container_image_archives(&path)?; + let image_archives = create_container_image_archives(container_image_archive_paths) + .map_err(|e| Status::internal(format!("Failed to create image archive response: {}", e)))?; + let response = ListImageArchivesResponse { + image_archives, + }; + Ok(Response::new(response)) + } } #[tokio::main] -- cgit v1.2.3