diff options
author | Ava Hahn <a.hahn@f5.com> | 2024-06-14 21:04:15 -0700 |
---|---|---|
committer | Ava Hahn <110854134+avahahn@users.noreply.github.com> | 2024-06-18 16:21:10 -0700 |
commit | e0c15ae4575335fb079e2d33fc853a547b2380c9 (patch) | |
tree | ce2a4e6eb80f79d1bb2d89c5667c71dbf11d375e /tools/unitctl/unit-client-rs/src | |
parent | d96d583328f614c658d42f5bb0d2a0f81621327e (diff) | |
download | unit-e0c15ae4575335fb079e2d33fc853a547b2380c9.tar.gz unit-e0c15ae4575335fb079e2d33fc853a547b2380c9.tar.bz2 |
tools/unitctl: implement application subcommand
* application subcommand UI schema
* application subcommand handler
* additions to unit-client-rs to expose application API
* elaborate on OpenAPI error handling
* adds wasm and wasi app schemas to OpenAPI Schema
* updates tools/unitctl OpenAPI library
* many linter fixes
* README.md updates
Signed-off-by: Ava Hahn <a.hahn@f5.com>
Diffstat (limited to '')
-rw-r--r-- | tools/unitctl/unit-client-rs/src/control_socket_address.rs | 2 | ||||
-rw-r--r-- | tools/unitctl/unit-client-rs/src/unit_client.rs | 48 | ||||
-rw-r--r-- | tools/unitctl/unit-client-rs/src/unitd_docker.rs | 111 |
3 files changed, 92 insertions, 69 deletions
diff --git a/tools/unitctl/unit-client-rs/src/control_socket_address.rs b/tools/unitctl/unit-client-rs/src/control_socket_address.rs index 402d2293..438ab0ad 100644 --- a/tools/unitctl/unit-client-rs/src/control_socket_address.rs +++ b/tools/unitctl/unit-client-rs/src/control_socket_address.rs @@ -34,7 +34,6 @@ impl ControlSocketScheme { } } - impl Display for ControlSocket { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -312,7 +311,6 @@ impl ControlSocket { } } - #[cfg(test)] mod tests { use rand::distributions::{Alphanumeric, DistString}; diff --git a/tools/unitctl/unit-client-rs/src/unit_client.rs b/tools/unitctl/unit-client-rs/src/unit_client.rs index b8c73ec0..b3f07308 100644 --- a/tools/unitctl/unit-client-rs/src/unit_client.rs +++ b/tools/unitctl/unit-client-rs/src/unit_client.rs @@ -15,9 +15,11 @@ use serde::{Deserialize, Serialize}; use crate::control_socket_address::ControlSocket; use unit_openapi::apis::configuration::Configuration; -use unit_openapi::apis::{Error as OpenAPIError, StatusApi}; -use unit_openapi::apis::{ListenersApi, ListenersApiClient, StatusApiClient}; -use unit_openapi::models::{ConfigListener, Status}; +use unit_openapi::apis::{ + ApplicationsApi, ApplicationsApiClient, AppsApi, AppsApiClient, Error as OpenAPIError, ListenersApi, + ListenersApiClient, StatusApi, StatusApiClient, +}; +use unit_openapi::models::{ConfigApplication, ConfigListener, Status}; const USER_AGENT: &str = concat!("UNIT CLI/", env!("CARGO_PKG_VERSION"), "/rust"); @@ -276,6 +278,46 @@ impl UnitClient { }) } + pub fn applications_api(&self) -> Box<dyn ApplicationsApi + 'static> { + new_openapi_client!(self, ApplicationsApiClient, ApplicationsApi) + } + + pub async fn applications(&self) -> Result<HashMap<String, ConfigApplication>, Box<UnitClientError>> { + self.applications_api().get_applications().await.or_else(|err| { + if let OpenAPIError::Hyper(hyper_error) = err { + Err(Box::new(UnitClientError::new( + hyper_error, + self.control_socket.to_string(), + "/applications".to_string(), + ))) + } else { + Err(Box::new(UnitClientError::OpenAPIError { source: err })) + } + }) + } + + pub async fn per_application_api(&self) -> Box<dyn AppsApi + 'static> { + new_openapi_client!(self, AppsApiClient, AppsApi) + } + + pub async fn restart_application(&self, name: &String) -> Result<HashMap<String, String>, Box<UnitClientError>> { + self.per_application_api() + .await + .get_app_restart(name.as_str()) + .await + .or_else(|err| { + if let OpenAPIError::Hyper(hyper_error) = err { + Err(Box::new(UnitClientError::new( + hyper_error, + self.control_socket.to_string(), + format!("/control/applications/{}/restart", name), + ))) + } else { + Err(Box::new(UnitClientError::OpenAPIError { source: err })) + } + }) + } + pub async fn is_running(&self) -> bool { self.status().await.is_ok() } diff --git a/tools/unitctl/unit-client-rs/src/unitd_docker.rs b/tools/unitctl/unit-client-rs/src/unitd_docker.rs index 6881893d..0d318096 100644 --- a/tools/unitctl/unit-client-rs/src/unitd_docker.rs +++ b/tools/unitctl/unit-client-rs/src/unitd_docker.rs @@ -1,19 +1,16 @@ use std::collections::HashMap; use std::fs::read_to_string; -use std::path::{PathBuf, MAIN_SEPARATOR}; use std::io::stderr; +use std::path::{PathBuf, MAIN_SEPARATOR}; +use crate::control_socket_address::ControlSocket; use crate::futures::StreamExt; use crate::unit_client::UnitClientError; use crate::unitd_process::UnitdProcess; -use crate::control_socket_address::ControlSocket; use bollard::container::{Config, ListContainersOptions, StartContainerOptions}; use bollard::image::CreateImageOptions; -use bollard::models::{ - ContainerCreateResponse, HostConfig, Mount, - MountTypeEnum, ContainerSummary, -}; +use bollard::models::{ContainerCreateResponse, ContainerSummary, HostConfig, Mount, MountTypeEnum}; use bollard::secret::ContainerInspectResponse; use bollard::Docker; @@ -156,22 +153,18 @@ impl UnitdContainer { // cant do this functionally because of the async call let mut mapped = vec![]; for ctr in summary { - if unitd_command_re.is_match(&ctr.clone().command - .or(Some(String::new())) - .unwrap()) { - let mut c = UnitdContainer::from(&ctr); - if let Some(names) = ctr.names { - if names.len() > 0 { - let name = names[0].strip_prefix("/") - .or(Some(names[0].as_str())).unwrap(); - if let Ok(cir) = docker - .inspect_container(name, None).await { - c.details = Some(cir); - } + if unitd_command_re.is_match(&ctr.clone().command.or(Some(String::new())).unwrap()) { + let mut c = UnitdContainer::from(&ctr); + if let Some(names) = ctr.names { + if names.len() > 0 { + let name = names[0].strip_prefix("/").or(Some(names[0].as_str())).unwrap(); + if let Ok(cir) = docker.inspect_container(name, None).await { + c.details = Some(cir); } } - mapped.push(c); } + mapped.push(c); + } } mapped } @@ -196,11 +189,11 @@ impl UnitdContainer { // either return translated path or original prefixed with "container" if keys.len() > 0 { - let mut matches = self.mounts[&keys[0]] - .clone() - .join(cp.as_path() - .strip_prefix(keys[0].clone()) - .expect("error checking path prefix")); + let mut matches = self.mounts[&keys[0]].clone().join( + cp.as_path() + .strip_prefix(keys[0].clone()) + .expect("error checking path prefix"), + ); /* Observed on M1 Mac that Docker on OSX * adds a bunch of garbage to the mount path * converting it into a useless directory @@ -208,15 +201,14 @@ impl UnitdContainer { */ if cfg!(target_os = "macos") { let mut abs = PathBuf::from(String::from(MAIN_SEPARATOR)); - let m = matches.strip_prefix("/host_mnt/private") - .unwrap_or(matches.strip_prefix("/host_mnt") - .unwrap_or(matches.as_path())); + let m = matches + .strip_prefix("/host_mnt/private") + .unwrap_or(matches.strip_prefix("/host_mnt").unwrap_or(matches.as_path())); // make it absolute again abs.push(m); matches = abs; } - matches.to_string_lossy() - .to_string() + matches.to_string_lossy().to_string() } else { format!("<container>:{}", cp.display()) } @@ -264,10 +256,7 @@ pub async fn deploy_new_container( let mut mounts = vec![]; // if a unix socket is specified, mounts its directory if socket.is_local_socket() { - let mount_path = PathBuf::from(socket.clone()) - .as_path() - .to_string_lossy() - .to_string(); + let mount_path = PathBuf::from(socket.clone()).as_path().to_string_lossy().to_string(); mounts.push(Mount { typ: Some(MountTypeEnum::BIND), source: Some(mount_path), @@ -290,22 +279,22 @@ pub async fn deploy_new_container( Some(CreateImageOptions { from_image: image.as_str(), ..Default::default() - }), None, None + }), + None, + None, ); while let Some(res) = stream.next().await { if let Ok(info) = res { if let Some(id) = info.id { if let Some(_) = totals.get_mut(&id) { - if let Some(delta) = info.progress_detail - .and_then(|detail| detail.current) { - pb.add(delta as u64); - } + if let Some(delta) = info.progress_detail.and_then(|detail| detail.current) { + pb.add(delta as u64); + } } else { - if let Some(total) = info.progress_detail - .and_then(|detail| detail.total) { - totals.insert(id, total); - pb.total += total as u64; - } + if let Some(total) = info.progress_detail.and_then(|detail| detail.total) { + totals.insert(id, total); + pb.total += total as u64; + } } } } @@ -357,27 +346,27 @@ pub async fn deploy_new_container( .await { // somehow our container doesnt exist - Err(e) => Err(UnitClientError::UnitdDockerError{ - message: e.to_string() - }), + Err(e) => Err(UnitClientError::UnitdDockerError { message: e.to_string() }), // here it is! Ok(info) => { if info.len() < 1 { return Err(UnitClientError::UnitdDockerError { message: "couldnt find new container".to_string(), }); - } else if info[0].names.is_none() || - info[0].names.clone().unwrap().len() < 1 { - return Err(UnitClientError::UnitdDockerError { - message: "new container has no name".to_string(), - }); - } + } else if info[0].names.is_none() || info[0].names.clone().unwrap().len() < 1 { + return Err(UnitClientError::UnitdDockerError { + message: "new container has no name".to_string(), + }); + } // start our container - match docker.start_container( - info[0].names.clone().unwrap()[0].strip_prefix(MAIN_SEPARATOR).unwrap(), - None::<StartContainerOptions<String>>, - ).await { + match docker + .start_container( + info[0].names.clone().unwrap()[0].strip_prefix(MAIN_SEPARATOR).unwrap(), + None::<StartContainerOptions<String>>, + ) + .await + { Err(err) => Err(UnitClientError::UnitdDockerError { message: err.to_string(), }), @@ -439,14 +428,8 @@ mod tests { ctr.host_path("/path/to/conf".to_string()) ); if cfg!(target_os = "macos") { - assert_eq!( - "/6/test".to_string(), - ctr.host_path("/var/test".to_string()) - ); - assert_eq!( - "/7/test".to_string(), - ctr.host_path("/var/var/test".to_string()) - ); + assert_eq!("/6/test".to_string(), ctr.host_path("/var/test".to_string())); + assert_eq!("/7/test".to_string(), ctr.host_path("/var/var/test".to_string())); } } |