summaryrefslogtreecommitdiffhomepage
path: root/tools/unitctl/unit-client-rs/src
diff options
context:
space:
mode:
authorAva Hahn <a.hahn@f5.com>2024-06-14 21:04:15 -0700
committerAva Hahn <110854134+avahahn@users.noreply.github.com>2024-06-18 16:21:10 -0700
commite0c15ae4575335fb079e2d33fc853a547b2380c9 (patch)
treece2a4e6eb80f79d1bb2d89c5667c71dbf11d375e /tools/unitctl/unit-client-rs/src
parentd96d583328f614c658d42f5bb0d2a0f81621327e (diff)
downloadunit-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.rs2
-rw-r--r--tools/unitctl/unit-client-rs/src/unit_client.rs48
-rw-r--r--tools/unitctl/unit-client-rs/src/unitd_docker.rs111
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()));
}
}